Find corner pattern in data set - python

I have a set of data like these:
And I want to recognize this 2 kind of corner shape, is there any way? I wrote s snippet but it sucks.
In this way I am trying to find desc corner (from top to down) and to find asc corner I am "rotating" the dataset and I apply the same algorithm for desc corners.
...
n_rows = len(matrix)
edge_closing_left_total = 0
j=0
for i in range(n_rows):
edge_closing_left = True
current_value = matrix[i][j]
if current_value >= 230:
current_row = i
start = j+1
end = j+1+3
for k in range(start, end):
if current_row+2 < n_rows:
if matrix[current_row][k]<230 and matrix[current_row+1][k]<230 or matrix[current_row+1][k]<230 and matrix[current_row+2][k]<=230:
current_row+=1
else:
edge_closing_left = False
break
if edge_closing_left:
edge_closing_left_total+=1
return edge_closing_left_total
Here the csv dataset file.

So you need to do template/pattern matching. This could be achieved with correlation. The following code should demonstrate how to do it using scipy:
import pandas as pd
from scipy.signal import correlate2d
img = pd.read_csv('matrix.csv', header=None).to_numpy()
norm = img - img.mean() # subtract mean to normalize
edge = norm[26:36, :] # the edge template, adjust if needed
corr = correlate2d(norm, edge, mode='valid')
auto_corr = correlate2d(edge, edge, mode='valid')
corr /= auto_corr # normalize correlation so that 1 means perfect correlation
corr_cutoff = .9 # 1 is pixel-perfect match
print(f'Found edge template {(corr > corr_cutoff).sum()} times in image when taking a similarity cut-off of {corr_cutoff}')
# Found edge template 2 times in image when taking a similarity cut-off of 0.9
The original image (transposed to make it wider than high):
The edge template looks like this:
The correlation map looks like this (note: this is actually only 1 px wide but blown up for visualization):
The thresholded correlation map with cut-off 0.9 (giving 2 results as expected, choose threshold as needed):

Related

Refine Lee Filter implementation in python. Filter according to the edge assigned at the pixel

Ref:
Question: Speckle ( Lee Filter) in Python
Relevant Answer to the current question - Code is borrowed from here.
from scipy.ndimage.filters import uniform_filter
from scipy.ndimage.measurements import variance
def lee_filter(img, size):
img_mean = uniform_filter(img, (size, size))
img_sqr_mean = uniform_filter(img**2, (size, size))
img_variance = img_sqr_mean - img_mean**2
overall_variance = variance(img)
img_weights = img_variance / (img_variance + overall_variance)
img_output = img_mean + img_weights * (img - img_mean)
return img_output
Question:
In the above code, instead of uniform size for the filter, I would like to specify a one of predefined windows and filter the image only with respect to the window at that pixel.
edge1 = np.array([[1,1,1],[0,1,1],[0,0,1]])
edge2 = np.array([[0,1,1],[0,1,1],[0,1,1]])
edge3 = np.array([[0,0,1],[0,1,1],[1,1,1]])
edge4 = np.array([[0,0,0],[1,1,1],[1,1,1]])
edge5 = np.array([[1,0,0],[1,1,0],[1,1,1]])
edge6 = np.array([[1,1,0],[1,1,0],[1,1,0]])
edge7 = np.array([[1,1,1],[1,1,0],[1,0,0]])
edge8 = np.array([[1,1,1],[1,1,1],[0,0,0]])
I want to convolve over the image and assign an edge to every pixel. Which will be the window for the mean filter (instead of uniform filter).
#The below program is a guess program based on algorithm and is incorrect (also incomplete). Please help me work this out on assigning window and filtering the image based on the window.
def custom_window_filter(img):
img_mean = uniform_filter(img,(5,5))
edge1 = np.array([[-1,0,1],[-1,0,1],[-1,0,1]])
edge2 = np.array([[0,1,1],[-1,0,1],[-1,-1,0]])
edge3 = np.array([[1,1,1],[0,0,0],[-1,-1,-1]])
edge4 = np.array([[1,1,0],[1,0,-1],[0,-1,-1]])
edge1_avg = sg.convolve(img_mean,edge1)
edge2_avg = sg.convolve(img_mean,edge2)
edge3_avg = sg.convolve(img_mean,edge3)
edge4_avg = sg.convolve(img_mean,edge4)
choices = np.ones(img.shape)
choices[np.where(np.abs(edge2_avg) > np.abs(edge1_avg))] = 2
choices[np.where(np.abs(edge3_avg) > np.abs(edge2_avg))] = 3
choices[np.where(np.abs(edge4_avg) > np.abs(edge3_avg))] = 4
'''
Use choices here to further refine the edge.
After acquiring the edge, use that edge to get mean and std deviation from the contents of the uniform data.
Use the said mean and std deviation to do a gaussian filter on that detected uniform data on the side of the edge.
Optional: Scale it to arbitrary window size 3x3 or 5x5 or 7x7 or 11x11
'''
P.S. I'm actually using images of size 122k x 5k (float32), can the processing be sped up using numba as it supports scipy and numpy operations.

