Histogram equalization for python - python

I have this routine to do histogram equalization of a photo:
def histeq(im,nbr_bins=256):
#get image histogram
imhist,bins = histogram(im.flatten(),nbr_bins,normed=True)
cdf = imhist.cumsum() #cumulative distribution function
cdf = 255 * cdf / cdf[-1] #normalize
#use linear interpolation of cdf to find new pixel values
im2 = interp(im.flatten(),bins[:-1],cdf)
return im2.reshape(im.shape), cdf
#im = array(Image.open('AquaTermi_lowcontrast.jpg').convert('L'))
im = array(Image.open('Unequalized.jpg').convert('L'))
#Image.open('plant4.jpg').convert('L').save('inverted.jpg')
im2,cdf = histeq(im)
plt.imshow(im2)
plt.savefig("outputhisto.jpg")
When I run this with the picture from the wiki page for histogram equalization it results in this:
Instead of properly adjusting the contrast of image to something along the lines of this. What am I doing wrong?

Are you sure you're just not rendering with the wrong colormap? Try
plt.imshow(im2, cmap=plt.cm.gray)
Or
plt.imshow(im2, cmap=plt.get_cmap('gray'))

Related

Discrete Fourier Transform: Inverse of a 2D periodic signal results in doubled frequency

When converting a periodic 2D signal from image space to Fourier space and back, the reconstructed signal has twice the frequency of the original signal (see picture below). I tried to use the Discrete Fourier Transform from NumPy and OpenCV, both with the same result. The problem does not occur when using a 1D DFT and IDFT.
Do you have any ideas what is the source of this problem could be and how to fix it?
Here is a sample code in Python demonstrating the issue:
import matplotlib.pyplot as plt
import numpy as np
# image width and height
w = 320
h = 320
# frequency of cosine wave w.r.t. the image width
frequency = 1
# function to create a horizontal 2D cosine wave
def create_cos_horizontal(frequency, w, h):
img = np.zeros((h,w),np.float32)
base_period = w/(2*np.pi)
print(frequency)
for x in range(0, w):
img[0:, x] = np.cos(x*frequency/base_period)
return img
img = create_cos_horizontal(frequency, w, h)
# transform from image space to fourier space
dft = np.fft.fft2(img)
# transform back from fourier space to image space
im_back = np.fft.ifft2(dft)
# show original and reconstructed image side by side
ax1 = plt.subplot(1,2,1)
ax1.imshow(img, cmap='gray')
ax1.set_title("original signal")
ax2 = plt.subplot(1,2,2)
ax2.imshow(np.abs(im_back), cmap='gray')
ax2.set_title("signal after DFT and IDFT")
Thank you so much in advance.
The call to abs() in the second plot is rectifying the cosine, inverting the negative part of the curve. If you replace it with real(im_back) the plots match as expected.

Histogram equalization gives me all black image

I am studying image processing and I am writing a code to do a Histogram equalization, but the output always give me an all black image.
This is my code:
import numpy as np
import scipy.misc, math
from PIL import Image
img = Image.open('/home/thaidy/Desktop/ex.jpg').convert('L')
#converting to ndarray
img1 = np.asarray(img)
#converting to 1D
fl = img1.flatten()
#histogram and the bins are computed
hist, bins = np.histogram(img1,256,[0,255])
#cdf computed
cdf = hist.cumsum()
#places where cdf = 0 is ignored
#rest stored in cdf_m
cdf_m = np.ma.masked_equal(cdf,0)
#histogram eq is performed
num_cdf_m = (cdf_m - cdf_m.min())*255
den_cdf_m = (cdf_m.max()-cdf_m.min())*255
cdf_m = num_cdf_m/den_cdf_m
#the masked places are now 0
cdf = np.ma.filled(cdf_m,0).astype('uint8')
#cdf values assigned in the flattened array
im2 = cdf[fl]
#transformin in 2D
im3 = np.reshape(im2,img1.shape)
im4 = Image.fromarray(im3)
im4.save('/home/thaidy/Desktop/output.jpg')
im4.show()
The cdf needs to be normalized before the equalization.
One way to do that is to set the optional parameter density of np.histogram to True:
hist, bins = np.histogram(img1,256,[0,255],density=True)
Other way is to divide cdf after computation by total pixel count:
cdf = hist.cumsum()
cdf /= cdf[-1]
I would also change the equalization part to simply:
T = (255 * cdf).astype(np.uint8)
im2 = T[fl]
Wikipedia suggests to use this transformation formula instead:
T = (np.ceil(256 * cdf) - 1).astype(np.uint8)

