Apply complex transformation to an image using matplotlib and numpy - python

Hi I am trying to apply the mobius transformation to an image using matplotlib. This is python code to do this.
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from numpy import *
img = mpimg.imread('test.jpg') # load an image
zi = [766j, 512+766j, 256+192j]
wi = [738j, 512+496j, 256+173j]
r = ones((600,700,3),dtype=uint8)*255 # empty-white image
for i in range(img.shape[1]):
for j in range(img.shape[0]):
z = complex(i,j)
qf = ((wi[0] * (-wi[1] * (zi[0]-zi[1]) * (z-zi[2]) + wi[2] * (z-zi[1]) * (zi[0]-zi[2])) - wi[1]*wi[2]*(z-zi[0]) * (zi[1]-zi[2])))
qs = (wi[2]*(zi[0]-zi[1])*(z-zi[2])-wi[1]*(z-zi[1])*(zi[0]-zi[2])+wi[0]*(z-zi[0])*(zi[1]-zi[2]))
w = qf/qs
r[int(imag(w)),int(real(w)),:] = img[j,i,:]
plt.subplot(121)
plt.imshow(img,origin='lower',aspect='auto')
plt.subplot(122)
plt.imshow(r,origin='lower',aspect='auto')
plt.show()
if I run this code, I get the following result.
If you see the right side, the size is changed. I want to know the way to fit the result image in the box. The way I did is I hard code the result image size and run the code. However, since the mobius transformation expands and shrink the image, sometimes I get very small image and sometimes I get very big image. Anyone can solve this problem??Thanks!

You can do the following to find the x limits and y limits of your transformed image:
plt.gca().set_aspect('equal')
i, j = np.where(np.all(r!=255, axis=2))
xlimits = j.min(), j.max()
ylimits = i.min(), i.max()
plt.xlim(xlimits)
plt.ylim(ylimits)
the set_aspect() was added to show the image in its original aspect ratio. numpy.where() will find the row and column indices where the image is not white (255, 255, 255), it is taking the minimum and maximum indices to set the new limits.

Related

Simplest algorithm for zooming an image in Python by K factor

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!

How to represent a binary image as a graph with the axis being height and width dimensions and the data being the pixels

I am trying to use Python along with opencv, numpy and matplotlib to do some computer vision for a robot which will use a railing to navigate. I am currently extremely stuck have run out of places to look. My current code is:
import cv2
import numpy as np
import matplotlib.pyplot as plt
image = cv2.imread('railings.jpg')
railing_image = np.copy(image)
resized_image = cv2.resize(railing_image,(881,565))
gray = cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
canny = cv2.Canny(blur, 85, 255)
cv2.imshow('test',canny)
image_array = np.array(canny)
ncols, nrows = image_array.shape
count = 0
scan = np.array
for x in range(0,image_array.shape[1]):
for y in range(0,image_array.shape[0]):
if image_array[y, x] == 0:
count += 1
scan = [scan, count]
print(scan)
plt.plot([0, count])
plt.axis([0, nrows, 0, ncols])
plt.show()
cv2.waitKey(0)
I am using a canny image which is stored in an array of 1's and 0's, the image I need represented is
The final result should look something like the following image.
I've tried using a histogram function but I've only managed to get that to output essentially a count of the number of times a 1 or 0 appears.
If anyone could help me or point me in the right direction that would produce a graph that represents the image pixels within a graph of height and width dimensions.
Thank you
I'm not sure how general this is but you could just use numpy argmax to get location of the maximum (like this) in your case. You should avoid loops as this will be very slow, better to use numpy functions. I've imported your image and used the cutoff criterion that 200 or more in the yellow channel is railing,
import cv2
import numpy as np
import matplotlib.pyplot as plt
#This loads the canny image you uploaded
image = cv2.imread('uojHJ.jpg')
#Trim off the top taskbar
trimimage = image[100:, :,0]
#Use argmax with 200 cutoff colour in one channel
maxindex = np.argmax(trimimage[:,:]>200, axis=0)
#Plot graph
plt.plot(trimimage.shape[0] - maxindex)
plt.show()
Where this looks as follows:

Checking if image is mostly black and white or color

