How to identify dashed lines in an image? - python

I am trying to identify small dashed lines in an image. An example would be identifying copy area in an excel type of application.
I have tried this.
I am finding it difficult to chose the filter sizes. So, I tried a different approach using Fourier Transform to check repeatability.
Given I know the dashed line pixel repetition range I go row by row by using a moving window to check for periodicity by finding dominant frequency in that window.
If dominant frequency is in range of dashed lines period I set the mask in the mask image. I repeat the same for columns. However this is still failing. Any suggestions/other techniques ?
Here is the code:
import cv2
import numpy as np
img = cv2.imread('test.png')
imgGray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('imgGray', imgGray)
rows,cols = imgGray.shape
maskImage = np.full((rows, cols), 0, dtype=np.uint8)
kernelL = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
imgLaplacian = cv2.filter2D(imgGray, cv2.CV_32F, kernelL)
imgResult = imgLaplacian
imgResult = np.clip(imgResult, 0, 255)
imgResult = imgResult.astype('uint8')
imgLaplacian = imgResult
cv2.imshow('imgLaplacian', imgLaplacian)
dashLineSearchInterval = 30
fmaxPixel =9 # minimum interval for dash repetation
fminPixel =7 # maximum interval for dash repetation
stride =2
for y in range(0,rows-dashLineSearchInterval,stride):
for x in range(0,cols-dashLineSearchInterval,stride):
kX = (imgLaplacian[y,x:x+ dashLineSearchInterval]).copy()
kX = kX - np.mean(kX)
N= dashLineSearchInterval
freq = np.fft.fftfreq(N)
ft = np.fft.fft(kX) # fourier transform
power = ft.real**2 + ft.imag**2 # power
maxPowerFreq= np.argmax(power) # dominant frequency
domFreq = freq [maxPowerFreq]
if(domFreq<0):
domFreq = -domFreq
#print(domFreq)
if float(1/fmaxPixel) <= domFreq <= float(1/fminPixel) :
maskImage[y,x:x+dashLineSearchInterval]=255
for x in range(0,cols-dashLineSearchInterval,stride):
for y in range(0,rows-dashLineSearchInterval,stride):
kY = (imgLaplacian[y:y+dashLineSearchInterval,x]).copy()
kY = kY - np.mean(kY)
N= dashLineSearchInterval
freq = np.fft.fftfreq(N)
ft = np.fft.fft(kY) # fourier transform
power = ft.real**2 + ft.imag**2 # power
maxPowerFreq= np.argmax(power) # dominant frequency
domFreq = freq [maxPowerFreq]
if(domFreq<0):
domFreq = -domFreq
#print(domFreq)
if float(1/fmaxPixel) <= domFreq <= float(1/fminPixel) :
maskImage[y:y+dashLineSearchInterval,x]=255
cv2.imshow('maskImage', maskImage)
cv2.waitKey()

Related

Doing naive affine_transforms (shear numpy image using numpy)