Histogram equalization with SimpleITK

On this SO answer suggest me this code:
import SimpleITK as sitk
import numpy as np
# Create a noise Gaussian blob test image
img = sitk.GaussianSource(sitk.sitkFloat32, size=[240,240,48], mean=[120,120,24])
img = img + sitk.AdditiveGaussianNoise(img,10)
# Create a ramp image of the same size
h = np.arange(0.0, 255,1.0666666666, dtype='f4')
h2 = np.reshape(np.repeat(h, 240*48), (48,240,240))
himg = sitk.GetImageFromArray(h2)
print(himg.GetSize())
# Match the histogram of the Gaussian image with the ramp
result=sitk.HistogramMatching(img, himg)
# Display the 3d image
import itkwidgets
itkwidgets.view(result)
Why do I need two images to do Histogram equalization?
Because I want to do Histogram Equalization, and this is Histogram Matching. In this article explain the different.
It's a bit of a work-around to achieve histogram equalization through histogram matching.
'himg' is a ramp image, so the intensities go from 0 to 255. It has all intensities equally represented, so it's histogram is flat.
So we're matching your image's histogram with a flat histogram. The net result is histogram equalization.

How to generate separate bounding boxes for non-contiguous photo color mask areas

I want to generate bounding boxes for approximately non-contiguous areas of a photo by color mask--in this case, green bands containing vegetation--so that I can clip that area to pass to an image classification function.
This is something that can be done with geospatial rasters fairly easily using GDAL, to polygonize areas of geotiff that have similar characteristics (https://www.gdal.org/gdal_polygonize.html). But in this case, I am trying to do this to a photo. I haven't found a solution for pure rasters.
For example, take a photo like this one:
which is masked into green bands using openCV and numpy:
try:
hsv=cv.cvtColor(image,cv.COLOR_BGR2HSV)
except:
print("File may be corrupt")
return(0,0)
# Define lower and uppper limits of what we call "brown"
brown_lo=np.array([18,0,0])
brown_hi=np.array([28,255,255])
green_lo=np.array([29,0,0])
green_hi=np.array([88,255,255])
# Mask image to only select browns
mask_brown=cv.inRange(hsv,brown_lo,brown_hi)
mask_green=cv.inRange(hsv,green_lo,green_hi)
hsv[mask_brown>0]=(18,255,255)
hsv[mask_green>0]=(53,255,255)
image2=cv.cvtColor(hsv,cv.COLOR_HSV2BGR)
cv.imwrite(QUERIES + 'queries/mask.jpg', image2)
I'd like to generate boxing boxes or polygons for the areas indicated here:
Any ideas how to do that?
I tried using openCV contours and convexhull algorithms, but they don't really get me anywhere:
threshold = val
# Detect edges using Canny
canny_output = cv.Canny(src_gray, threshold, threshold * 2)
# Find contours
_, contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# Find the convex hull object for each contour
hull_list = []
for i in range(len(contours)):
hull = cv.convexHull(contours[i])
hull_list.append(hull)
# Draw contours + hull results
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
for i in range(len(contours)):
color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
cv.drawContours(drawing, contours, i, color)
cv.drawContours(drawing, hull_list, i, color)
# Show in a window
cv.imshow('Contours', drawing)
So, as you tried with contours and it didn't work, I tried to go the clustering way. I started off with K-means which is the best place to start IMO. Here is the code:
import cv2
import numpy as np
from sklearn.cluster import KMeans, MeanShift
import matplotlib.pyplot as plt
def centroid_histogram(clt):
numlabels = np.arange(0, len(np.unique(clt.labels_)) + 1)
(hist, _) = np.histogram(clt.labels_, bins=numlabels)
hist = hist.astype("float")
hist /= hist.sum()
return hist
def plot_colors(hist, centroids):
bar = np.zeros((50, 300, 3), dtype="uint8")
startX = 0
for (percent, color) in zip(hist, centroids):
endX = startX + (percent * 300)
cv2.rectangle(bar, (int(startX), 0), (int(endX), 50),
color.astype("uint8").tolist(), -1)
startX = endX
return bar
image1 = cv2.imread('mean_shift.jpg')
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
image = image1.reshape((image1.shape[0] * image1.shape[1], 3))
#clt = MeanShift(bandwidth=2, bin_seeding=True)
clt = KMeans(n_clusters=3)
clt.fit(image)
hist = centroid_histogram(clt)
bar = plot_colors(hist, clt.cluster_centers_)
plt.figure()
plt.axis("off")
plt.imshow(bar)
plt.show()
Using 3 cluster centers, I got the following result:
and using 6 cluster centers:
which basically shows you the proportion of these colors in the image.
The links that helped me do this:
https://www.pyimagesearch.com/2014/05/26/opencv-python-k-means-color-clustering/ and
https://github.com/log0/build-your-own-meanshift/blob/master/Meanshift%20Image%20Segmentation.ipynb
Now, there are couple of issues I see here:
You might not know the number of clusters in all the images. In that case, you should look into Mean-Shift. Unlike the K-Means algorithm, meanshift does not require specifying the number of clusters in advance. The number of clusters is determined by the algorithm with respect to the data.
I have used SLIC in these kind of problems. It is a subset of the K-means-based algorithms and is very efficient. You can also give this one a try since it is available in scikit, which is the goto library for machine learning in python IMO. In the same direction, you could also give this a try.
Hope I have helped you some way! Cheers!

Automatic Skew correction using opencv [duplicate]

This question already has answers here:
Python OpenCV skew correction for OCR
(3 answers)
Closed 10 months ago.
I want a way to automatically detect and correct skew of a image of a receipt,
I tried to find variance between the rows for various angles of rotation and choose the angle which has the the maximum variance.
To calculate variance I did the following:
1.For each row I calculated the sum of the pixels values and stored it in a list.
2.Found the the variance of the list using np.var(list)
src = cv.imread(f_name, cv.IMREAD_GRAYSCALE)
blurred=median = cv.medianBlur(src,9)
ret,thresh2 = cv.threshold(src,127,255,cv.THRESH_BINARY_INV)
height, width = thresh2.shape[:2]
print(height,width)
res=[-1,0]
for angle in range(0,100,10):
rotated_temp=deskew(thresh2,angle)
cv.imshow('rotated_temp',rotated_temp)
cv.waitKey(0)
height,width=rotated_temp.shape[:2]
li=[]
for i in range(height):
sum=0
for j in range(width):
sum+=rotated_temp[i][j]
li.append(sum)
curr_variance=np.var(li)
print(curr_variance,angle)
if(curr_variance>res[0]):
res[0]=curr_variance
res[1]=angle
print(res)
final_rot=deskew(src,res[1])
cv.imshow('final_rot',final_rot)
cv.waitKey(0)
However the variance for a skewed image is coming to be more than the properly aligned image,is there any way to correct this
variance for the horizontal text aligned image(required):122449908.009789
variance for the vertical text aligned image :1840071444.404522
I have tried using HoughLines However since the spacing between the text is too less vertical lines are detected,hence this also fails
Any modifications or other approaches are appreciated
Working code for skew correction
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image as im
from scipy.ndimage import interpolation as inter
input_file = r'E:\flaskV8\test1.jpg'
img = im.open(input_file)
convert to binary
wd, ht = img.size
pix = np.array(img.convert('1').getdata(), np.uint8)
bin_img = 1 - (pix.reshape((ht, wd)) / 255.0)
plt.imshow(bin_img, cmap='gray')
plt.savefig(r'E:\flaskV8\binary.png')
def find_score(arr, angle):
data = inter.rotate(arr, angle, reshape=False, order=0)
hist = np.sum(data, axis=1)
score = np.sum((hist[1:] - hist[:-1]) ** 2)
return hist, score
delta = 1
limit = 5
angles = np.arange(-limit, limit+delta, delta)
scores = []
for angle in angles:
hist, score = find_score(bin_img, angle)
scores.append(score)
best_score = max(scores)
best_angle = angles[scores.index(best_score)]
print('Best angle: {}'.format(best_angle))
data = inter.rotate(bin_img, best_angle, reshape=False, order=0)
img = im.fromarray((255 * data).astype("uint8")).convert("RGB")
img.save(r'E:\flaskV8\skew_corrected.png')

Categories