I try to detect shift in image with fft2. I find a code to do that but I dont really understand why the shift value is incorrect.
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
from scipy import fftpack
import matplotlib.image as mpimg
import scipy.signal
def cross_image(im1, im2):
im1_gray = np.sum(im1.astype('float'), axis=2)
im2_gray = np.sum(im2.astype('float'), axis=2)
im1_gray -= np.mean(im1_gray)
im2_gray -= np.mean(im2_gray)
image1FFT = np.conjugate( np.fft.fft2(im1_gray) )
image2FFT = np.conjugate( np.fft.fft2(im2_gray) )
imageCCor = np.real( np.fft.ifft2( (image1FFT*image2FFT) ) )
return imageCCor
# return scipy.signal.fftconvolve(im1_gray, im2_gray[::-1,::-1], mode='same')
def plot_spectrum(im_fft):
from matplotlib.colors import LogNorm
# A logarithmic colormap
plt.imshow(np.abs(im_fft), norm=LogNorm(vmin=5))
plt.colorbar()
im = mpimg.imread('/home/xavier/0001.jpg')
plt.figure()
plt.imshow(im, plt.cm.gray)
plt.title('Original image')
xPixelShift = 25
yPixelShift = -7
row, col,channels = im.shape
relativeSize = np.floor( min( 1-abs(xPixelShift)/col, 1-abs(yPixelShift)/row ) * 10 ) /10
x0 = int( (col - col*relativeSize)/2 )
x1 = col-x0 + 1
y0 = int( (row - row*relativeSize)/2 )
y1 = row-y0 + 1
# not shifted ROI
image1 = im[y0:y1,x0:x1].copy()
plt.figure()
plt.imshow(image1, plt.cm.gray)
# shifted ROI
image2 = im[y0+yPixelShift:y1+yPixelShift,x0+xPixelShift:x1+xPixelShift].copy()
plt.figure()
plt.imshow(image2, plt.cm.gray)
imageCCorShift = cross_image(image1,image2)
plt.figure()
plt.imshow(imageCCorShift)
row, col,channels = image1.shape
yShift, xShift = np.unravel_index( np.argmax(imageCCorShift), (row,col) )
print("shift of image1 in x-direction [pixel]: " + str(xShift))
print("shift of image1 in y-direction [pixel]: " + str(yShift))
When I use that commented code, it works:
scipy.signal.fftconvolve(im1_gray, im2_gray[::-1,::-1], mode='same')
But when I use my code, it does work
image1FFT = np.conjugate( np.fft.fft2(im1_gray) )
image2FFT = np.conjugate( np.fft.fft2(im2_gray) )
imageCCor = np.real( np.fft.ifft2( (image1FFT*image2FFT) ) )
return imageCCor
What the scipy function fftconvolve do more than my code?
Related
I'm trying to add an alpha channel to an .exr file(RGB) using OpenEXR python library or OpenCV, couldn't figure out how. Here's where I am.
import OpenEXR,Imath,array,os
import numpy as np
def write_exr_alpha(dir,output_dir):
z_file = OpenEXR.InputFile(dir)
FLOAT = Imath.PixelType(Imath.PixelType.FLOAT)
(z,_,_) = [array.array('f', z_file.channel(Chan, FLOAT)).tolist() for Chan in ("R", "G", "B") ]
dw = z_file.header()['dataWindow']
sz = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
os.makedirs(os.path.dirname(output_dir), exist_ok=True)
#get alpha values
As = np.ones(np.array(z).shape)
pos = np.where(np.array(z) > 100)[0]
As[pos] = 0
zs = array.array('f',z).tobytes()
out = OpenEXR.OutputFile(output_dir, OpenEXR.Header(sz[0], sz[1]))
# write to .exr with alpha channel
out.writePixels({'R' : zs, 'G' : zs, 'B' : zs ,'A': As})
and OpenCV
import os,cv2
import numpy as np
def write_exr_alpha(dir,output_dir):
os.makedirs(os.path.dirname(output_dir), exist_ok=True)
image=cv2.imread(dir,cv2.IMREAD_ANYCOLOR | cv2.IMREAD_ANYDEPTH)
normal_b, normal_g, normal_r = cv2.split(image)
As = np.ones(normal_b.shape, dtype=normal_b.dtype)
pos = np.where(np.array(image[:,:,0]) > 100)
for i in range(0,len(pos)):
As[pos[0][i]][pos[1][i]]=0
image_out = cv2.merge((normal_r, normal_g, normal_b, As))
cv2.imwrite(output_dir,image_out)
Edit: A test sample.
There I created an animation using celluloid library. This animation shows the point which move along the elliptical line. The problem is that, when I run this code, everything is working in sublime text, but when I use Jupyter, it outputs error. How can I solve this problem? You can see my code in the answers.
import math
import numpy as np
import matplotlib.pyplot as plt
from celluloid import Camera
import copy
img = np.ones((90,60,3))
fig = plt.figure(figsize=(10,8), dpi = 100, facecolor='w')
camera = Camera(fig)
quarterCircleOne = []
quarterCircleTwoHalfOne = []
quarterCircleTwoHalfTwo = []
quarterCircleThree = []
quarterCircleFourHalfOne = []
quarterCircleFourHalfTwo = []
xc = 45
yc = 30
rx = 25
ry = 15
def GenerateElipse(xc,yc,rx,ry):
img[xc, yc] = (255,0,0)
img[xc+1, yc] = (255,0,0)
img[xc-1, yc] = (255,0,0)
img[xc, yc+1] = (255,0,0)
img[xc, yc-1] = (255,0,0)
xk = 0
yk = ry
pk = ry**2 -rx**2 * ry+1/4* rx**2
while ry**2 * xk < rx**2 *yk:
if pk>0:
xk = xk+1
yk = yk-1
pk = pk + 2 * ry**2 *xk + ry**2 - 2* rx**2 *yk
else:
xk = xk+1
pk=pk + 2* ry**2 * xk + ry**2
img[xc+xk, yc+yk] = (0,0,0)
img[xc+xk, yc-yk] = (0,0,0)
img[xc-xk, yc+yk] = (0,0,0)
img[xc-xk, yc-yk] = (0,0,0)
quarterCircleOne.append([xc-xk, yc-yk])
quarterCircleTwoHalfTwo.append([xc-xk, yc+yk])
quarterCircleThree.append([xc+xk, yc+yk])
quarterCircleFourHalfOne.append([xc+xk, yc-yk])
pk = ry**2 * (xk-1/2)**2 + rx**2 * (yk-1)**2 - rx**2 * ry**2
while yk>0:
if pk>0:
yk = yk-1
pk=pk -2 * rx**2 * yk + rx**2
else:
xk = xk+1
yk = yk-1
pk=pk - 2 * rx**2 * yk + 2* ry**2 *xk + rx**2
img[xc+xk, yc+yk] = (0,0,0)
img[xc+xk, yc-yk] = (0,0,0)
img[xc-xk, yc+yk] = (0,0,0)
img[xc-xk, yc-yk] = (0,0,0)
quarterCircleOne.append([xc-xk, yc-yk])
quarterCircleTwoHalfOne.append([xc-xk, yc+yk])
quarterCircleThree.append([xc+xk, yc+yk])
quarterCircleFourHalfTwo.append([xc+xk, yc-yk])
GenerateElipse(45, 30, 25, 15)
def DrawElipse(speed, img):
sortedQuarterCircleOne = sorted(quarterCircleOne,key=lambda x: x[1])
sortedQuarterCircleTwoHalfOne =
sorted(quarterCircleTwoHalfOne,key=lambda x: x[1])
sortedQuarterCircleTwoHalfTwo =
sorted(quarterCircleTwoHalfTwo,key=lambda x: x[0])
sortedQuarterCircleThree = sorted(quarterCircleThree,key=lambda x: x[1],
reverse=True)
sortedQuarterCircleFourHalfTwo =
sorted(quarterCircleFourHalfTwo,key=lambda x: x[1], reverse=True)
sortedQuarterCircleFourHalfOne =
sorted(quarterCircleFourHalfOne,key=lambda x: x[0], reverse=True)
circle = [sortedQuarterCircleOne, sortedQuarterCircleTwoHalfOne,
sortedQuarterCircleTwoHalfTwo, sortedQuarterCircleThree,
sortedQuarterCircleFourHalfTwo, sortedQuarterCircleFourHalfOne]
i = 0
for x in range(0, 6):
for coordinates in circle[x]:
freshImage = copy.deepcopy(img)
freshImage[coordinates[0], coordinates[1]] = (0,0,0)
freshImage[coordinates[0], coordinates[1]+1] = (255,0,0)
freshImage[coordinates[0], coordinates[1]-1] = (255,0,0)
freshImage[coordinates[0]+1, coordinates[1]-1] = (0,0,255)
freshImage[coordinates[0]+1, coordinates[1]+1] = (0,0,255)
freshImage[coordinates[0]+1, coordinates[1]] = (255,0,0)
freshImage[coordinates[0]-1, coordinates[1]-1] = (0,0,255)
freshImage[coordinates[0]-1, coordinates[1]+1] = (0,0,255)
freshImage[coordinates[0]-1, coordinates[1]] = (255,0,0)
if i % speed == 0:
plt.imshow(freshImage)
camera.snap()
i = i + 1
DrawElipse(1, img);
animation = camera.animate()
plt.show()
The "error" Clipping input data to the valid range... stems from the data you give to plt.imshow. It hints that you should supply data within [0..255] for integers e.g. np.uint8 and [0..1] for floating point types e.g. np.float32. Check your input data, and clip it beforehand if necessary, or cast it to the right datatype, e.g. plt.imshow(np.uint8(freshImage)).
That error is not what makes the animation not show up; it's an error provided by matplotlib's imshow. It will actually do its best to render the image despite the problem in the input range.
To see the animation use this in the end (in a Jupyter notebook, remember to from IPython.display import HTML):
animation = camera.animate()
HTML(animation.to_html5_video())
As per the documentation for Celluliod: https://github.com/jwkvam/celluloid
I would like to fill a polygon using basemap/shapefile data, but only a certain %. For example, in the example below, we fill based on the values, but let's say I wanted to fill a % of the polygon based on these values (code from here):
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
import numpy as np
fig= plt.figure()
ax= fig.add_subplot(111)
m=Basemap(projection='cyl',llcrnrlat=34.5,llcrnrlon=19,
urcrnrlat=42,urcrnrlon=28.5,resolution='h')
m.drawmapboundary(fill_color='aqua')
m.fillcontinents(color='w',lake_color='aqua')
m.drawcoastlines()
m.readshapefile('data/nomoi/nomoi','nomoi')
dict1={14464: 1.16, 14465: 1.35, 14466: 1.28, 14467: 1.69, 14468: 1.81, 14418: 1.38}
colvals = dict1.values()
cmap=plt.cm.RdYlBu
norm=plt.Normalize(min(colvals),max(colvals))
patches = []
for info, shape in zip(m.nomoi_info, m.nomoi):
if info['ID_2'] in list(dict1.keys()):
color=cmap(norm(dict1[info['ID_2']]))
patches.append( Polygon(np.array(shape), True, color=color) )
pc = PatchCollection(patches, match_original=True, edgecolor='k', linewidths=1., zorder=2)
ax.add_collection(pc)
#colorbar
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array(colvals)
fig.colorbar(sm, ax=ax)
plt.show()
Thank you.
import math
from shapely.geometry import Polygon as shpoly
#shapefile of main massachusetts shape
iowpoly = state_shapes['Massachusetts'][32]
def return_xy(coords):
return [np.asarray([i[0] for i in coords]), np.asarray([i[1] for i in coords])]
def return_area(coords):
x, y = return_xy(coords)
return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
def return_bounding_box(coords):
x, y = return_xy(coords)
return [[min(x), min(y)], [max(x), max(y)]]
def split_x_wise(bbox, weights, split = 2):
lleft = bbox[0]
uright = bbox[1]
dx = abs(uright[0] - lleft[0])
weights = np.cumsum(sorted(weights, reverse=True))
xcoords = [lleft[0]+weights[x-1]*dx for x in range(1, split)]
return xcoords
def generate_splits_by_area(coords, bbox, weights, tolerance = 0.03, div = 100):
xareasplits = {}
weights = np.cumsum(sorted(weights, reverse=True))[:-1]
lleft = bbox[0]
uright = bbox[1]
dx = abs(uright[0] - lleft[0])
xsplit = [lleft[0]+(dx/div)*x for x in range(1, div)]
for w in weights:
xareasplits[str(w)] = None
mainarea = shpoly(coords).area
for i, s in enumerate(xsplit):
poly = []
if i == 0:
continue
for ip, p in enumerate(coords):
if p[0] < s:
poly.append(p)
shpl = shpoly(poly).area
frac = shpl/mainarea
for w in weights:
if abs(w-frac) <= tolerance:
if xareasplits[str(w)] == None:
xareasplits[str(w)] = s
return list(xareasplits.values())
def return_split(coords, weights, split = 2, by_area = False, tolerance = 0.03, div = 100):
polys = {}
for x in range(0, split):
polys[str(x+1)] = {'points':[], 'maxit' : None}
bbox = return_bounding_box(coords)
if not by_area:
xsplit = split_x_wise(bbox, weights, split)
#test = generate_splits_by_area(coords, bbox, weights, tolerance=tolerance, div=div)
else:
xsplit = generate_splits_by_area(coords, bbox, weights, tolerance=tolerance, div=div)
xsplit.append(bbox[0][0])
xsplit.append(bbox[1][0])
xsplit = sorted(xsplit)
#print(xsplit)
#print(test)
for ip, p in enumerate(coords):
for i, splt in enumerate(xsplit):
if i > 0:
if (p[0] > xsplit[i-1]) & (p[0] < splt):
if len(polys[str(i)]['points']) == 0:
polys[str(i)]['points'].append(coords[ip-1])
polys[str(i)]['points'].append(p)
polys[str(i)]['maxit'] = ip
for poly, data in polys.items():
tmaxit = data['maxit']+1
if tmaxit >= len(coords):
data['points'].append(coords[0])
else:
data['points'].append(coords[tmaxit])
return polys
#return [p for p in coords if p[0] > xsplit[0]]
#bboxiowa = return_bounding_box(iowpoly)
splitpoly = return_split(iowpoly, weights = [0.2780539772727273, 0.1953716856060606, 0.19513494318181818, 0.18329782196969696, 0.14814157196969696],by_area = True,split = 5)
for k, v in splitpoly.items():
print (k, len(v['points']))
print (v['maxit'])
test = shpoly(splitpoly["1"]['points'])
test
I managed to write my own code to split and fill shapel polygons from shapefiles. The above code example splits the Massachusetts shapefile into 5 segments, weighted according to weights and by area.
The first 2 parts of the split look like this:
My timing shows that k-means consistently loses out on timing, compared to a mixture model, initialized using k-means.
What's the explanation for this? Is the GMM using a different k-means algorithm? Am I misunderstanding how it works? Does it use a differently sized dataset (smaller than I'm drawing from?).
import sklearn.cluster
import sklearn.mixture
import numpy as np
import time
import matplotlib.pyplot as plt
k = 3
N = 100
def clust():
m = sklearn.cluster.KMeans(n_clusters = k)
m.fit(X.reshape(-1, 1))
return m.cluster_centers_
def fit():
m = sklearn.mixture.GaussianMixture(n_components = k, init_params = "kmeans")
m.fit(X.reshape(-1, 1))
return m.means_
duration_clust = []
duration_fit = []
ctrs_clust = []
ctrs_fit = []
for i in range(N):
_1 = np.random.normal(0.25, 0.15, 50)
_2 = np.random.normal(0.50, 0.15, 50)
_3 = np.random.normal(0.75, 0.15, 50)
X = np.concatenate((_1, _2, _3)).reshape(-1, 1)
ts = time.time()
c = clust()
te = time.time()
time_clust = (te - ts) * 1e3
ts = time.time()
f = fit()
te = time.time()
time_fit = (te - ts) * 1e3
duration_clust.append(time_clust)
duration_fit.append(time_fit)
ctrs_clust.append(c)
ctrs_fit.append(f)
bins0 = np.arange(0, 20, 1)
bins1 = np.linspace(0,1,30)
fig, ax = plt.subplots(nrows = 2)
ax[0].hist(duration_clust, label = "Kmeans", bins = bins0, alpha = 0.5)
ax[0].hist(duration_fit, label = "GMM with Kmeans", bins = bins0, alpha = 0.5)
ax[0].set_xlabel("duration (ms)")
ax[0].legend(loc = "upper right")
ax[1].hist(np.ravel(ctrs_clust), label = "Kmeans centers", bins = bins1, alpha = 0.5)
ax[1].hist(np.ravel(ctrs_fit), label = "GMM centers", bins = bins1, alpha = 0.5)
ax[1].set_xlabel("Center location")
ax[1].axvline([0.25], label = "Truth", color = "black")
ax[1].axvline([0.50], color = "black")
ax[1].axvline([0.75], color = "black")
ax[1].legend(loc = "upper right")
plt.tight_layout()
plt.show()
I'm trying to replicate the behavior of MATLAB imagesc() call in matplotlib - specifically:
- for very large images, decimate the image
- as the user zooms in, show the image with less decimation.
I've written a class that will do it, but my solution seems overly complex. Does anybody know a better way?
Thanks in advance,
Brian
answer by OP, edited out of the question:
Here's my solution:
The basic idea is:
Catch a xlim_changed or ylim_changed event
Calculate a desired stride based on image size and desired num pixels
Draw
https://github.com/flailingsquirrel/cmake_scipy_ctypes_example/blob/master/src/python/FastImshow.py
#!/usr/bin/env python
'''
Fast Plotter for Large Images - Resamples Images to a target resolution on each zoom.
Example::
sz = (10000,20000) # rows, cols
buf = np.arange(sz[0]*sz[1]).reshape(sz)
extent = (100,150,1000,2000)
fig = plt.figure()
ax = fig.add_subplot(111)
im = FastImshow(buf,extent,ax)
im.show()
plt.show()
'''
import numpy as np
import matplotlib.pyplot as plt
class FastImshow:
'''
Fast plotter for large image buffers
Example::
sz = (10000,20000) # rows, cols
buf = np.arange(sz[0]*sz[1]).reshape(sz)
extent = (100,150,1000,2000)
fig = plt.figure()
ax = fig.add_subplot(111)
im = FastImshow(buf,extent,ax)
im.show()
plt.show()
'''
def __init__(self,buf,ax,extent=None,tgt_res=512):
'''
[in] img buffer
[in] extent
[in] axis to plot on
[in] tgt_res(default=512) : target resolution
'''
self.buf = buf
self.sz = self.buf.shape
self.tgt_res = tgt_res
self.ax = ax
# Members required to account for mapping extent to buf coordinates
if extent:
self.extent = extent
else:
self.extent = [ 0, self.sz[1], 0, self.sz[0] ]
self.startx = self.extent[0]
self.starty = self.extent[2]
self.dx = self.sz[1] / (self.extent[1] - self.startx ) # extent dx
self.dy = self.sz[0] / (self.extent[3] - self.starty ) # extent dy
# end __init__
def get_strides( self,xstart=0, xend=-1, ystart=0, yend=-1, tgt_res=512 ):
'''
Get sampling strides for a given bounding region. If none is provided,
use the full buffer size
'''
# size = (rows,columns)
if xend == -1:
xend = self.sz[1]
if yend == -1:
yend = self.sz[0]
if (xend-xstart) <= self.tgt_res:
stridex = 1
else:
stridex = max(int((xend - xstart) / self.tgt_res),1)
if (yend-ystart) <= self.tgt_res:
stridey = 1
else:
stridey = max(int((yend - ystart) / self.tgt_res),1)
return stridex,stridey
# end get_strides
def ax_update(self, ax):
'''
Event handler for re-plotting on zoom
- gets bounds in img extent coordinates
- converts to buffer coordinates
- calculates appropriate strides
- sets new data in the axis
'''
ax.set_autoscale_on(False) # Otherwise, infinite loop
# Get the range for the new area
xstart, ystart, xdelta, ydelta = ax.viewLim.bounds
xend = xstart + xdelta
yend = ystart + ydelta
xbin_start = int(self.dx * ( xstart - self.startx ))
xbin_end = int(self.dx * ( xend - self.startx ))
ybin_start = int(self.dy * ( ystart - self.starty ))
ybin_end = int(self.dy * ( yend - self.starty ))
# Update the image object with our new data and extent
im = ax.images[-1]
stridex,stridey = self.get_strides( xbin_start,xbin_end,ybin_start,ybin_end)
im.set_data( self.buf[ybin_start:ybin_end:stridey,xbin_start:xbin_end:stridex] )
im.set_extent((xstart, xend, ystart, yend))
ax.figure.canvas.draw_idle()
# end ax_update
def show(self):
'''
Initial plotter for buffer
'''
stridex, stridey = self.get_strides()
self.ax.imshow( buf[::stridex,::stridey],extent=self.extent,origin='lower',aspect='auto' )
self.ax.figure.canvas.draw_idle()
self.ax.callbacks.connect('xlim_changed', self.ax_update)
self.ax.callbacks.connect('ylim_changed', self.ax_update)
# end show
# end ImgDisplay
if __name__=="__main__":
sz = (10000,20000) # rows, cols
buf = np.arange(sz[0]*sz[1]).reshape(sz)
extent = (100,150,1000,2000)
fig = plt.figure()
ax = fig.add_subplot(111)
im = FastImshow(buf,ax,extent=extent,tgt_res=1024)
im.show()
plt.show()