from scipy import ndimage
height, width, colors = image.shape
transform = [[1, 0, 0],
[0.5, 1, 0],
[0, 0, 1]]
sheared_array = ndimage.affine_transform(image,
transform,
offset=(0, -height*0.7, 0),
output_shape=(height, width*2, colors))
plt.imshow(sheared_array)
My current code does this. My aim is to shear the image by any degree X.
I want to do the same thing with a naive approach. As in, without any pre-defined functions. Just python/numpy code from scratch.
Given the image:
the following code should do what you want to achieve. It copies y-rows of pixels from the numpy array representing the source image to a new created wider image at appropriate x-offsets calculated from the given shear angle. The variable names in a following code are chosen in a way explaining what they are used for providing further details about what the code does:
from PIL import Image
import numpy as np
shearAngleDegrees = 30
PILimg = Image.open('shearNumpyImageByAngle.jpg')
#PILimg.show()
npImg = np.asarray(PILimg)
def shearNpImgByAngle(numpyImageArray, shearAngleDegrees, maxShearAngle=75):
import numpy as np
from math import tan, radians
assert -maxShearAngle <= shearAngleDegrees <= maxShearAngle
ccw = True if shearAngleDegrees > 0 else False # shear counter-clockwise?
imgH, imgW, imgRGBtplItems = npImg.shape
shearAngleRadians = radians(shearAngleDegrees)
imgWplus2imgH = abs(tan(shearAngleRadians)) # (plus in width)/(image height)
imgWplus = int((imgH-1)*imgWplus2imgH) # image width increase in pixels
npImgOut = np.zeros((imgH, imgW+imgWplus, imgRGBtplItems), dtype='uint8')
Wplus, Wplus2H = (0, -imgWplus2imgH) if ccw else (imgWplus,imgWplus2imgH)
for y in range(imgH):
shiftX = Wplus-int(y*Wplus2H)
npImgOut[y][shiftX:shiftX+imgW] = npImg[y]
return npImgOut
#:def
npImgOut = shearNpImgByAngle(npImg, shearAngleDegrees)
PILout = Image.fromarray(npImgOut)
PILout.show()
PILout.save('shearNumpyImageByAngle_shearedBy30deg.jpg')
gives:
As a nice add-on to the above code an extension filling the black edges of the sheared image mirroring the source picture around its sides:
def filledShearNpImgByAngle(npImg, angleDeg, fill=True, maxAngle=75):
import numpy as np
from math import tan, radians
assert -maxAngle <= angleDeg <= maxAngle
ccw = True if angleDeg > 0 else False # shear counter-clockwise?
imgH, imgW, imgRGBtplItems = npImg.shape
angleRad = radians(angleDeg)
imgWplus2imgH = abs(tan(angleRad)) # (plus in width)/(image height)
imgWplus = int((imgH-1)*imgWplus2imgH) # image add. width in pixels
npImgOut = np.zeros((imgH, imgW+imgWplus, imgRGBtplItems),
dtype=npImg.dtype) # 'uint8')
Wplus, Wplus2H = (0, -imgWplus2imgH) if ccw else (imgWplus, imgWplus2imgH)
for y in range(imgH):
shiftXy = Wplus-int(y*Wplus2H)
npImgOut[y][shiftXy:shiftXy+imgW] = npImg[y]
if fill:
assert imgW > imgWplus
npImgOut[y][0:shiftXy] = np.flip(npImg[y][0:shiftXy], axis=0)
npImgOut[y][imgW+shiftXy:imgW+imgWplus] = np.flip(npImg[y][imgW-imgWplus-1+shiftXy:imgW-1], axis=0)
[imgW-x-2]
return npImgOut
#:def
from PIL import Image
import numpy as np
PILimg = Image.open('shearNumpyImageByAngle.jpg')
npImg = np.asarray(PILimg)
shearAngleDegrees = 20
npImgOut = filledShearNpImgByAngle(npImg, shearAngleDegrees)#, fill=False)
shearAngleDegrees = 10
npImgOut = filledShearNpImgByAngle(npImgOut, shearAngleDegrees)#, fill=False)
PILout = Image.fromarray(npImgOut)
PILout.show()
PILout.save('shearNumpyImageByAngle_filledshearBy30deg.jpg')
gives:
or other way around:

Finding anomalous values from sinusoidal data