Why is it that passing an image through a low pass filter yields values higher than the original image?

I have a hybrid image that was created by superimposing the low frequencies of one image with the high frequencies of another. I'm trying to separate (de-hybridize) this image by passing it through a low-pass filter to extract the low frequencies (one of the two images), and then subtracting that from the original image to yield the other image (high frequencies).
**Problem: ** When I extract the low frequencies, the values are all higher than the original image, so when I subtract the low frequencies from the original image, what's left is a bunch of negative values.
Does anyone know why my low pass filter is yielding higher frequency values than the original image?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from numpy.fft import fft2, ifft2, fftshift, ifftshift
# Make Gaussian filter
def makeGaussianFilter(numRows, numCols, sigma, highPass=True):
centerI = int(numRows/2) + 1 if numRows % 2 == 1 else int(numRows/2)
centerJ = int(numCols/2) + 1 if numCols % 2 == 1 else int(numCols/2)
def gaussian(i,j):
coefficient = np.exp(-1.0 * ((i - centerI)**2 + (j - centerJ)**2) / (2 * sigma**2))
return 1 - coefficient if highPass else coefficient
return np.array([[gaussian(i,j) for j in range(numCols)] for i in range(numRows)])
# Filter discrete Fourier transform
def filterDFT(imageMatrix, filterMatrix):
shiftedDFT = fftshift(fft2(imageMatrix))
filteredDFT = shiftedDFT * filterMatrix
return ifft2(ifftshift(filteredDFT))
# Low-pass filter
def lowPass(imageMatrix, sigma):
n,m = imageMatrix.shape
return filterDFT(imageMatrix, makeGaussianFilter(n, m, sigma, highPass=False))
# Read in einsteinandwho.png and convert to format that can be displayed by plt.imshow
im3 = mpimg.imread('einsteinandwho.png')
rows = im3.shape[0]
cols = im3.shape[1]
img3 = np.ones((rows, cols, 4))
for i in range(rows):
for j in range(cols):
img3[i][j][0:3] = im3[i][j]
img3[j][j][3] = 1
# Extract low frequencies and convert to format that can be displayed by plt.imshow
lowPassed = np.real(lowPass(im3, 10))
low = np.ones((rows, cols, 4))
for i in range(rows):
for j in range(cols):
low[i][j][0:3] = lowPassed[i][j]
low[j][j][3] = 1
# Remove low frequencies from image
output = img3[:,:,0:3] - low[:,:,0:3]
Does anyone know why my low pass filter is yielding higher frequency values than the original image?
Do notice the difference between pixel values and frequency values. You are seeing the pixel values being higher, not the frequency values!
When I run your code I see the high-frequency component having both negative and positive pixel values, not all negative values. It is expected for this image to have a zero mean. The zero frequency component (also called DC component) is the one that sets the mean pixel value. By subtracting a low-pass filtered image, you are setting the zero frequency to 0, and thus setting the mean pixel value to 0 (the low-pass filtered image contains all of the power of the zero frequency).

Search for all templates using scikit-image