I am trying to classify if an image mostly contains black and white or color, to be precise it is a photo of a photocopy(think xerox),which is mostly black and white.The image is NOT single channel image, but a 3 channel image.
I just want to know if there are any obvious ways to solve this that im missing.
for now im trying to plot histograms and may be do a pixel count, but that does not look very promising,any suggestions on this would be really helpful.
Thanks in advance.
I am unsure of the exact use case, but having experienced similar issues I used this rather helpful article.
https://www.alanzucconi.com/2015/05/24/how-to-find-the-main-colours-in-an-image/
The GitHub containing the full code is found here: https://gist.github.com/jayapal/077f63f3163abbfb3c50c7d209524cc6
If this is for your own visual the histogram should be enough, if you are attempting to automate however, it may be helpful to round the color values up or down, this would provide information on if the image is darker or lighter than a certain value.
What are you using this code for on a larger perspective? Maybe that will help provide more adequate information
Edit: The code above also provides the ability to define a region of the image, hopefully this will make your selection more accurate
Adding code directly
from sklearn.cluster import KMeans
from sklearn import metrics
import cv2
import numpy as np
import cv2
image = cv2.imread("red.png")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Resize it
h, w, _ = image.shape
w_new = int(100 * w / max(w, h) )
h_new = int(100 * h / max(w, h) )
image = cv2.resize(image, (w_new, h_new));
# Reshape the image to be a list of pixels
image_array = image.reshape((image.shape[0] * image.shape[1], 3))
print image_array
# Clusters the pixels
clt = KMeans(n_clusters = 3)
clt.fit(image_array)
def centroid_histogram(clt):
# grab the number of different clusters and create a histogram
# based on the number of pixels assigned to each cluster
numLabels = np.arange(0, len(np.unique(clt.labels_)) + 1)
(hist, _) = np.histogram(clt.labels_, bins = numLabels)
# normalize the histogram, such that it sums to one
hist = hist.astype("float")
hist /= hist.sum()
# return the histogram
return hist
# Finds how many pixels are in each cluster
hist = centroid_histogram(clt)
# Sort the clusters according to how many pixel they have
zipped = zip (hist, clt.cluster_centers_)
zipped.sort(reverse=True, key=lambda x : x[0])
hist, clt.cluster_centers = zip(*zipped)
# By Adrian Rosebrock
import numpy as np
import cv2
bestSilhouette = -1
bestClusters = 0;
for clusters in range(2, 10):
# Cluster colours
clt = KMeans(n_clusters = clusters)
clt.fit(image_array)
# Validate clustering result
silhouette = metrics.silhouette_score(image_array, clt.labels_,
metric='euclidean')
# Find the best one
if silhouette > bestSilhouette:
bestSilhouette = silhouette;
bestClusters = clusters;
print bestSilhouette
print bestClusters

K-Means: why are my results "muddier" than a website performing a similar function?