How can I find anomalous values from following data. I am simulating a sinusoidal pattern. While I can plot the data and spot any anomalies or noise in data, but how can I do it without plotting the data. I am looking for simple approaches other than Machine learning methods.
import random
import numpy as np
import matplotlib.pyplot as plt
N = 10 # Set signal sample length
t1 = -np.pi # Simulation begins at t1
t2 = np.pi; # Simulation ends at t2
in_array = np.linspace(t1, t2, N)
print("in_array : ", in_array)
out_array = np.sin(in_array)
plt.plot(in_array, out_array, color = 'red', marker = "o") ; plt.title("numpy.sin()")
Inject random noise
noise_input = random.uniform(-.5, .5); print("Noise : ",noise_input)
in_array[random.randint(0,len(in_array)-1)] = noise_input
print(in_array)
plt.plot(in_array, out_array, color = 'red', marker = "o") ; plt.title("numpy.sin()")
Data with noise
I've thought of the following approach to your problem, since you have only some values that are anomalous in the time vector, it means that the rest of the values have a regular progression, which means that if we gather all the data points in the vector under clusters and calculate the average step for the biggest cluster (which is essentially the pool of values that represent the real deal), then we can use that average to do a triad detection, in a given threshold, over the vector and detect which of the elements are anomalous.
For this we need two functions: calculate_average_step which will calculate that average for the biggest cluster of close values, and then we need detect_anomalous_values which will yield the indexes of the anomalous values in our vector, based on that average calculated earlier.
After we detected the anomalous values, we can go ahead and replace them with an estimated value, which we can determine from our average step value and by using the adjacent points in the vector.
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def calculate_average_step(array, threshold=5):
"""
Determine the average step by doing a weighted average based on clustering of averages.
array: our array
threshold: the +/- offset for grouping clusters. Aplicable on all elements in the array.
"""
# determine all the steps
steps = []
for i in range(0, len(array) - 1):
steps.append(abs(array[i] - array[i+1]))
# determine the steps clusters
clusters = []
skip_indexes = []
cluster_index = 0
for i in range(len(steps)):
if i in skip_indexes:
continue
# determine the cluster band (based on threshold)
cluster_lower = steps[i] - (steps[i]/100) * threshold
cluster_upper = steps[i] + (steps[i]/100) * threshold
# create the new cluster
clusters.append([])
clusters[cluster_index].append(steps[i])
# try to match elements from the rest of the array
for j in range(i + 1, len(steps)):
if not (cluster_lower <= steps[j] <= cluster_upper):
continue
clusters[cluster_index].append(steps[j])
skip_indexes.append(j)
cluster_index += 1 # increment the cluster id
clusters = sorted(clusters, key=lambda x: len(x), reverse=True)
biggest_cluster = clusters[0] if len(clusters) > 0 else None
if biggest_cluster is None:
return None
return sum(biggest_cluster) / len(biggest_cluster) # return our most common average
def detect_anomalous_values(array, regular_step, threshold=5):
"""
Will scan every triad (3 points) in the array to detect anomalies.
array: the array to iterate over.
regular_step: the step around which we form the upper/lower band for filtering
treshold: +/- variation between the steps of the first and median element and median and third element.
"""
assert(len(array) >= 3) # must have at least 3 elements
anomalous_indexes = []
step_lower = regular_step - (regular_step / 100) * threshold
step_upper = regular_step + (regular_step / 100) * threshold
# detection will be forward from i (hence 3 elements must be available for the d)
for i in range(0, len(array) - 2):
a = array[i]
b = array[i+1]
c = array[i+2]
first_step = abs(a-b)
second_step = abs(b-c)
first_belonging = step_lower <= first_step <= step_upper
second_belonging = step_lower <= second_step <= step_upper
# detect that both steps are alright
if first_belonging and second_belonging:
continue # all is good here, nothing to do
# detect if the first point in the triad is bad
if not first_belonging and second_belonging:
anomalous_indexes.append(i)
# detect the last point in the triad is bad
if first_belonging and not second_belonging:
anomalous_indexes.append(i+2)
# detect the mid point in triad is bad (or everything is bad)
if not first_belonging and not second_belonging:
anomalous_indexes.append(i+1)
# we won't add here the others because they will be detected by
# the rest of the triad scans
return sorted(set(anomalous_indexes)) # return unique indexes
if __name__ == "__main__":
N = 10 # Set signal sample length
t1 = -np.pi # Simulation begins at t1
t2 = np.pi; # Simulation ends at t2
in_array = np.linspace(t1, t2, N)
# add some noise
noise_input = random.uniform(-.5, .5);
in_array[random.randint(0, len(in_array)-1)] = noise_input
noisy_out_array = np.sin(in_array)
# display noisy sin
plt.figure()
plt.plot(in_array, noisy_out_array, color = 'red', marker = "o");
plt.title("noisy numpy.sin()")
# detect anomalous values
average_step = calculate_average_step(in_array)
anomalous_indexes = detect_anomalous_values(in_array, average_step)
# replace anomalous points with an estimated value based on our calculated average
for anomalous in anomalous_indexes:
# try forward extrapolation
try:
in_array[anomalous] = in_array[anomalous-1] + average_step
# else try backwward extrapolation
except IndexError:
in_array[anomalous] = in_array[anomalous+1] - average_step
# generate sine wave
out_array = np.sin(in_array)
plt.figure()
plt.plot(in_array, out_array, color = 'green', marker = "o");
plt.title("cleaned numpy.sin()")
plt.show()
Noisy sine:
Cleaned sine:
Your problem relies in the time vector (which is of 1 dimension). You will need to apply some sort of filter on that vector.
First thing that came to mind was medfilt (median filter) from scipy and it looks something like this:
from scipy.signal import medfilt
l1 = [0, 10, 20, 30, 2, 50, 70, 15, 90, 100]
l2 = medfilt(l1)
print(l2)
the output of this will be:
[ 0. 10. 20. 20. 30. 50. 50. 70. 90. 90.]
the problem with this filter though is that if we apply some noise values to the edges of the vector like [200, 0, 10, 20, 30, 2, 50, 70, 15, 90, 100, -50] then the output would be something like [ 0. 10. 10. 20. 20. 30. 50. 50. 70. 90. 90. 0.] and obviously this is not ok for the sine plot since it will produce the same artifacts for the sine values array.
A better approach to this problem is to treat the time vector as an y output and it's index values as the x input and do a linear regression on the "time linear function", not the quotes, it just means we're faking the 2 dimensional model by applying a fake X vector. The code implies the use of scipy's linregress (linear regression) function:
from scipy.stats import linregress
l1 = [5, 0, 10, 20, 30, -20, 50, 70, 15, 90, 100]
l1_x = range(0, len(l1))
slope, intercept, r_val, p_val, std_err = linregress(l1_x, l1)
l1 = intercept + slope * l1_x
print(l1)
whose output will be:
[-10.45454545 -1.63636364 7.18181818 16. 24.81818182
33.63636364 42.45454545 51.27272727 60.09090909 68.90909091
77.72727273]
Now let's apply this to your time vector.
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import linregress
N = 20
# N = 10 # Set signal sample length
t1 = -np.pi # Simulation begins at t1
t2 = np.pi; # Simulation ends at t2
in_array = np.linspace(t1, t2, N)
# add some noise
noise_input = random.uniform(-.5, .5);
in_array[random.randint(0, len(in_array)-1)] = noise_input
# apply filter on time array
in_array_x = range(0, len(in_array))
slope, intercept, r_val, p_val, std_err = linregress(in_array_x, in_array)
in_array = intercept + slope * in_array_x
# generate sine wave
out_array = np.sin(in_array)
print("OUT ARRAY")
print(out_array)
plt.plot(in_array, out_array, color = 'red', marker = "o") ; plt.title("numpy.sin()")
plt.show()
the output will be:
the resulting signal will be an approximation of the original, as it is with any form of extrapolation/interpolation/regression filtering.

