I am trying to implement adaptive histogram equalization in python. I take an image and split it into smaller regions and then apply the traditional histogram equalization to it. I then combine the smaller images into one and obtain a final resultant image. The final image appears to be very blocky in nature and has different contrast levels for each individual region. Is there a way I could maintain a uniform contrast for each individual image so that it looks like a single image instead of smaller images stitched together.
import cv2
import numpy as np
from matplotlib import pyplot as plt
from scipy.misc import imsave
from scipy import ndimage
from scipy import misc
import scipy.misc
import scipy
import image_slicer
from image_slicer import join
from PIL import Image
img = 'watch.png'
num_tiles = 25
tiles = image_slicer.slice(img, num_tiles)
for tile in tiles:
img = scipy.misc.imread(tile.filename)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf *hist.max()/ cdf.max()
plt.plot(cdf_normalized, color = 'g')
plt.hist(img.flatten(),256,[0,256], color = 'g')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
cdf_m = np.ma.masked_equal(cdf,0)
cdf_o = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
cdf = np.ma.filled(cdf_o,0).astype('uint8')
img3 = cdf[img]
cv2.imwrite(tile.filename,img3)
tile.image = Image.open(tile.filename
image = join(tiles)
image.save('watch-join.png')
I reviewed the actual algorithm and came up with the following implementation. I am sure there is a better way to do this. Any suggestions are appreciated.
import numpy as np
import cv2
img = cv2.imread('watch.png',0)
print img
img_size=img.shape
print img_size
img_mod = np.zeros((600, 800))
for i in range(0,img_size[0]-30):
for j in range(0,img_size[1]-30):
kernel = img[i:i+30,j:j+30]
for k in range(0,30):
for l in range(0,30):
element = kernel[k,l]
rank = 0
for m in range(0,30):
for n in range(0,30):
if(kernel[k,l]>kernel[m,n]):
rank = rank + 1
img_mod[i,j] = ((rank * 255 )/900)
im = np.array(img_mod, dtype = np.uint8)
cv2.imwrite('target.png',im)
Related
I cannot figure out how to draw a pixel in ipycanvas. I am drawing rectangles instead of pixels and this makes drawing very slow.
Drawing a rectangle using:
canvas.fill_rect
Code to display image in ipycanvas :
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import ipycanvas
from ipycanvas import Canvas
import requests
from io import BytesIO
url = r"https://wallpapercave.com/dwp1x/wp1816238.jpg"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
array = img.tobytes()
canvas = Canvas(width=img.width, height=img.height)
with ipycanvas.hold_canvas():
for i in range(int(len(array)/3)):
r = array[i * 3 + 0] # red
g = array[i * 3 + 1] # green
b = array[i * 3 + 2] # blue
canvas.fill_style = f"#{r:02x}{g:02x}{b:02x}" # setting color
canvas.fill_rect(i%img.width, int(i/img.width), 1, 1) # drawing rectangle
canvas
Output:
I am drawing image pixel by pixel because I want to apply filters in images.
How to draw pixels in ipycanvas?
Not sure if this will help but given you're talking about filtering I'd assume you mean things like convolutions. Numpy and Scipy help a lot and provide various ways of applying these and work well with images from Pillow.
For example:
import requests
from io import BytesIO
from PIL import Image
import numpy as np
from scipy import signal
image_req = requests.get("https://wallpapercave.com/dwp1x/wp1816238.jpg")
image_req.raise_for_status()
image = Image.open(BytesIO(image_req.content))
# create gaussian glur of a given standard deviation
sd = 3
filt = np.outer(*2*[signal.windows.gaussian(int(sd*5)|1, sd)])
filt /= filt.sum()
# interpret image as 3d array
arr = np.array(image)
# apply it to each channel independently, this loop runs in ~0.1 seconds
for chan in range(3):
arr[:,:,chan] = signal.oaconvolve(arr[:,:,chan], filt, mode='same')
# array back into image for display in notebook
Image.fromarray(arr)
This produces an image like:
I'm a total newbie to Python.
What's the simplest algorithm by which I can zoom an image by a factor of 3?
I don't want to use the already made zoom functions available.
The task is moderately cumbersome, so I have shown a simple way to implement row zooming. You can similarly modify the indexes to implement column indexing for new_image as well.
# loading the image
from PIL import Image
import numpy as np
image = np.asarray( Image.open("img.jpg") )
import matplotlib.pyplot as plt
# create new image of correct size
m = len(image[0])
n = len(image)
factor = 3
new_image = np.zeros((factor*(n-1) + 1,factor*(m-1) + 1,3), dtype=int)
# implement row zooming
for i in range(n):
row = image[i]
for k in range(len(row)-1):
new_image[i][k*factor], new_image[i][(k+1)*factor] = row[k], row[k+1]
for mode in range(3):
# need mode as three colour channels in RGB
lo = int(min(row[k][mode], row[k+1][mode]))
hi = int(max(row[k][mode], row[k+1][mode]))
diff = int((hi-lo)//factor)
for x in range(factor-1):
new_image[i][k*factor+1+x][mode] = lo + (x*diff)
Let us say you have a .png image named lenna.png on you file system. You can load it and convert it to a numpy array like this
from PIL import Image
import numpy as np
image = np.asarray( Image.open("lenna.png") )
import matplotlib.pyplot as plt
plt.imshow(image)
plt.show()
Numpy offers a simple wayto increase the pixel resolution like this:
# Simply increase the resolution of the image by repeating the pixels
zoom_factor = 3
for i in range(2):
image = np.repeat(image, zoom_factor, axis=i)
If we plot the image it now simply has more pixels in each dimension:
You could then display only part of the image by cropping your new high resolution image like this
# Focus on any paricular region by croping it out
image = image[700:1000, 700:1000]
plt.imshow(image)
plt.show()
The result looks like this
Cheers!
I get an error when I'm trying to find the 2 circle inner one for pupil and outer one for iris but unable to do so. Firstly I reshape the image then then finding bandwidth to know kernel value then I do segmentation in using mean shift algo after then i marked cluster region in red colour:
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk,Image
import numpy as np
import scipy.ndimage as snd
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets.samples_generator import make_blobs
from itertools import cycle
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.pylab as pylab
import cv2
pylab.rcParams['figure.figsize'] = 16, 12
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(initialdir="F:\mean shift\images",title="Open File",filetypes= (("all files","*.*"),("jpg files","*.jpg")))
image = Image.open(file_path)
image = np.array(image)
original_shape = image.shape
# Flatten image.
X = np.reshape(image, [-1, 3])
plt.imshow(image)
bandwidth = estimate_bandwidth(X, quantile=0.1, n_samples=100)
print(bandwidth)
ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
ms.fit(X)
labels = ms.labels_
print(labels.shape)
cluster_centers = ms.cluster_centers_
print(cluster_centers.shape)
labels_unique = np.unique(labels)
n_clusters_ = len(labels_unique)
print("number of estimated clusters : %d" % n_clusters_)
segmented_image = np.reshape(labels, original_shape[:2]) # Just take size, ignore RGB channels.
plt.figure(2)
plt.imshow(segmented_image)
plt.axis('off')
masked_image = np.copy(image)
# convert to the shape of a vector of pixel values
masked_image = masked_image.reshape((-1, 3))
# color (i.e cluster) to disable
cluster = 2
masked_image[labels == cluster] = [255, 0, 0]
# convert back to original shape
masked_image = masked_image.reshape(image.shape)
# show the image
plt.imshow(masked_image)
nemo = cv2.cvtColor(masked_image, cv2.COLOR_BGR2RGB)
cv2.imwrite("mean_shift.bmp",nemo)
plt.show()
I have a segmented image using SLIC from scipy and for every superpixel i get an image where only that superpixel is colored and the rest of the image is black. I want to iterate ONLY on the colored pixels from that one superpixel.
I have tried using for loop like this:
for i in range(0,mask.shape[0]):
for j in range(0,mask.shape[1]):
x,y,z = each_segment[i][j] #gets the pixel RGB value
unique_pixel_array = [x,y,z] #creates a vector that holds those values for each pixel
if (unique_pixel_array != [0,0,0]):
print(unique_pixel_array)
This method is working however it is very inefficient considerin it is iterating over the entire image, and if i have a big image it will take a very long time to process for every superpixel.
Is there a faster and more efficient way to do this?
I will attach the whole code below , maybe you will get a better sense of the whole thing.
from skimage.segmentation import slic
from skimage.segmentation import mark_boundaries
from skimage.util import img_as_float
import matplotlib.pyplot as plt
import numpy as np
import cv2
img = cv2.imread("image.png")
segments = slic(img_as_float(img),n_segments= 7 ,slic_zero=True,sigma =5)
fig = plt.figure("Superpixels -- %d segments" % (22))
ax = fig.add_subplot(1, 1, 1)
ax.imshow(mark_boundaries(img, segments))
plt.axis("off")
plt.show()
for (sp,segVal) in enumerate (np.unique(segments)):
mask = np.zeros(img.shape[:2],dtype = "uint8")
mask[segments == segVal] = 255
each_segment = cv2.bitwise_and(img,img,mask=mask)
for i in range(0,mask.shape[0]):
for j in range(0,mask.shape[1]):
x,y,z = each_segment[i][j]
unique_pixel_array = [x,y,z]
print(unique_pixel_array)
cv2.imshow("Mask", mask)
cv2.imshow("Applied", cv2.bitwise_and(img, img, mask = mask))
cv2.waitKey(0)
I'm trying to add a random noise from uniform distribution between min pixel
value and 0.1 times the maximum pixel value to each pixel for each channel of original image.
Here's my code so far:
[in]:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Read image with cv2
image = cv2.imread('example_image.jpg' , 1)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Display image
imshow(image_rgb)
# R,G,B channel separation
R, G, B = cv2.split(image_rgb)
# Creating Noise
noise_R = np.random.uniform(R.min(),R.max()*0.1, R.size)
noise_R.shape = (256,256)
noise_G = np.random.uniform(B.min(),B.max()*0.1, G.size)
noise_G.shape = (256,256)
noise_B = np.random.uniform(G.min(), G.max()*0.1, B.size)
noise_B.shape = (256,256)
# Adding noise to each channel separately
R = R + noise_R
G = G + noise_G
B = B + noise_B
rgb_noise = R + G + B
noisy_image = image + rgb_noise
[out]:
ValueError: operands could not be broadcast together with shapes (256,256,3) (256,256)
I'm getting an ValueError that the array shapes for rgb_noise and image are not equal. I've tried changing the shape of rgb_noise to that of image's but the I get a size error. How to fix it ? Is there any better method ?
Your solution is a bit verbose, and could be made more compact.
However, the reason why you do not get white-ish noise is that you compute your red channel differently from the other two.
Changing this:
noise_R = np.random.uniform(R_min,R_max*0.3, image_G.size)
to this:
noise_R = np.random.uniform(R_min,R_max*0.1, image_R.size)
You can be simplistic and add the noise by only the numpy array.
import numpy
import matplotlib.pyplot as plt
import cv2
Look, plotting the image will only work good with jupyter notebooks.
Do cv2.imshow() for other IDEs.
1) Have your Image
img = cv2.imread('path').astype(np.uint0)
2) Make a random noise
r, g, b = img.shape
noise = np.random.randint(0,255,r*g*b).reshape(r,g,b)
3) Blend them
image_with_noise = cv2.addWeighted(img,0.5,noise,0.5,0)
You can adjust the value of alpha and beta values.
There you have a noisy image!