I am trying to horizontally stretch an image in a very specific way. Each x prime coordinate should follow a tangent path with respect to the original x coordinate. I believe there are two ways to do this:
Inverse the tangent function and map it normally
Map the tangent function and then inverse the mapping
Using this answer for map inversion, Im trying to figure out why the two images are not the same. I know that the first method gives me the correct image that I'm looking for, so why doesnt the second method work? Is it because of the "limited precision" that #ChristophRackwitz commented on the answer?
import cv2
import glob
import numpy as np
import math
A = -1010
B = -3.931
C = 5.258
D = 978.3
M = -193.8
N = 1740
def get_tan_func_value(x):
return A * math.tan((((x-N)/M)+B)/C) + D
def get_inverse_tan_func_value(x):
return M * (C*math.atan((x-D)/A) - B) + N
# answer from linked post
def invert_map(F, shape):
I = np.zeros_like(F)
I[:,:,1], I[:,:,0] = np.indices(shape)
P = np.copy(I)
for i in range(10):
P += I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
return P
# import image
images = glob.glob('*.jpg')
img = cv2.imread(images[0])
h, w = img.shape[:2]
map_x_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_x_inverse_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_y = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
# x tan function map
for i in range(map_x_tan.shape[0]):
map_x_tan[i,:] = [get_tan_func_value(x) for x in range(map_x_tan.shape[1])]
# x inverse tan function map
for i in range(map_x_inverse_tan.shape[0]):
map_x_inverse_tan[i,:] = [get_inverse_tan_func_value(x) for x in range(map_x_inverse_tan.shape[1])]
# default y map
for j in range(map_y.shape[1]):
map_y[:,j] = [y for y in range(map_y.shape[0])]
# convert x tan map to 2 channel (x,y) map
(xymap_tan, _) = cv2.convertMaps(map1=map_x_tan, map2=map_y, dstmap1type=cv2.CV_32FC2)
# invert the 2 channel x tan map
xymap_inverted = invert_map(xymap_tan, (h,w))
# remap and write the target image (inverse tan function with normal map)
target = cv2.remap(img, map_x_inverse_tan, map_y, cv2.INTER_LINEAR)
cv2.imwrite("target.jpg", target)
# remap and write the attempted image (normal tan function with inverted map)
attempt = cv2.remap(img, xymap_inverted, None, cv2.INTER_LINEAR)
cv2.imwrite("attempt.jpg", attempt)
Method 1: Target Image
Method 2: Attempt Image
The results show that the attempt (normal tan function with inverted map) has less stretching near the edges of the image than expected. Almost everywhere else on the images are identical except the edges. I did not post the original picture to save space.
I've played around with that invert_map procedure. It seems slightly susceptible to oscillation.
use this instead:
def invert_map(F):
(h, w) = F.shape[:2] # (h, w, 2), "xymap"
I = np.zeros_like(F)
I[:,:,1], I[:,:,0] = np.indices((h,w)) # identity map
P = np.copy(I)
for i in range(10):
correction = I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
P += correction * 0.5
return P
I simply damped the correction by 0.5, which makes the fixed point iteration tamer, converging a lot faster too.
In my experiments with your tan map, I've found that 5-10 iterations are good enough already, and there's no further progress in further iterations.
Entire notebook of my explorations: https://gist.github.com/crackwitz/67f76f8a9eff21476b080c06d20660d0
Feature request: https://github.com/opencv/opencv/issues/22120
Related
I want to loop over the pixels of a binary image in python and set the value of a pixel depending on a surrounding neighborhood of pixels. Similar to convolution but I want create a method that sets the value of the center pixel using a custom function rather than normal convolution that sets the center pixel to the arithmetic mean of the neighborhood.
In essence I would like to create a function that does the following:
def convolve(img, conv_function = lambda subImg: np.mean(subImg)):
newImage = emptyImage
for nxn_window in img:
newImage[center_pixel] = conv_function(nxn_window)
return newImage
At the moment I have a solution but it is very slow:
#B is the structuing array or convolution window/kernel
def convolve(func):
def wrapper(img, B):
#get dimensions of img
length, width = len(img), len(img[0])
#half width and length of dimensions
hw = (int)((len(B) - 1) / 2)
hh = (int)((len(B[0]) - 1) / 2)
#convert to npArray for fast operations
B = np.array(B)
#initialize empty return image
retVal = np.zeros([length, width])
#start loop over the values where the convolution window has a neighborhood
for row in range(hh, length - hh):
for pixel in range(hw, width - hw):
#window as subarray of pixels
window = [arr[pixel-hh:pixel+hh+1]
for arr in img[row-hw:row+hw+1]]
retVal[row][pixel] = func(window, B)
return retVal
return wrapper
with this function as a decorator I then do
# dilation
#convolve
def __add__(img, B):
return np.mean(np.logical_and(img, B)) > 0
# erosion
#convolve
def __sub__(img, B):
return np.mean(np.logical_and(img, B)) == 1
Is there a library that provides this type of function or is there a better way I can loop over the image?
Here's an idea: assign each pixel an array with its neighborhood and then simply apply your custom function to the extended image. It'll be fast BUT will consume more memory ( times more memory; if your B.shape is (3, 3) then you'll need 9 times more memory). Try this:
import numpy as np
def convolve2(func):
def conv(image, kernel):
""" Apply given filter on an image """
k = kernel.shape[0] # which is assumed equal to kernel.shape[1]
width = k//2 # note that width == 1 for k == 3 but also width == 1 for k == 2
a = framed(image, width) # create a frame around an image to compensate for kernel overlap when shifting
b = np.empty(image.shape + kernel.shape) # add two more dimensions for each pixel's neighbourhood
di, dj = image.shape[:2] # will be used as delta for slicing
# add the neighbourhood ('kernel size') to each pixel in preparation for the final step
# in other words: slide the image along the kernel rather than sliding the kernel along the image
for i in range(k):
for j in range(k):
b[..., i, j] = a[i:i+di, j:j+dj]
# apply the desired function
return func(b, kernel)
return conv
def framed(image, width):
a = np.zeros(np.array(image.shape) + [2 * width, 2 * width]) # only add the frame to the first two dimensions
a[width:-width, width:-width] = image # place the image centered inside the frame
return a
I've used a greyscale image 512x512 pixels and a filter 3x3 for testing:
embossing_kernel = np.array([
[-2, -1, 0],
[-1, 1, 1],
[0, 1, 2]
])
#convolve2
def filter2(img, B):
return np.sum(img * B, axis=(2,3))
#convolve2
def __add2__(img, B):
return np.mean(np.logical_and(img, B), axis=(2,3)) > 0
# image_gray is a 2D grayscale image (not color/RGB)
b = filter2(image_gray, embossing_kernel)
To compare with your convolve I've used:
#convolve
def filter(img, B):
return np.sum(img * B)
#convolve
def __add__(img, B):
return np.mean(np.logical_and(img, B)) > 0
b = filter2(image_gray, embossing_kernel)
The time for convolve was 4.3 s, for convolve2 0.05 s on my machine.
In my case the custom function needs to specify the axes over which to operate, i.e., the additional dimensions holding the neighborhood data. Perhaps the axes could be avoided too but I haven't tried.
Note: this works for 2D images (grayscale) (as you asked about binary images) but can be easily extended to 3D (color) images. In your case you could probably get rid of the frame (or fill it with zeros or ones e.g., in case of repeated application of the function).
In case memory was an issue you might want to adapt a fast implementation of convolve I've posted here: https://stackoverflow.com/a/74288118/20188124.
I am trying to write a program which fades an image in radial direction. which means as we move away from the centre of the image, the pixels fade to black. For this, I have written five different functions:
center: returns coordinate pair (center_y, center_x) of the image center.
radial_distance: returns for image with width w and height h an array with shape (h,w), where the number at index (i,j) gives the euclidean distance from the point (i,j) to the center of the image.
scale: returns a copy of the array 'a' (or image) with its elements scaled to be in the given range.
radial_mask: takes an image as a parameter and returns an array with same height and width filled with values between 0.0 and 1.0.
radial_fade: returns the image multiplied by its radial mask.
The program is:
import numpy as np
import matplotlib.pyplot as plt
def center(a):
y, x = a.shape[:2]
return ((y-1)/2,(x-1)/2) # note the order: (center_y, center_x)
def radial_distance(b):
h, w = b.shape[:2]
y, x = center(b)
o = b[:h,:w,0]
for i in range(h):
for j in range(w):
o[i,j] = np.sqrt((y-i)**2 + (x-j)**2)
return o
def scale(c, tmin=0.0, tmax=1.0):
"""Returns a copy of array 'a' with its values scaled to be in the range
[tmin,tmax]."""
mini, maxi = c.min(), c.max()
if maxi == 0:
return 0
else:
m = (tmax - tmin)/(maxi - mini)
f = tmin - m*mini
return c*m + f
def radial_mask(d):
f = radial_distance(d)
g = scale(f, tmin=0.0, tmax=1.0)
# f = g[:,:,0]
n = 1.0 - g
return n
def radial_fade(l):
f, g = l.shape[:2]
q = l[:f,:g,0]
return q * radial_mask(l)
image = plt.imread("src/painting.png")
fig, ax = plt.subplots(3)
masked = radial_mask(ima)
faded = radial_fade(ima)
ax[0].imshow(ima)
ax[1].imshow(masked)
ax[2].imshow(faded)
plt.show()
there is something wrong somewhere in the code as it does not do the expected job.
One problem is that in
o = b[:h,:w,0]
you're using the same precision as the image that may be integers (e.h. uint8).
You should use for example
o = np.zeros((h, w), np.float32)
I am trying to use OpenCV to estimate one pose of a camera relative to another, using SIFT feature tracking, FLANN matching and subsequent calculations of the fundamental and essential matrix. After decomposing the essential matrix, I check for degenerate configurations and obtain the "right" R and t.
Problem is, they never seem to be right. I am including a couple of image pairs:
Image 2 taken with 45 degree rotation along the Y axis and same position w.r.t. Image 1.
Image pair
Result
Image 2 taken from approx. couple of meters away along the negative X direction, slight displacement in the negative Y direction. Approx. 45-60 degree rotation in camera pose along Y axis.
Image pair
Result
The translation vector in the second case, seems to be overestimating the movement in Y and underestimating the movement in X. The rotation matrices when converted to Euler angles give wrong results in both the cases. This happens with a lot of other datasets as well. I have tried switching the fundamental matrix computation technique between RANSAC, LMEDS etc., and am now doing it with RANSAC and a second computation using only the inliers with the 8 point method. Changing the feature detection method does not help either. The epipolar lines seem to be proper, and the fundamental matrix satisfies x'.F.x = 0
Am I missing something fundamentally wrong here? Given the program understands the epipolar geometry properly, what could possibly be happening that results in a completely wrong pose? I am doing the check to make sure points lie in front of both cameras. Any thoughts/suggestions would be very helpful. Thanks!
EDIT: Tried the same technique with two different calibrated cameras spaced apart; and computed essential matrix as K2'.F.K1, but still the translations and rotations are still way off.
Code for reference
import cv2
import numpy as np
from matplotlib import pyplot as plt
# K2 = np.float32([[1357.3, 0, 441.413], [0, 1355.9, 259.393], [0, 0, 1]]).reshape(3,3)
# K1 = np.float32([[1345.8, 0, 394.9141], [0, 1342.9, 291.6181], [0, 0, 1]]).reshape(3,3)
# K1_inv = np.linalg.inv(K1)
# K2_inv = np.linalg.inv(K2)
K = np.float32([3541.5, 0, 2088.8, 0, 3546.9, 1161.4, 0, 0, 1]).reshape(3,3)
K_inv = np.linalg.inv(K)
def in_front_of_both_cameras(first_points, second_points, rot, trans):
# check if the point correspondences are in front of both images
rot_inv = rot
for first, second in zip(first_points, second_points):
first_z = np.dot(rot[0, :] - second[0]*rot[2, :], trans) / np.dot(rot[0, :] - second[0]*rot[2, :], second)
first_3d_point = np.array([first[0] * first_z, second[0] * first_z, first_z])
second_3d_point = np.dot(rot.T, first_3d_point) - np.dot(rot.T, trans)
if first_3d_point[2] < 0 or second_3d_point[2] < 0:
return False
return True
def drawlines(img1,img2,lines,pts1,pts2):
''' img1 - image on which we draw the epilines for the points in img1
lines - corresponding epilines '''
pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
r,c = img1.shape
img1 = cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR)
img2 = cv2.cvtColor(img2,cv2.COLOR_GRAY2BGR)
for r,pt1,pt2 in zip(lines,pts1,pts2):
color = tuple(np.random.randint(0,255,3).tolist())
x0,y0 = map(int, [0, -r[2]/r[1] ])
x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ])
cv2.line(img1, (x0,y0), (x1,y1), color,1)
cv2.circle(img1,tuple(pt1), 10, color, -1)
cv2.circle(img2,tuple(pt2), 10,color,-1)
return img1,img2
img1 = cv2.imread('C:\\Users\\Sai\\Desktop\\room1.jpg', 0)
img2 = cv2.imread('C:\\Users\\Sai\\Desktop\\room0.jpg', 0)
img1 = cv2.resize(img1, (0,0), fx=0.5, fy=0.5)
img2 = cv2.resize(img2, (0,0), fx=0.5, fy=0.5)
sift = cv2.SIFT()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50) # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)
good = []
pts1 = []
pts2 = []
# ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
if m.distance < 0.7*n.distance:
good.append(m)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].pt)
pts2 = np.float32(pts2)
pts1 = np.float32(pts1)
F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_RANSAC)
# Selecting only the inliers
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]
F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_8POINT)
print "Fundamental matrix is"
print
print F
pt1 = np.array([[pts1[0][0]], [pts1[0][1]], [1]])
pt2 = np.array([[pts2[0][0], pts2[0][1], 1]])
print "Fundamental matrix error check: %f"%np.dot(np.dot(pt2,F),pt1)
print " "
# drawing lines on left image
lines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)
lines1 = lines1.reshape(-1,3)
img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)
# drawing lines on right image
lines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F)
lines2 = lines2.reshape(-1,3)
img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)
E = K.T.dot(F).dot(K)
print "The essential matrix is"
print E
print
U, S, Vt = np.linalg.svd(E)
W = np.array([0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]).reshape(3, 3)
first_inliers = []
second_inliers = []
for i in range(len(pts1)):
# normalize and homogenize the image coordinates
first_inliers.append(K_inv.dot([pts1[i][0], pts1[i][1], 1.0]))
second_inliers.append(K_inv.dot([pts2[i][0], pts2[i][1], 1.0]))
# Determine the correct choice of second camera matrix
# only in one of the four configurations will all the points be in front of both cameras
# First choice: R = U * Wt * Vt, T = +u_3 (See Hartley Zisserman 9.19)
R = U.dot(W).dot(Vt)
T = U[:, 2]
if not in_front_of_both_cameras(first_inliers, second_inliers, R, T):
# Second choice: R = U * W * Vt, T = -u_3
T = - U[:, 2]
if not in_front_of_both_cameras(first_inliers, second_inliers, R, T):
# Third choice: R = U * Wt * Vt, T = u_3
R = U.dot(W.T).dot(Vt)
T = U[:, 2]
if not in_front_of_both_cameras(first_inliers, second_inliers, R, T):
# Fourth choice: R = U * Wt * Vt, T = -u_3
T = - U[:, 2]
# Computing Euler angles
thetaX = np.arctan2(R[1][2], R[2][2])
c2 = np.sqrt((R[0][0]*R[0][0] + R[0][1]*R[0][1]))
thetaY = np.arctan2(-R[0][2], c2)
s1 = np.sin(thetaX)
c1 = np.cos(thetaX)
thetaZ = np.arctan2((s1*R[2][0] - c1*R[1][0]), (c1*R[1][1] - s1*R[2][1]))
print "Pitch: %f, Yaw: %f, Roll: %f"%(thetaX*180/3.1415, thetaY*180/3.1415, thetaZ*180/3.1415)
print "Rotation matrix:"
print R
print
print "Translation vector:"
print T
plt.subplot(121),plt.imshow(img5)
plt.subplot(122),plt.imshow(img3)
plt.show()
There are many things which can lead to inaccurate estimation of camera pose from point correspondences. Some factors you have to consider:-
(*) 8 point method minimizes algebraic error ( x'.F.x = 0). It is usually better to find a solution which minimizes a meaningful geometric error. For example, you can use re-projection error in your RANSAC implementation.
(*) The linear algorithm which solves for fundamental matrix from 8 points is sensitive to noise. Sub-pixel accurate point matching, proper data normalization and accurate camera calibration are all important for better results.
(*) Feature point localization and matching lead to noisy point matches, hence the solution you get by solving the algebraic equation x'Fx should really be used as an initial estimate and further steps such as parameter optimization need to be applied to refine the solution.
(*) Some two view camera configurations can lead to an ambiguous solution hence further methods (such as third view disambiguation) are needed for reliable results.
How do you get K, the internal parameters of the camera? It seems to me that the computation of fundamental matrix is correct, because the matches points lie on the epipolar lines. But if the matrix K is inaccurate, you may get a wrong essential matrix and thus the wrong R and t.
In the book"Programming Computer Vision with Python", we need to check the E's rank by using such code:
Computes the second camera matrix (assuming P1 = [I 0])
from an essential matrix. Output is a list of four
possible camera matrices.
make sure E is rank 2
U,S,V = np.linalg.svd(E)
if np.linalg.det(np.dot(U,V))<0:
V = -V
E = np.dot(U,np.dot(np.diag([1.0,1.0,0.0]),V))
I am not sure if this also can improve the performance.
Please let me know.
Expanding on the second point by #Sammy,
(*) The linear algorithm which solves for fundamental matrix from 8
points is sensitive to noise. Sub-pixel accurate point matching,
proper data normalization and accurate camera calibration are all
important for better results.
I would suggest to compute essential matrix directly using the findEssentialMat by openCV instead of findFundamentalMat because the former has less degrees of freedom and can hence be numerically more stable.
The new openCV versions support giving two different camera matrices and distortion coefficients for findEssentialMat as well.
Goal: To input an image (2d numpy array) and a window size, and output the same array with the local maxima remaining, but 0 elsewhere.
What I am struggling with: I think I made a stupid mistake in my code, maybe a few typos in my loop but I am not sure (the local maxima are only on the left side of the image, which is not true). As I note below I would also welcome any easy tricks with OpenCV or numpy to make this solution shorter.
similar questions: Finding Local Maxima in an Image
and Find local maxima in grayscale image using OpenCV
Mine is different because: I am looking to surpress all but local maxima. I don't have to use my code below, I haven't been able to find a built in function of opencv or numpy to do what I need to (harris corners in cv will implicitly do this as one of the steps, but I need something to perform the sole operation I need). I read a little bit on dilate and wasn't sure if that would be useful here as well.
What I have tried so far.
def nonMaximalSupress(image,NHoodSize):
#For
for x in range(0,image.shape[0]-1):
if x+NHoodSize[0]<image.shape[0]:
#while we can still take a square
#print "AHH ", image.shape
startWindow=0
for y in range(startWindow,image.shape[1]-NHoodSize[1]):
#try:
if np.sum(image[x:x+NHoodSize[0]][y:y+NHoodSize[1]])==0:
localMax=0
else:
localMax = np.amax(image[x:x+NHoodSize[0]][y:y+NHoodSize[1]])
#except ValueError:
#localMax=0
#print "local max is ", localMax
maxCoord=np.unravel_index(np.argmax((image[x:x+NHoodSize[0],y:y+NHoodSize[1]])),
image.shape)+np.array((x,y))
#print "X is %r, Y is %r, max coord is %r \n y+nhood is %r" %(x,y,maxCoord,y+NHoodSize[1])
#suppress everything
image[x:x+NHoodSize[0]][y:y+NHoodSize[1]]=0
#reset only the max
#print maxCoord
if localMax > 0:
print localMax
print "max coord is ", maxCoord[0], maxCoord[1]
image[maxCoord[0]][maxCoord[1]]=localMax
#increment y
x+=NHoodSize[0]
return image
How about something like this:
# Use the max filter to make a mask
roi = 3
size = 2 * roi + 1
image_max = ndimage.maximum_filter(image, size=size, mode='constant')
mask = (image == image_max)
image *= mask
# Remove the image borders
image[:roi] = 0
image[-roi:] = 0
image[:, :roi] = 0
image[:, -roi:] = 0
# Optionally find peaks above some threshold
image_t = (image > peak_threshold) * 1
# get coordinates of peaks
f = np.transpose(image_t.nonzero())
This may not be entirely correct, but it works better on a small test case
def nonMaximalSupress1(image,NHoodSize):
#
dX, dY = NHoodSize
M, N = image.shape
for x in range(0,M-dX+1):
for y in range(0,N-dY+1):
window = image[x:x+dX, y:y+dY]
if np.sum(window)==0:
localMax=0
else:
localMax = np.amax(window)
maxCoord=np.unravel_index(np.argmax(window), window.shape) + np.array((x,y))
#suppress everything
image[x:x+dX, y:y+dY]=0
#reset only the max
if localMax > 0:
print localMax
print "max coord is ", maxCoord
image[tuple(maxCoord)] = localMax
return image
I've used local variables to make things easier to read, and tweaked the loop ranges. But the big change is in how I index image. Especially when indexing with slices, you must use one set of brackets.
image[x:x+dX, y:y+dY] is the correct way to select a window, not image[x:x+dX][y:y+dY].
It can be cleaned up a bit more by modifying the window. Since it is a view, changing it changes image.
def nonMaximalSupress2(image,NHoodSize):
#
dX, dY = NHoodSize
M, N = image.shape
for x in range(0,M-dX+1):
for y in range(0,N-dY+1):
window = image[x:x+dX, y:y+dY]
if np.sum(window)==0:
localMax=0
else:
localMax = np.amax(window)
maxCoord = np.argmax(window)
# zero all but the localMax in the window
window[:] = 0
window.flat[maxCoord] = localMax
return image
A different approach using peak_local_max from skimage.feature.peak and center_of_mass from scipy.ndimage.measurements for localizing the centers of the peaks if a peak may consist of multiple pixels with the same pixel intensity:
from skimage.feature.peak import peak_local_max
from scipy.ndimage.measurements import center_of_mass
from scipy.ndimage import label
from scipy.ndimage.morphology import generate_binary_structure
footprint = generate_binary_structure(3, 3)
peaks = peak_local_max(img, indices=False, footprint=footprint)
lbl, num_features = label(maxima)
centers = center_of_mass(maxima, lbl, range(1, num_features + 1))
values = img[tuple(np.int0(np.transpose(centers)))]
I want to transform in Python 2d arrays/images to polar, process then, and subsequently transform them back to cartesian. The following is the result from ImajeJ Polar Transformer plugin (used on the concentric circles of the sample code):
The number and dims of the images is quite large so I was checking whether openCV has a fast and simple way to do this.
I read about cv. CartToPolar and PolarToCart but I failed to use it. I understand better the LogPolar where the input and output are arrays, and where you can set the center, interpolation,and inversion (i.e CV_WARP_INVERSE_MAP). Is there a way to use CartToPolar/PolarToCart in an similar fashion?
import numpy as np
import cv
#sample 2D array that featues concentric circles
circlesArr = np.ndarray((512,512),dtype=np.float32)
for i in range(10,600,10): cv.Circle(circlesArr,(256,256),i-10,np.random.randint(60,500),thickness=4)
#logpolar
lp = np.ndarray((512,512),dtype=np.float32)
cv.LogPolar(circlesArr,lp,(256,256),100,cv.CV_WARP_FILL_OUTLIERS)
#logpolar Inverse
lpinv = np.ndarray((512,512),dtype=np.float32)
cv.LogPolar(lp,lpinv,(256,256),100, cv.CV_WARP_INVERSE_MAP + cv.CV_WARP_FILL_OUTLIERS)
#display images
from scipy.misc import toimage
toimage(lp, mode="L").show()
toimage(lpinv, mode="L").show()
This is for a tomography (CT) workflow where rings artifacts can be filtered out easier if they appear as lines.
Latest versions of opencv supports a function cv2.linearPolar.
This may be another solution that does not involve the use of opencv:
def polar2cart(r, theta, center):
x = r * np.cos(theta) + center[0]
y = r * np.sin(theta) + center[1]
return x, y
def img2polar(img, center, final_radius, initial_radius = None, phase_width = 3000):
if initial_radius is None:
initial_radius = 0
theta , R = np.meshgrid(np.linspace(0, 2*np.pi, phase_width),
np.arange(initial_radius, final_radius))
Xcart, Ycart = polar2cart(R, theta, center)
Xcart = Xcart.astype(int)
Ycart = Ycart.astype(int)
if img.ndim ==3:
polar_img = img[Ycart,Xcart,:]
polar_img = np.reshape(polar_img,(final_radius-initial_radius,phase_width,3))
else:
polar_img = img[Ycart,Xcart]
polar_img = np.reshape(polar_img,(final_radius-initial_radius,phase_width))
return polar_img
the CV source code mentions a LinearPolar. it doesn't seem to be documented, but appears to be similar to LogPolar. have you tried that?
Here's an example of the log-polar transform implemented using SciPy:
https://github.com/stefanv/supreme/blob/master/supreme/transform/transform.py#L51
Given that this is only a coordinate transformation, it should be easier to adapt to your problem than the OpenCV version.
Update: I'm looking for something similar and now you can do it round trip with cv2 as follow:
import cv2
import math
# img -> your image
h, w = img.shape[:2]
img_center = (h/2, w/2)
img_radius = math.hypot(h/2, w/2)
cart_2_polar_flag = cv2.WARP_FILL_OUTLIERS
img_forth = cv2.linearPolar(img, img_center, img_radius, cart_2_polar_flag)
polar_2_cart_flag = cv2.WARP_INVERSE_MAP
img_back = cv2.linearPolar(img_forth, img_center, img_radius, polar_2_cart_flag)