wrong colors in result - Pyramid Blending using OpenCV and python

I am trying to blend two images, given a mask, using the following script:
import cv2
import numpy as np
def pyramid_blend(A, B, m, num_levels):
GA = A.copy()
GB = B.copy()
GM = m.copy()
gpA = [GA]
gpB = [GB]
gpM = [GM]
for i in xrange(num_levels):
GA = cv2.pyrDown(GA)
GB = cv2.pyrDown(GB)
GM = cv2.pyrDown(GM)
gpA.append(np.float32(GA))
gpB.append(np.float32(GB))
gpM.append(np.float32(GM))
lpA = [gpA[num_levels - 1]]
lpB = [gpB[num_levels - 1]]
gpMr = [gpM[num_levels - 1]]
for i in xrange(num_levels - 1, 0, -1):
size = (gpA[i - 1].shape[1], gpA[i - 1].shape[0])
LA = np.subtract(gpA[i - 1], cv2.pyrUp(gpA[i], dstsize=size))
LB = np.subtract(gpB[i - 1], cv2.pyrUp(gpB[i], dstsize=size))
lpA.append(LA)
lpB.append(LB)
gpMr.append(gpM[i - 1])
LS = []
for la, lb, gm in zip(lpA, lpB, gpMr):
ls = la * gm + lb * (1.0 - gm)
LS.append(ls)
ls_ = LS[0]
for i in xrange(1, num_levels):
size = (LS[i].shape[1], LS[i].shape[0])
ls_ = cv2.add(cv2.pyrUp(ls_, dstsize=size), np.float32(LS[i]))
return ls_
if __name__ == '__main__':
A = cv2.imread('./black.jpg')
B = cv2.imread('./white.jpg')
m = cv2.imread('./mask.jpg')
lpb = pyramid_blend(A, B, m, 6)
What i did:
Find the Gaussian Pyramids of the images.
From Gaussian Pyramids, find their Laplacian Pyramids
Join the left half and right half of images in each levels of Laplacian Pyramids using mask.
From this joint image pyramids, reconstruct the original image.
The images are used -
The result i get -
For some reason, and i dont understand why, the colors of the result image are completely off.
I can detect two problems:
You assume your mask is 0 or 1.0, but it is actually 0 or 255, when loading your mask you can do the following:
m[m==255]=1.0
You are probably displaying float32 images with imshow.... Just convert it to np.uint8 to display it...
lpb = np.uint8(lpb)
Saying that, still you probably have another error, but I do not see it right now, since it still have some tiny parts with weird colors, and also I expect the blend to work more smooth in the middle. Here is my result:
UPDATE
It looks like when you do too many levels, the artifacts appear, when you have only 3 (instead of 6) weird coloring appears (with the fixes stated above as well). Probably the colors needs to be saturated perhaps when subtracting?
In case anyone finds this thread in the future and wonders about what was wrong with the above code, the reason for the image artifacts is because the returned image, due to floating point rounding error, has some values above 255 and some values below 0. The ones below 0, when casting to uint8, get wrapped around to a large value, which is why some pixels get mangled.
See my commented and lightly modified version of the original code below. I only made three changes, including the mask change that api55 mentioned.
import cv2
import numpy as np
def pyramid_blend(A, B, m, num_levels):
# 1. as in api55's answer, mask needs to be from 0 to 1, since you're multiplying a pixel value by it. Since mask
# is binary, we only need to set all values which are 255 to 1
m[m == 255] = 1
GA = A.copy()
GB = B.copy()
GM = m.copy()
gpA = [GA]
gpB = [GB]
gpM = [GM]
for i in range(num_levels):
GA = cv2.pyrDown(GA)
GB = cv2.pyrDown(GB)
GM = cv2.pyrDown(GM)
gpA.append(np.float32(GA))
gpB.append(np.float32(GB))
gpM.append(np.float32(GM))
lpA = [gpA[num_levels - 1]]
lpB = [gpB[num_levels - 1]]
gpMr = [gpM[num_levels - 1]]
for i in range(num_levels - 1, 0, -1):
size = (gpA[i - 1].shape[1], gpA[i - 1].shape[0])
LA = np.subtract(gpA[i - 1], cv2.pyrUp(gpA[i], dstsize=size))
LB = np.subtract(gpB[i - 1], cv2.pyrUp(gpB[i], dstsize=size))
lpA.append(LA)
lpB.append(LB)
gpMr.append(gpM[i - 1])
LS = []
for la, lb, gm in zip(lpA, lpB, gpMr):
ls = la * gm + lb * (1.0 - gm)
LS.append(ls)
ls_ = LS[0]
for i in range(1, num_levels):
size = (LS[i].shape[1], LS[i].shape[0])
ls_ = cv2.add(cv2.pyrUp(ls_, dstsize=size), np.float32(LS[i]))
# 2. because of floating point rounding error, some pixels in ls_ will be larger than 255, and some will be
# lower than 0. When casting back to uint8, this causes pixels lower than 0 to get wrapped around to 255, so we
# should threshold it before passing it back
ls_[ls_ > 255] = 255; ls_[ls_ < 0] = 0
# 3. when passing back, before saving and displaying, need to cast back to a uint8 from float64
return ls_.astype(np.uint8)
if __name__ == '__main__':
A = cv2.imread('./black.jpg')
B = cv2.imread('./white.jpg')
m = cv2.imread('./mask.jpg')
lpb = pyramid_blend(A, B, m, 6)
cv2.imshow('foo', lpb)
cv2.waitKey()
cv2.destroyAllWindows()
This results in the following image:

Compare the LBP in python

I generated a texture image like this
I have to compare two textures. I have used histogram comparison method.
image_file = 'output_ori.png'
img_bgr = cv2.imread(image_file)
height, width, channel = img_bgr.shape
hist_lbp = cv2.calcHist([img_bgr], [0], None, [256], [0, 256])
print("second started")
image_fileNew = 'output_scan.png'
img_bgr_new = cv2.imread(image_fileNew)
height_new, width_new, channel_new = img_bgr_new.shape
print("second lbp")
hist_lbp_new = cv2.calcHist([img_bgr_new], [0], None, [256], [0, 256])
print("compar started")
compare = cv2.compareHist(hist_lbp, hist_lbp_new, cv2.HISTCMP_CORREL)
print(compare)
But this method is not effective. It shows similar results for two different image textures. Also it is not showing too much of variation to identify Print & Scan effect. How do I compare the textures? I thought of analysing the GLCM characteristics.
import cv2
import numpy as np
from skimage.feature import greycomatrix
img = cv2.imread('images/noised_img1.jpg', 0)
image = np.array(img, dtype=np.uint8)
g = greycomatrix(image, [1, 2], [0, np.pi/2], levels=4, normed=True, symmetric=True)
contrast = greycoprops(g, 'contrast')
print(contrast)
In this method, I am getting the output as 2*2 matrix. How do I compare two matrices of several features like contrast, similarity, homogeneity, ASM, energy and correlation?
COMMENT CLARIFICATION
import numpy as np
from PIL import Image
class LBP:
def __init__(self, input, num_processes, output):
# Convert the image to grayscale
self.image = Image.open(input).convert("L")
self.width = self.image.size[0]
self.height = self.image.size[1]
self.patterns = []
self.num_processes = num_processes
self.output = output
def execute(self):
self._process()
if self.output:
self._output()
def _process(self):
pixels = list(self.image.getdata())
pixels = [pixels[i * self.width:(i + 1) * self.width] for i in range(self.height)]
# Calculate LBP for each non-edge pixel
for i in range(1, self.height - 1):
# Cache only the rows we need (within the neighborhood)
previous_row = pixels[i - 1]
current_row = pixels[i]
next_row = pixels[i + 1]
for j in range(1, self.width - 1):
# Compare this pixel to its neighbors, starting at the top-left pixel and moving
# clockwise, and use bit operations to efficiently update the feature vector
pixel = current_row[j]
pattern = 0
pattern = pattern | (1 << 0) if pixel < previous_row[j-1] else pattern
pattern = pattern | (1 << 1) if pixel < previous_row[j] else pattern
pattern = pattern | (1 << 2) if pixel < previous_row[j+1] else pattern
pattern = pattern | (1 << 3) if pixel < current_row[j+1] else pattern
pattern = pattern | (1 << 4) if pixel < next_row[j+1] else pattern
pattern = pattern | (1 << 5) if pixel < next_row[j] else pattern
pattern = pattern | (1 << 6) if pixel < next_row[j-1] else pattern
pattern = pattern | (1 << 7) if pixel < current_row[j-1] else pattern
self.patterns.append(pattern)
def _output(self):
# Write the result to an image file
result_image = Image.new(self.image.mode, (self.width - 2, self.height - 2))
result_image.putdata(self.patterns)
result_image.save("output.png")
I generated texture with this code. I have texture and I have methods to calculate the texture properties, but the question is how to identify the similarity between two textures.
Suppose you have two classes, for example couscous and knitwear, and you wish to classify an unknown color image as either couscous or knitwear. One possible method would be:
Converting the color images to grayscale.
Computing the local binary patterns.
Calculating the normalized histogram of local binary patterns.
The following snippet implements this approach:
import numpy as np
from skimage import io, color
from skimage.feature import local_binary_pattern
def lbp_histogram(color_image):
img = color.rgb2gray(color_image)
patterns = local_binary_pattern(img, 8, 1)
hist, _ = np.histogram(patterns, bins=np.arange(2**8 + 1), density=True)
return hist
couscous = io.imread('https://i.stack.imgur.com/u3xLI.png')
knitwear = io.imread('https://i.stack.imgur.com/Zj14J.png')
unknown = io.imread('https://i.stack.imgur.com/JwP3j.png')
couscous_feats = lbp_histogram(couscous)
knitwear_feats = lbp_histogram(knitwear)
unknown_feats = lbp_histogram(unknown)
Then you need to measure the similarity (or dissimilarity) between the LBP histogram of the unknown image and the histograms of the images that represent the two considered classes. Euclidean distance between histograms is a popular dissimilarity measure.
In [63]: from scipy.spatial.distance import euclidean
In [64]: euclidean(unknown_feats, couscous_feats)
Out[64]: 0.10165884804845844
In [65]: euclidean(unknown_feats, knitwear_feats)
Out[65]: 0.0887492936776889
In this example the unknown image will be classified as knitwear because the dissimilarity unknown-couscous is greater than the dissimilarity unknown-knitwear. This is in good agreement with the fact that the unknown image is actually a different type of knitwear.
import matplotlib.pyplot as plt
hmax = max([couscous_feats.max(), knitwear_feats.max(), unknown_feats.max()])
fig, ax = plt.subplots(2, 3)
ax[0, 0].imshow(couscous)
ax[0, 0].axis('off')
ax[0, 0].set_title('Cous cous')
ax[1, 0].plot(couscous_feats)
ax[1, 0].set_ylim([0, hmax])
ax[0, 1].imshow(knitwear)
ax[0, 1].axis('off')
ax[0, 1].set_title('Knitwear')
ax[1, 1].plot(knitwear_feats)
ax[1, 1].set_ylim([0, hmax])
ax[1, 1].axes.yaxis.set_ticklabels([])
ax[0, 2].imshow(unknown)
ax[0, 2].axis('off')
ax[0, 2].set_title('Unknown (knitwear)')
ax[1, 2].plot(unknown_feats)
ax[1, 1].set_ylim([0, hmax])
ax[1, 2].axes.yaxis.set_ticklabels([])
plt.show(fig)

