Slicing an image based on peaks - python

I have the following peak detection that I did using hilbert transform that gets the envelop of a signal, then I detect the peaks out of it.
I would like to cut out those peaks from peak 1, to peak 2, and from peak 2, to peak 3, and from peak 3 to peak 4, and so on.
That's a sample image:
sample image
and That's result of the process
peaks
and that's my code:
import cv2
import numpy as np
from PIL import Image
from scipy import signal
from math import factorial
from matplotlib import pyplot as plt
from scipy.signal import savgol_filter
import scipy.signal.signaltools as sigtool
from sklearn.preprocessing import normalize
from scipy.signal import find_peaks, peak_widths, find_peaks_cwt
from scipy.signal import argrelextrema
# ---------------------------------------Functions---------------------------------------------------#
def normalize(v):
norm = np.linalg.norm(v)
if norm == 0:
return v
return v / norm
'''
reduces the photo to a vector representing its pixel freuqeuncy at each column
'''
def image_reduce(img):
col_counts = cv2.reduce(img, 0, cv2.REDUCE_SUM, dtype=cv2.CV_32SC1)
column = col_counts.flatten().tolist()
# print("Column counts:\n\n", column)
return column
def histogram_plot(convluted_word, peaks, fit):
plt.plot(convluted_word)
plt.plot(fit, 'r')
plt.plot(peaks, fit[peaks], "x")
plt.show()
# ---------------------------------------Main Code Flow---------------------------------------------------#
# ---------------------------an example on how to use this package----------------------------------------#
def slice_digits(image_name):
img = cv2.imread(image_name, 0)
column_frequency = image_reduce(cv2.bitwise_not(img))
column_frequency = normalize(column_frequency)
env = np.abs(sigtool.hilbert(column_frequency))
square_sig = (env > 0.1)
square_sig = square_sig.astype(float)
square_sig = np.divide(square_sig, 15.0)
square_sig = np.where((column_frequency > 0), 0.1, 0)
peaks, _ = find_peaks(env > 0.1)
plt.plot(env)
plt.scatter(peaks, env[peaks], s = 50, c = 'r')
edges = np.nonzero(np.diff(square_sig))[0]
plt.scatter(edges, env[edges], c = 'g')
plt.show()
all_slices = []
for i in range(len(peaks) - 1):
x0, x1 = peaks[i:i + 2]
image_slice = img[x0:x1]
# Now do something with the slice, e.g.
cv2.imshow("slice",image_slice)
all_slices.append(image_slice)
# used for debugging
#histogram_plot(column_frequency, peaks, square_sig)
# segements the picture
#listt, image_final = char_slicer(edges, img)
plt.show()
# display result
#return image_final
if __name__ == '__main__':
image = r"c:\ahmed\doc.png"
res_image = slice_digits(image)
What I want is something like that:
desired

To slice an image from coordinates in a list, which you have in peaks, you can use:
all_slices = []
for i in range(len(peaks)-1):
x0, x1 = peaks[i:i+2]
image_slice = img[:, x0:x1]
# Now do something with the slice, e.g.
all_slices.append(image_slice)
For your specific case the complete listing is
import cv2
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal.signaltools as sigtool
from scipy.signal import find_peaks, peak_widths, find_peaks_cwt
def image_reduce(img):
col_counts = cv2.reduce(img, 0, cv2.REDUCE_SUM, dtype=cv2.CV_32SC1)
column = col_counts.flatten().tolist()
# print("Column counts:\n\n", column)
return column
def normalize(v):
norm = np.linalg.norm(v)
if norm == 0:
return v
return v / norm
def slice_digits(image_name):
img = cv2.imread(image_name, 0)
column_frequency = image_reduce(cv2.bitwise_not(img))
column_frequency = normalize(column_frequency)
env = np.abs(sigtool.hilbert(column_frequency))
square_sig = (env > 0.1)
square_sig = square_sig.astype(float)
square_sig = np.divide(square_sig, 15.0)
square_sig = np.where((column_frequency > 0), 0.1, 0)
peaks, _ = find_peaks(env > 0.1)
plt.plot(env)
plt.scatter(peaks, env[peaks], s=50, c='r')
edges = np.nonzero(np.diff(square_sig))[0]
plt.scatter(edges, env[edges], c='g')
all_slices = []
for i in range(len(peaks) - 1):
x0, x1 = peaks[i:i + 2]
image_slice = img[:, x0:x1]
print("coords:", x0, x1)
# Now do something with the slice, e.g.
all_slices.append(image_slice)
plt.figure("Slice %d)" % i)
plt.imshow(image_slice)
plt.show()
if __name__ == '__main__':
image = r"c:\ahmed\doc.png"
res_image = slice_digits(image)