I am trying to follow the tutorial from scikit-image regarding Template Matching (check it here).
Using just this example, I would like to find all matching coins (maxima) in the image, not only this one which gave the highest score. I was thinking about using:
maxima = argrelextrema(result, np.greater)
but the problem is that it finds also very small local maxima, which are just a noise. Is there any way to screen numpy array and find the strongest maxima? Thanks!
To find all the coins the documentation suggests "...you should use a proper peak-finding function." The easiest of these is probably peak_local_max (as suggested in the comments) which is also from skimage, and has a manual page here. Using some reasonable numbers in the *args gets the peaks out of the response image.
The second comment about the peaks being displaced is also discussed in the documentation
"Note that the peaks in the output of match_template correspond to the origin (i.e. top-left corner) of the template."
One can manually correct for this (by translating the peaks by the side lengths of the template), or you can set the pad_input bool to True (source), which as a by-product means that the peaks in the response function line up with the center of the template at the point of maximal overlap.
Combining these two bits into a script we get something like:
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
from skimage.feature import match_template
from skimage.feature import peak_local_max # new import!
image = data.coins()
coin = image[170:220, 75:130]
result = match_template(image, coin,pad_input=True) #added the pad_input bool
peaks = peak_local_max(result,min_distance=10,threshold_rel=0.5) # find our peaks
# produce a plot equivalent to the one in the docs
plt.imshow(result)
# highlight matched regions (plural)
plt.plot(peaks[:,1], peaks[:,0], 'o', markeredgecolor='r', markerfacecolor='none', markersize=10)
I have been digging and found some solution but unfortunately I am not sure if I know what exactly is done in the script. I have slightly modified script found here:
neighborhood_size = 20 #how many pixels
threshold = 0.01 #threshold of maxima?
data_max = filters.maximum_filter(result, neighborhood_size)
maxima = (result == data_max)
data_min = filters.minimum_filter(result, neighborhood_size)
diff = ((data_max - data_min) > threshold)
maxima[diff == 0] = 0
x_image,y_image = [], []
temp_size = coin.shape[0]
labeled, num_objects = ndimage.label(maxima)
slices = ndimage.find_objects(labeled)
x, y = [], []
for dy,dx in slices:
x_center = (dx.start + dx.stop - 1)/2
x.append(x_center)
y_center = (dy.start + dy.stop - 1)/2
y.append(y_center)
fig, (raw,found) = plt.subplots(1,2)
raw.imshow(image,cmap=plt.cm.gray)
raw.set_axis_off()
found.imshow(result)
found.autoscale(False)
found.set_axis_off()
plt.plot(x,y, 'ro')
plt.show()
and does this:
I also realized, that coordinates of found peaks are shifted in comparison to raw image. I think the difference comes from the template size. I will update when I will find out more.
EDIT: with slight code modification I was able also to find places on the input image:
x_image_center = (dx.start + dx.stop - 1 + temp_size) / 2
x_image.append(x_image_center)
y_image_center = (dy.start + dy.stop - 1 + temp_size) / 2
y_image.append(y_image_center)

Detecting border pixel of a segmentation label

I can compute the SLIC boundaries using skimage as follows:
def compute_superpixels(frame, num_pixels=100, std=5, iter_max=10,
connectivity=False, compactness=10.0):
return slic(frame, n_segments=num_pixels, sigma=std, max_iter=iter_max,
enforce_connectivity=connectivity, compactness=compactness)
Now, what I would like to do is get the index of pixels which form the boundary of each label. So my idea was to get all pixels belonging to a given segment and then check which pixels have a change in all two directions
def boundary_pixels(segments, index):
# Get all pixels having a given index
x, y = np.where(segments == index)
right = x + 1
# check we are in bounds
right_mask = right < segments.shape[0]
down = y + 1
down_mask = down < segments.shape[1]
left = x - 1
left_mask = left >= 0
up = y - 1
up_mask = up >= 0
neighbors_1 = np.union1d(right_n, down_n)
neighbors_2 = np.union1d(left_n, up_n)
neighbors = np.union1d(neighbors_1, neighbors_2)
# Not neighbours to ourselves
neighbors = np.delete(neighbors, np.where(neighbors == i))
However, with this all I managed to do was to get the neighbours in the 4 directions of a given label. Can someone suggest some way to actually get all pixels on the border of the label.
I found an answer to my own question. The mark_boundaries in the skimage.segmentation package does exactly what I needed.
Usage:
processed = mark_boundaries(frame, segments==some_segment)
Here frame is he current image frame and segments is the label array. some_segment is the label integer index whose boundaries we are interested in.
You can make use of the find_contours function available in skimage.measure module to find the co-ordinates of the pixels along the boundary. An example is available at find_contours.. Next, you can change for change in both directions as needed.