Chromatic Aberration Estimation in python

Hi this code estimates chromatic aberration in an image by giving the center of distortion (x,y) and magnitude of distortion (alpha) between the red and green channels and also between the blue and green channels. I have an error in the WarpRegion function
File "CAfeb.py", line 217, in warpRegion
reg_w = sp.interpolate.interp2d(yrampf,xrampf,Cwarp, yramp1f, xramp1f,'cubic');
File "/usr/lib/python2.7/dist-packages/scipy/interpolate/interpolate.py", line 109, in __init__
'quintic' : 5}[kind]
TypeError: unhashable type: 'numpy.ndarray'
Below is the complete code - Any help will be greatly appreciated-Thank you. Areej
import math
from PIL import Image
import numpy as np
from decimal import Decimal
import scipy as sp
from scipy import interpolate
from scitools.std import ndgrid
from scipy import ogrid, sin, mgrid, ndimage, array
def ldimage():
#load image
global im
im = Image.open("/home/areej/Desktop/mandril_color.tif")
def analyzeCA(mode, im):
n_regions = 10;
reg_size = [300, 300];
overlap = 0.5;
levels = 9;
steps = 2;
edge_width = 10;
hist_sz = 128;
# alpha_1 and alpha_2 are assumed to be between these values
w_data = [0.9985, 1.0015];
reg_list=[]
#creating an array of pixels so that we can access them
pix=im.load()
#
#Analyze full image
if mode=='full':
print "Doing a full analysis"
# mx_shift is the third argument in 'full' mode
mx_shift = n_regions;
# [ydim,xdim,zdim]= size(im);
ydim=im.size[0]
xdim=im.size[1]
zdim=3
print "Image dimensions: [ydim, xdim, zdim]= "+str([ydim,xdim,zdim])
global alpha_mx, alpha_mn
alpha_mx = 1 + 4*mx_shift / math.sqrt( xdim*xdim + ydim*ydim );
alpha_mn = 1.0/alpha_mx;
print "alpha_mx= "+str(alpha_mx)
print "alpha_mn= "+str(alpha_mn)
#recompute alpha_1 and alpha_2 to be between
#these new values
w_data = [alpha_mn, alpha_mx];
ew = edge_width;
#take the image minus a ew-wide edge
roi = [ew+1, xdim-ew, ew+1, ydim-ew];
print "edge_width= "+str(ew)
print "roi= "+str(roi)
#Analyze blue to green chromatic aberration
bg_params = parameterSearch( im, [3, 2], roi, ew, hist_sz, w_data);
# Analyze red to green chromatic aberration
rg_params = parameterSearch( im, [1, 2], roi, ew, hist_sz, w_data );
elif mode=='reg':
print "we should do a regional analysis here"
else:
print "unsupported call"
#def estimateCARegions( im, [3, 2], reg_list, settings ):
def parameterSearch( im, colour_space, roi, ew, hist_sz, w_data):
#levels is number of iterations
levels = 8;
steps = 2;
#[ydim,xdim,zdim] = size(im);
ydim=im.size[0]
xdim=im.size[1]
zdim= 3
x_data = [1, xdim];
y_data = [1, ydim];
xlim = x_data;
ylim = y_data;
zlim = w_data;
#work out which of height and width is the bigger
dim = max(xdim,ydim)
print "The highest dimension is : "+str(dim)
#check that roi falls within expected boundries
if ((roi[0] <= ew) or (roi[1] > xdim-ew) or (roi[2] <= ew) or (roi[3] > ydim-ew)):
print "ROI is too close to image edges"
return -1 # TODO: terminate here with an error
#Get image regions
source = im.split()
Cfixed = source[2]
Cwarp = source[1]
#[ydim,xdim,zdim] = size(im);
ydimCwarp=Cwarp.size[0]
xdimCwarp=Cwarp.size[1]
print 'xdimCwarp'+str(xdimCwarp)
roi_pad = [roi[0]-ew, roi[1]+ew, roi[2]-ew, roi[3]+ew];
for levels in range(1,8):
#Guess at a center and then compute best warp
#user defined function linear_space used to generate linearly spaced vectors
x_coords = np.linspace(0,511,steps+2)
y_coords = np.linspace(0,511,steps+2)
z_coords = np.linspace(alpha_mn,alpha_mx,steps+2)
step_x=(xlim[1]-xlim[0])/(steps+1)
start_x=xlim[0]+step_x
end_x=xlim[1]-step_x+0.5
step_y=(ylim[1]-ylim[0])/(steps+1)
start_y=ylim[0]+step_y
end_y=ylim[1]-step_y+0.5
step_z=(zlim[1]-zlim[0])/(steps+1)
start_z=zlim[0]+step_z
fudge_z=step_z/2.0
end_z=zlim[1]-step_z+fudge_z
#Do not include end points in search;
centers_x, centers_y, warps= np.mgrid[start_x:end_x:step_x,start_y:end_y:step_y,start_z:end_z:step_z]
centers_x=centers_x.flatten()
centers_y=centers_y.flatten()
warps=warps.flatten()
mi = np.zeros(centers_x.size)
for k in range(0,centers_x.size):
cx = centers_x[k]
cy = centers_y[k]
wz = warps[k]
#Warp the region
temp_im = warpRegion(Cwarp, roi_pad, [cx, cy, wz])
#correlation
mi[k] = np.corrcoef(Cfixed, temp_im)
#Now pick the best quadrant
v, max_ix = math.max(mi)
ix, jx, kx = arrayInd(mi.size, max_ix);
##The coordinates of err are off by 1 from x_coords and y_coords because
##we did not include the end point
xlim = x_coords([jx, jx+2]);
ylim = y_coords([ix, ix+2]);
zlim = z_coords([kx, kx+2]);
cx = math.mean(xlim);
cy = math.mean(ylim);
wz = math.mean(zlim);
print "x= "+str(cx)
print "y= "+str(cy)
print "z= "+str(wz)
def warpRegion(Cwarp, roi_pad, (cx, cy, wz)):
#Unpack region indices
sx, ex, sy, ey = roi_pad
xramp, yramp = np.mgrid[sx:ex+1, sy:ey+1]
xrampc = xramp - cx;
yrampc = yramp - cy;
xramp1 = 1/wz*xrampc;
yramp1 = 1/wz*yrampc;
xrampf = xrampc.flatten()
yrampf = yrampc.flatten()
xramp1f = xramp1.flatten()
yramp1f = yramp1.flatten()
reg_w = sp.interpolate.interp2d(yrampf,xrampf,Cwarp, yramp1f, xramp1f,'cubic');
ldimage()
analyzeCA('full', im)
As DSM states correctly this is not the correct calling syntax for interp2d which can be viewed at scipy.interp2d. If you would read the calling syntax and then your error message again (or the module itself whichever you prefer) you would recognize that you are trying to use an array as index for a dictionary which will naturally throw an exception.
I think what you are trying to do is an interpolation of the grid given by the arrays xrampf, yrampf at the new positions xrampf1, yrampf1. The scipy documentation also gives an exact same usage example which translate as following to your code:
interp_func = sp.interpolate.interp2d(yrampf, xrampf, Cwarp, kind='cubic')
reg_w = interp_func(yramp1f, xramp1f)
I hope that was your intention to do.
Kind regards

Categories