Related

vectorizing custom python function with numpy array

Not sure if that is the correct terminology. Basically trying to take a black and white image and first transform it such that all the white pixels that border black-pixels remain white, else turn black. That part of the program works fine, and is done in find_edges. Next I need to calculate the distance from each element in the image to the closest white-pixel. Right now I am doing it by using a for-loop that is insanely slow. Is there a way to make the find_nearest_edge function written solely with numpy without the need for a for-loop to call it on each element? Thanks.
####
from PIL import Image
import numpy as np
from scipy.ndimage import binary_erosion
####
def find_nearest_edge(arr, point):
w, h = arr.shape
x, y = point
xcoords, ycoords = np.meshgrid(np.arange(w), np.arange(h))
target = np.sqrt((xcoords - x)**2 + (ycoords - y)**2)
target[arr == 0] = np.inf
shortest_distance = np.min(target[target > 0.0])
return shortest_distance
def find_edges(img):
img = img.convert('L')
img_np = np.array(img)
kernel = np.ones((3,3))
edges = img_np - binary_erosion(img_np, kernel)*255
return edges
a = Image.open('a.png')
x, y = a.size
edges = find_edges(a)
out = Image.fromarray(edges.astype('uint8'), 'L')
out.save('b.png')
dists =[]
for _x in range(x):
for _y in range(y):
dist = find_nearest_edge(edges,(_x,_y))
dists.append(dist)
print(dists)
Images:
You can use KDTree to compute distances fast.
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import binary_erosion
from scipy.spatial import KDTree
def find_edges(img):
img_np = np.array(img)
kernel = np.ones((3,3))
edges = img_np - binary_erosion(img_np, kernel)*255
return edges
def find_closest_distance(img):
# NOTE: assuming input is binary image and white is any non-zero value!
white_pixel_points = np.array(np.where(img))
tree = KDTree(white_pixel_points.T)
img_meshgrid = np.array(np.meshgrid(np.arange(img.shape[0]), np.arange(img.shape[1]))).T
distances, _ = tree.query(img_meshgrid)
return distances
test_image = np.zeros((200, 200))
rectangle = np.ones((30, 80))
test_image[20:50, 60:140] = rectangle
test_image[150:180, 60:140] = rectangle
test_image[60:140, 20:50] = rectangle.T
test_image[60:140, 150:180] = rectangle.T
test_image = test_image * 255
edge_image = find_edges(test_image)
distance_image = find_closest_distance(edge_image)
fig, axes = plt.subplots(1, 3, figsize=(12, 5))
axes[0].imshow(test_image, cmap='Greys_r')
axes[1].imshow(edge_image, cmap='Greys_r')
axes[2].imshow(distance_image, cmap='Greys_r')
plt.show()
You can make your code 25X faster by just changing find_nearest_edge as follows. Many other optimizations are possible, but this is the biggest bottleneck in your code.
from numba import njit
#njit
def find_nearest_edge(arr, point):
x, y = point
shortest_distance = np.inf
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
if arr[i,j] == 0: continue
shortest_distance = min(shortest_distance, (i-x)**2 + (j-y)**2)
return np.sqrt(shortest_distance)