Correct method and Python package that can find width of an image's feature

The input is a spectrum with colorful (sorry) vertical lines on a black background. Given the approximate x coordinate of that band (as marked by X), I want to find the width of that band.
I am unfamiliar with image processing. Please direct me to the correct method of image processing and a Python image processing package that can do the same.
I am thinking PIL, OpenCV gave me an impression of being overkill for this particular application.
What if I want to make this an expert system that can classify them in the future?
I'll give a complete minimal working example (as suggested by sega_sai). I don't have access to your original image, but you'll see it doesn't really matter! The peak distributions found by the code below are:
Mean values at: 26.2840960523 80.8255092125
import Image
from scipy import *
from scipy.optimize import leastsq
# Load the picture with PIL, process if needed
pic = asarray(Image.open("band2.png"))
# Average the pixel values along vertical axis
pic_avg = pic.mean(axis=2)
projection = pic_avg.sum(axis=0)
# Set the min value to zero for a nice fit
projection /= projection.mean()
projection -= projection.min()
# Fit function, two gaussians, adjust as needed
def fitfunc(p,x):
return p[0]*exp(-(x-p[1])**2/(2.0*p[2]**2)) + \
p[3]*exp(-(x-p[4])**2/(2.0*p[5]**2))
errfunc = lambda p, x, y: fitfunc(p,x)-y
# Use scipy to fit, p0 is inital guess
p0 = array([0,20,1,0,75,10])
X = xrange(len(projection))
p1, success = leastsq(errfunc, p0, args=(X,projection))
Y = fitfunc(p1,X)
# Output the result
print "Mean values at: ", p1[1], p1[4]
# Plot the result
from pylab import *
subplot(211)
imshow(pic)
subplot(223)
plot(projection)
subplot(224)
plot(X,Y,'r',lw=5)
show()
Below is a simple thresholding method to find the lines and their width, it should work quite reliably for any number of lines. The yellow and black image below was processed using this script, the red/black plot illustrates the found lines using parameters of threshold = 0.3, min_line_width = 5)
The script averages the rows of an image, and then determines the basic start and end positions of each line based on a threshold (which you can set between 0 and 1), and a minimum line width (in pixels). By using thresholding and minimum line width you can easily filter your input images to get the lines out of them. The first function find_lines returns all the lines in an image as a list of tuples containing the start, end, center, and width of each line. The second function find_closest_band_width is called with the specified x_position, and returns the width of the closest line to this position (assuming you want distance to centre for each line). As the lines are saturated (255 cut-off per channel), their cross-sections are not far from a uniform distribution, so I don't believe trying to fit any kind of distribution is really going to help too much, just unnecessarily complicates.
import Image, ImageStat
def find_lines(image_file, threshold, min_line_width):
im = Image.open(image_file)
width, height = im.size
hist = []
lines = []
start = end = 0
for x in xrange(width):
column = im.crop((x, 0, x + 1, height))
stat = ImageStat.Stat(column)
## normalises by 2 * 255 as in your example the colour is yellow
## if your images start using white lines change this to 3 * 255
hist.append(sum(stat.sum) / (height * 2 * 255))
for index, value in enumerate(hist):
if value > threshold and end >= start:
start = index
if value < threshold and end < start:
if index - start < min_line_width:
start = 0
else:
end = index
center = start + (end - start) / 2.0
width = end - start
lines.append((start, end, center, width))
return lines
def find_closest_band_width(x_position, lines):
distances = [((value[2] - x_position) ** 2) for value in lines]
index = distances.index(min(distances))
return lines[index][3]
## set your threshold, and min_line_width for finding lines
lines = find_lines("8IxWA_sample.png", 0.7, 4)
## sets x_position to 59th pixel
print 'width of nearest line:', find_closest_band_width(59, lines)
I don't think that you need anything fancy for you particular task.
I would just use PIL + scipy. That should be enough.
Because you essentially need to take your image, make a 1D-projection of it
and then fit a Gaussian or something like that to it. The information about the approximate location of the band should be used a first guess for the fitter.

Categories