I'm trying to extract the top 3 most-prominent colors in an image. I'm no expert on computer vision or image analysis, so I'm relying on the kindness of strangers and borrowed code. I've got the following Python implementation (abridged to remove database storage of results, etc., so it won't be directly runnable)
from sklearn.cluster import KMeans
import cv2
import numpy as np
import colorsys
(...)
def centroid_histogram(clusters):
numLabels = np.arange(0, len(np.unique(clusters.labels_)) + 1)
(hist, _) = np.histogram(clusters.labels_, bins = numLabels)
# normalize the histogram, such that it sums to one
hist = hist.astype("float")
hist /= hist.sum()
# return the histogram
return hist
(...)
# load the image and convert it to RGB
image = cv2.imread(image)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# break down the image into one long list of pixels
image = image.reshape((image.shape[0] * image.shape[1], 3))
# perform KMeans clustering
clusters = KMeans(n_clusters=3)
clusters.fit(image)
histogram = centroid_histogram(clusters)
for (percent, color) in zip(histogram, clusters.cluster_centers_):
r = int(color[0])
g = int(color[1])
b = int(color[2])
h, s, v = colorsys.rgb_to_hsv(r/255.0, g/255.0, b/255.0)
h = h * 360.0
I've run the same image through this code, and through an image-processing website, and the results are different. I understand we don't know the exact algorithm or parameters the site is using. I get that. But the site is getting the results I want, and my code is outputting a darker, more muddied palette. Mine is the bars on the right.
My code also generates the palette with a percentage, which is why the palette blocks are unequal. (Is that the issue?)
Thanks.

Python Image distortion

I'm trying to apply a ripple effect to an image in python.
I found Pillow's im.transform(im.size, Image.MESH,.... is it possible?
Maybe I have to load the image with numpy and apply the algorithm.
I also found this: http://www.pygame.org/project-Water+Ripples-1239-.html
another way manually but I don't know any algorithm. this is my start. it doesn't do anything...
#!/usr/bin/env python3
from PIL import Image
import sys
import numpy
import math
im = Image.open(sys.argv[1])
im.show()
matrix = numpy.asarray(im)
width = im.size[0]
height = im.size[1]
amplitude = ? # parameters
frequency = ?
matrix_dest = numpy.zeros((im.size[0],im.size[1],3))
for x in range(0, width):
for y in range(0, height):
pass # ç_ç
im2 = Image.fromarray(numpy.uint8(matrix_dest))
im2.show()
EDIT:
I'd really like to keep this structure (using pillow. I already use extensivly in my project and if I can I wouldn't add any other dependency) and not including scipi or matplotlib..
With the following code I have the distortion I wanted, but colors are screwed up.
Maybe I have to apply the distortion to R,G,B planes and then compose the result in one image.
Or palettize the image and then apply the original palette.
(Btw the image would be used as a texture to display moving water in a 3D environment.)
im = Image.open(sys.argv[1])
im.show()
m = numpy.asarray(im)
m2 = numpy.zeros((im.size[0],im.size[1],3))
width = im.size[0]
height = im.size[1]
A = m.shape[0] / 3.0
w = 1.0 / m.shape[1]
shift = lambda x: A * numpy.sin(2.0*numpy.pi*x * w)
for i in range(m.shape[0]):
print(int(shift(i)))
m2[:,i] = numpy.roll(m[:,i], int(shift(i)))
im2 = Image.fromarray(numpy.uint8(m2))
im2.show()
You could use np.roll to rotate each row or column according to some sine function.
from scipy.misc import lena
import numpy as np
import matplotlib.pyplot as plt
img = lena()
A = img.shape[0] / 3.0
w = 2.0 / img.shape[1]
shift = lambda x: A * np.sin(2.0*np.pi*x * w)
for i in range(img.shape[0]):
img[:,i] = np.roll(img[:,i], int(shift(i)))
plt.imshow(img, cmap=plt.cm.gray)
plt.show()
Why don't you try something like:
# import scipy
# import numpy as np
for x in range(cols):
column = im[:,x]
y = np.floor(sin(x)*10)+10
kernel = np.zeros((20,1))
kernel[y] = 1
scipy.ndimage.filters.convolve(col,kernel,'nearest')
I threw this together just right now, so you'll need to tweak it a bit. The frequency of the sin is definitely too high, check here. But I think overall this should work.
I had a similar problem where sometimes the colors appear to be messed up (getting some weird red lines) after applying the sin when attempting the proposed solutions here. Couldn't resolve it.
I understand the original poster doesn't want more dependencies if possible, but for those unrestricted, here is a an alternative sample solution provided by scikit docs:
http://scikit-image.org/docs/dev/auto_examples/transform/plot_piecewise_affine.html#sphx-glr-auto-examples-transform-plot-piecewise-affine-py
Copying from the doc above:
import numpy as np
import matplotlib.pyplot as plt
from skimage.transform import PiecewiseAffineTransform, warp
from skimage import data
image = data.astronaut()
rows, cols = image.shape[0], image.shape[1]
src_cols = np.linspace(0, cols, 20)
src_rows = np.linspace(0, rows, 10)
src_rows, src_cols = np.meshgrid(src_rows, src_cols)
src = np.dstack([src_cols.flat, src_rows.flat])[0]
# add sinusoidal oscillation to row coordinates
dst_rows = src[:, 1] - np.sin(np.linspace(0, 3 * np.pi, src.shape[0])) * 50
dst_cols = src[:, 0]
dst_rows *= 1.5
dst_rows -= 1.5 * 50
dst = np.vstack([dst_cols, dst_rows]).T
tform = PiecewiseAffineTransform()
tform.estimate(src, dst)
out_rows = image.shape[0] - 1.5 * 50
out_cols = cols
out = warp(image, tform, output_shape=(out_rows, out_cols))
fig, ax = plt.subplots()
ax.imshow(out)
ax.plot(tform.inverse(src)[:, 0], tform.inverse(src)[:, 1], '.b')
ax.axis((0, out_cols, out_rows, 0))
plt.show()

Categories

Resources