Get all zeroes from matplotlib graph

How could I get a list of all the zeroes of a matplotlib graph? By zeroes I mean every coordinate where the y-value of my function is at 0 (very close to zero also works for what I'm trying to accomplish). My code and the graph it generates are down below.
THE .WAV FILE: https://drive.google.com/drive/folders/11nE0nyd9UPViicSIKNeiBqSNbdWGRbIl?usp=sharing
MY CODE:
from scipy.io import wavfile
import matplotlib.pyplot as plt
import numpy as np
samplerate, data = wavfile.read(r'C:\Users\jack_l\Downloads\louduntitled.wav')
length = data.shape[0] / samplerate
import matplotlib.pyplot as plt
import numpy as np
time = np.linspace(0., length, data.shape[0])
plt.plot(time, data[:, 1], label="Right channel")
plt.legend()
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.show()
GRAPH:
I believe you want to find all times where the y-value is zero.
What about finding zero-crossings of the array data[:, 1] (here a), and use the returned indices to find the interpolated time values where y-value is zero.
CODE
import matplotlib.pyplot as plt
import numpy as np
from scipy.io import wavfile
# load data
samplerate, data = wavfile.read(r'C:\Users\jack_l\Downloads\louduntitled.wav')
length = data.shape[0] / samplerate
time = np.linspace(0., length, data.shape[0])
a = data[:, 1] # here put your data
zc_idxs = np.where(np.diff(np.sign(a)))[0] # indices of element before zero crossing
t_zero = []
for zc_i in zc_idxs: # interpolate each zero crossing
t1 = time[zc_i]
t2 = time[zc_i + 1]
a1 = a[zc_i]
a2 = a[zc_i + 1]
t_zero.append(t1 + (0 - a1) * ((t2 - t1) / (a2 - a1)))
plt.plot(time, a, label="Right channel")
plt.plot(t_zero, np.zeros((len(t_zero), 1)), 'o')
We can't access to your data, so I used the cos function to have a similar shape.
Here is my code that perform a linear interpolation (there's a small error due to the non-linear behavior of the cos function)
import numpy as np
n = 2000
x = np.linspace(0, 10, n)
y = np.cos(x)
res = []
is_positive = y[0] > 0
for i in range(n):
if y[i] == 0:
res.append(x[i])
elif (is_positive and y[i] < 0) or (not is_positive and y[i] > 0):
x_gap = x[i] - x[i-1]
coef_gap = y[i]/(y[i] - y[i-1])
res.append(x[i] + x_gap*coef_gap)
is_positive = not is_positive
analytic_res = [i*np.pi/2 for i in range(1,6,2)]
print(res)
print(analytic_res)
Output :
[1.5807794610537442, 4.722328378160516, 7.863877295269648]
[1.5707963267948966, 4.71238898038469, 7.853981633974483]
NB : if the very first value is zero, the code could have a weird behavior

Convolution on Python

So I suppose to calculate the convolution between Fourier Transformed image and the mask.
from scipy import fftpack
import numpy as np
import imageio
from PIL import Image, ImageDraw
import cv2
import matplotlib.pyplot as plt
import math
from scipy.ndimage.filters import convolve
input_image = Image.open('....image....')
input_image=np.array(input_image)
M,N = input_image.shape[0],input_image.shape[1]
FT_img = fftpack.fftshift(fftpack.fft2(input_image))
n = 2; # order value can change this value accordingly
D0 = 60; # cut-off frequency can change this value accordingly
# Designing filter
u = np.arange(0, M)
idx = u > M/2
u[idx] = u[idx] - M
v = np.arange(0, N)
idy = v > N/2
v[idy] = v[idy] - N
V,U = np.meshgrid(v,u)
# Calculating Euclidean Distance
D=np.linalg.norm(V-U)
# determining the filtering mask
H = 1/(1 + (D0/D)**(2*n));
# Convolution between the Fourier Transformed image and the mask
G = convolve(H, FT_img)
And I get "Runtime error:filter weights array has incorrect shape." error at the last line when I run this code snippet. What I understand is H is float and FT_img is array so I cannot perform convolution on these. But I don't know how to solve that.
How can I solve this problem?
calculating distance D, and filter H for each (u, v) this will yield an array with same size of input image, multiplying that array(H the Filter) with the image in Fourier Domain will be equivalent to convolution in the Time domain, and the results will be as following:
import numpy as np
import cv2
import matplotlib.pyplot as plt
# Read Image as Grayscale
img = cv2.imread('input.png', 0)
# Designing filter
#------------------------------------------------------
def butterworth_filter(shape, n=2, D0=60):
'''
n = 2; # order value can change this value accordingly
D0 = 60; # cut-off frequency can change this value accordingly
'''
M, N = shape
# Initialize filter with zeros
H = np.zeros((M, N))
# Traverse through filter
for u in range(0, M):
for v in range(0, N):
# Get euclidean distance from point D(u,v) to the center
D_uv = np.sqrt((u - M / 2) ** 2 + (v - N / 2) ** 2)
# determining the filtering mask
H[u, v] = 1/(1 + (D0/D_uv)**(2*n))
return H
#-----------------------------------------------------
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
phase_spectrumR = np.angle(fshift)
magnitude_spectrum = 20*np.log(np.abs(fshift))
# Generate Butterworth Filter
H = butterworth_filter(img.shape)
# Convolution between the Fourier Transformed image and the mask
G = H * fshift
# Obtain the Result
result = np.abs(np.fft.ifft2(np.fft.ifftshift((G))))
plt.subplot(222)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.axis('off')
plt.subplot(221)
plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('magnitude spectrum')
plt.axis('off')
plt.subplot(223)
plt.imshow(H, "gray")
plt.title("Butterworth Filter")
plt.axis('off')
plt.subplot(224)
plt.imshow(result, "gray")
plt.title("Result")
plt.axis('off')
plt.show()

How to divide an image into ''n'' different polygons in Python

I am new to python and am trying to divide an image into 'n' different polygon using python. My target is to convert an image into n random polygon shaped images. I tried Voronoi algorithm but its kind of messy. I would really appreciate any help. Any other segmentation method etc.
My previous Code:
import random
import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial import Voronoi, voronoi_plot_2d
img = plt.imread("abc.jpg")
fig, ax = plt.subplots()
ax.imshow(img)
def points(radius,rangeX,rangeY,qty):
deltas = set()
for x in range(-radius, radius+1):
for y in range(-radius, radius+1):
if x*x + y*y <= radius*radius:
deltas.add((x,y))
randPoints = []
excluded = set()
i = 0
while i<qty:
x = random.randrange(*rangeX)
y = random.randrange(*rangeY)
if (x,y) in excluded: continue
randPoints.append((x,y))
i += 1
excluded.update((x+dx, y+dy) for (dx,dy) in deltas)
return randPoints
def plot1(randPoints,fig):
points = np.array(randPoints)
vor = Voronoi(points)
print vor.vertices
voronoi_plot_2d(vor,ax = fig.gca())
#plt.savefig('abc.png')
plt.show()
radius = 20
rangeX = (0, 960)
rangeY = (0, 480)
qty = 9
points = points(radius, rangeX, rangeY, qty)
plot1(points,fig)
My Input:
My output:
This is for n = 9 I would appreciate any help I can get.

Apply rotation matrix to vector + plot it

I have created a vector (v) and would like to perform the rotMatrix function on it. I cannot figure out how to call the function rotMatrix with a degree of 30 on the vector (v). I am also plotting the vectors.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("white")
import math
def rotMatrix(angle):
return np.array([[np.cos(np.degrees(angle)), np.arcsin(np.degrees(angle))], [np.sin(np.degrees(angle)), np.cos(np.degrees(angle))]])
v = np.array([3,7])
v30 = rotMatrix(np.degrees(30)).dot(v)
plt.arrow(0,0,v[0],v[1], head_width=0.8, head_length=0.8)
plt.arrow(0,0,v30[0],v30[1],head_width=0.8, head_length=0.8)
plt.axis([-5,5,0,10])
plt.show()
In your rotMatrix function you have used the arcsin() function. You want to use -sin() You should also convert your degrees value to radians
return np.array([[np.cos(np.radians(angle)),
-np.sin(np.radians(angle))],
[np.sin(np.radians(angle)),
np.cos(np.radians(angle))]])
Or slightly improve efficiently and readability by
c = np.cos(np.radians(angle))
s = np.sin(np.radians(angle))
return np.array([[c, -s], [s, c]])
and the call with
rotMatrix(30).dot(v)
-sin and arcsin are very different.
When in doubt, play around with the calculations in an interactive Python/numpy session.
In [23]: 30/180*np.pi # do it yourself convsion - easy
Out[23]: 0.5235987755982988
In [24]: np.radians(30) # degrees to radians - samething
Out[24]: 0.52359877559829882
In [25]: np.sin(np.radians(30)) # sin(30deg)
Out[25]: 0.49999999999999994
enter image description here import numpy as np
import matplotlib.pyplot as plt
A = np.array([[3],[-3]])
n = np.linspace(0,2*np.pi,100)
def rotate_me(A,n):
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols = 2, figsize=(18, 16))
buf1 = []
buf11 = []
buf12 = []
buf13 = []
buf1p = []
buf2 = []
# t = []
for theta in n:
x=2
x1=3
x2=2
x3=3
# xp=3
# theta = 1/p
origin = [0],[0]
R = np.array([[x*np.cos(theta),-np.sin(theta)],
[np.sin(theta),np.cos(theta)]])
R1 = np.array([[np.cos(theta),-np.sin(theta)],
[np.sin(theta),np.cos(theta)*x1]])
R2 = np.array([[np.cos(theta),-np.sin(theta)],
[x2*np.sin(theta),np.cos(theta)]])
R3 = np.array([[np.cos(theta),-np.sin(theta)*x3],
[np.sin(theta),np.cos(theta)]])
Rp = np.array([[np.cos(theta),-np.sin(theta)],
[np.sin(theta),np.cos(theta)]])
V = R.dot(A)
V1 = R1.dot(A)
V2 = R2.dot(A)
V3 = R3.dot(A)
Vp = Rp.dot(A)
buf1.append(np.linalg.norm(V))
buf11.append(np.linalg.norm(V1))
buf12.append(np.linalg.norm(V2))
buf13.append(np.linalg.norm(V3))
buf1p.append(np.linalg.norm(Vp))
# buf2.append(np.linalg.norm(A))
ax1.quiver(*origin,V[0,:],V[1,:], scale=21,color ='r',label='cos')
ax1.quiver(*origin,V1[0,:],V1[1,:], scale=21,color ='g',label='cos')
ax1.quiver(*origin,V2[0,:],V2[1,:], scale=21,color ='m',label='sin')
ax1.quiver(*origin,V3[0,:],V3[1,:], scale=21,color ='b',label='-sin')
ax1.quiver(*origin,Vp[0,:],Vp[1,:], scale=21,color ='k',label='pure')
# print(theta)
# ax1.legend()
ax2.plot(n,buf1,color ='r')
ax2.plot(n,buf11,color ='g')
ax2.plot(n,buf12,color ='m')
ax2.plot(n,buf13,color ='b')
ax2.plot(n,buf1p,color ='k')
# ax2.plot(n,buf2,color ='b')
ax2.set_xlabel('angle')
ax2.set_ylabel('magnitude')
plt.show()
# return buf1,buf2
rotate_me(A,n)

Categories