I am having trouble correctly rotating an image in an affine transformation. Currently the below below is what i'm using:
rotation_matrix = np.array([[np.cos(rotation_angle),
-np.sin(rotation_angle),0],
[np.sin(rotation_angle),
np.cos(rotation_angle),0],
[0,0,1]])
If I set the angle to anything greater than approximately 50 degrees I get an entirely black image without anything in it (I set the new image as entirely black, which indicates that none of the translated pixels are falling within the range of the new image). If i rotate less than 50 degrees I get some portion of the image, but it doesn't look correctly rotated from what I can tell. Also, origin 0,0 is in the top left corner. I want part of the image to be obscured if it is rotated outside of the bounds of the original image.
Prior to applying the the rotation, I am taking the inverse via
#get inverse of transform matrix
inverse_transform_matrix = np.linalg.inv(multiplied_matrices)
Where rotation occurs:
def Apply_Matrix_To_Image(matrix_to_apply, image_map):
#takes an image and matrices and applies it.
x_min = 0
y_min = 0
x_max = image_map.shape[0]
y_max = image_map.shape[1]
new_image_map = np.zeros((x_max, y_max), dtype=int)
for y_counter in range(0, y_max):
for x_counter in range(0, x_max):
curr_pixel = [x_counter,y_counter,1]
curr_pixel = np.dot(matrix_to_apply, curr_pixel)
print(curr_pixel)
if curr_pixel[0] > x_max - 1 or curr_pixel[1] > y_max - 1 or x_min > curr_pixel[0] or y_min > curr_pixel[1]:
next
else:
new_image_map[x_counter][y_counter] = image_map[int(curr_pixel[0])][int(curr_pixel[1])]
return new_image_map
# tested with python3
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
def GetRotateMatrixWithCenter(x, y, angle):
# https://math.stackexchange.com/questions/2093314
move_matrix = np.array(
[
[1, 0, x],
[0, 1, y],
[0, 0, 1]
])
rotation_matrix = np.array(
[
[np.cos(angle), -np.sin(angle), 0],
[np.sin(angle), np.cos(angle), 0],
[0, 0, 1]
])
back_matrix = np.array(
[
[1, 0, -x],
[0, 1, -y],
[0, 0, 1]
])
r = np.dot(move_matrix, rotation_matrix)
return np.dot(r, back_matrix)
def Apply_Matrix_To_Image(matrix_to_apply, image_map):
#takes an image and matrices and applies it.
x_min = 0
y_min = 0
x_max = image_map.shape[0]
y_max = image_map.shape[1]
new_image_map = np.zeros((x_max, y_max), dtype=int)
for y_counter in range(0, y_max):
for x_counter in range(0, x_max):
curr_pixel = [x_counter,y_counter,1]
curr_pixel = np.dot(matrix_to_apply, curr_pixel)
# print(curr_pixel)
if curr_pixel[0] > x_max - 1 or curr_pixel[1] > y_max - 1 or x_min > curr_pixel[0] or y_min > curr_pixel[1]:
next
else:
new_image_map[x_counter][y_counter] = image_map[int(curr_pixel[0])][int(curr_pixel[1])]
return new_image_map
# convert image to grayscale
img = Image.open('small.png').convert("L")
img = np.asarray(img)
image_width = img.shape[0]
image_height = img.shape[1]
plt.subplot(1,2,1)
plt.title('Origin image')
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
plt.subplot(1,2,2)
plt.title('Transformed image')
alpha = 0
while True:
rotation_angle = 0 + alpha
alpha = alpha + 1 # increate 1 degree
rotation_angle = np.deg2rad(rotation_angle) # degree to radian
rotation_matrix = GetRotateMatrixWithCenter(image_width / 2, image_height / 2, rotation_angle)
roteated = Apply_Matrix_To_Image(rotation_matrix, img)
plt.imshow(roteated, cmap='gray', vmin=0, vmax=255)
plt.pause(0.001)
plt.show()
Updated Contents:
transform Degree to radian with np.deg2rad()
Realtime draw rotated image with matplotlib for debug
With https://math.stackexchange.com/questions/2093314, roate with image center
**Running screen: **
Related
I'm trying to calculate 3d points with graycode structured light. My setup has one camera and one projector. I project gray code and capture them. However when i was try calculate 3d points, i'm getting extra 3d point as you can see in image. Here is my code to decode graycode images and triangulate them.
x_index = stripe.decode(x_captures, x_nega_captures) # thresh=60) #
y_index = stripe.decode(y_captures, y_nega_captures) # thresh=60) #
# Merge X and Y correspondences
img_correspondence = cv2.merge([0.0 * np.zeros_like(x_index),
x_index / CAMERA_RESOLUTION[0],
y_index / CAMERA_RESOLUTION[1]])
# Clip correspondences to 8b
img_correspondence = np.clip(img_correspondence * 255, 0, 255)
img_correspondence = img_correspondence.astype(np.uint8)
# Mask correspondences with projectable area to eliminate sprurious points
img_correspondence[~is_projectable] = 0
# Visualize correspondences
plt.figure()
plt.imshow(img_correspondence)
plt.axis('off')
plt.title('Correspondence Map')
#plt.show()
# Construct 2D correspondences
# x_p and y_p are the 2D coordinates in projector image space
x_p = np.expand_dims(x_index[is_projectable].flatten(), -1)
y_p = np.expand_dims(y_index[is_projectable].flatten(), -1)
projector_points = np.hstack((x_p, y_p))
# x and y are the 2D coordinates in camera image space
xx, yy = np.meshgrid(np.arange(CAMERA_RESOLUTION[0]), np.arange(CAMERA_RESOLUTION[1]))
x = np.expand_dims(xx[is_projectable].flatten(), -1)
y = np.expand_dims(yy[is_projectable].flatten(), -1)
camera_points = np.hstack((x, y))
camera_points = np.expand_dims(camera_points, 1).astype(np.float32)
projector_points = np.expand_dims(projector_points, 1).astype(np.float32)
camera_norm_1 = cv2.undistortPoints(camera_points, camera_K, camera_d, P=camera_K)
proj_norm_1 = cv2.undistortPoints(projector_points, projector_K, projector_d, P=camera_K)
P0 = np.dot(camera_K, np.array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0]]))
P1 = np.concatenate((camera_K # projector_R, camera_K # projector_t), axis=1)
triangulated_points = cv2.triangulatePoints(P0, P1, camera_norm_1, proj_norm_1).T
points_3d = (triangulated_points[:, :3] / triangulated_points[:, -1:])
I'm doing a "Circle View"(Bird View) system for a long truck. Due to the fact that the car is long on the sides of one camera is not enough. I decided to try to put two cameras and sew them, but there is a drawback. the code that I use stitches the image is not quite even and the joint is visible. How can I change the code to make the joint was less visible and sewn better? A chessboard with a size of 4x6. It stands in the middle of two video cameras. Maybe there's a way to stitch on a checkerboard?
Here's my stitching result:
Here are two images to be stitched together:
1 image:
2 image:
The code I have now:
import cv2 as cv
import numpy as np
def FindHomography(Matches, BaseImage_kp, SecImage_kp):
# If less than 4 matches found, exit the code.
if len(Matches) < 4:
print("\nNot enough matches found between the images.\n")
exit(0)
# Storing coordinates of points corresponding to the matches found in both the images
BaseImage_pts = []
SecImage_pts = []
for Match in Matches:
BaseImage_pts.append(BaseImage_kp[Match[0].queryIdx].pt)
SecImage_pts.append(SecImage_kp[Match[0].trainIdx].pt)
# Changing the datatype to "float32" for finding homography
BaseImage_pts = np.float32(BaseImage_pts)
SecImage_pts = np.float32(SecImage_pts)
# Finding the homography matrix(transformation matrix).
(HomographyMatrix, Status) = cv.findHomography(SecImage_pts, BaseImage_pts, cv.RANSAC, 4.0)
return HomographyMatrix, Status
def GetNewFrameSizeAndMatrix(HomographyMatrix, Sec_ImageShape, Base_ImageShape):
# Reading the size of the image
(Height, Width) = Sec_ImageShape
# Taking the matrix of initial coordinates of the corners of the secondary image
# Stored in the following format: [[x1, x2, x3, x4], [y1, y2, y3, y4], [1, 1, 1, 1]]
# Where (xi, yi) is the coordinate of the i th corner of the image.
InitialMatrix = np.array([[0, Width - 1, Width - 1, 0],
[0, 0, Height - 1, Height - 1],
[1, 1, 1, 1]])
# Finding the final coordinates of the corners of the image after transformation.
# NOTE: Here, the coordinates of the corners of the frame may go out of the
# frame(negative values). We will correct this afterwards by updating the
# homography matrix accordingly.
FinalMatrix = np.dot(HomographyMatrix, InitialMatrix)
[x, y, c] = FinalMatrix
x = np.divide(x, c)
y = np.divide(y, c)
# Finding the dimentions of the stitched image frame and the "Correction" factor
min_x, max_x = int(round(min(x))), int(round(max(x)))
min_y, max_y = int(round(min(y))), int(round(max(y)))
New_Width = max_x
New_Height = max_y
Correction = [0, 0]
if min_x < 0:
New_Width -= min_x
Correction[0] = abs(min_x)
if min_y < 0:
New_Height -= min_y
Correction[1] = abs(min_y)
# Again correcting New_Width and New_Height
# Helpful when secondary image is overlaped on the left hand side of the Base image.
if New_Width < Base_ImageShape[1] + Correction[0]:
New_Width = Base_ImageShape[1] + Correction[0]
if New_Height < Base_ImageShape[0] + Correction[1]:
New_Height = Base_ImageShape[0] + Correction[1]
# Finding the coordinates of the corners of the image if they all were within the frame.
x = np.add(x, Correction[0])
y = np.add(y, Correction[1])
OldInitialPoints = np.float32([[0, 0],
[Width - 1, 0],
[Width - 1, Height - 1],
[0, Height - 1]])
NewFinalPonts = np.float32(np.array([x, y]).transpose())
# Updating the homography matrix. Done so that now the secondary image completely
# lies inside the frame
HomographyMatrix = cv.getPerspectiveTransform(OldInitialPoints, NewFinalPonts)
return [New_Height, New_Width], Correction, HomographyMatrix
ratio_thresh = 0.9
image1 = cv.imread(filename='/home/msi-user/PycharmProjects/170Camera/1_camera.jpg')
image2 = cv.imread(filename='/home/msi-user/PycharmProjects/170Camera/2_camera.jpg')
# -----------------------------------------KAZE--------------------------------#
AKAZE = cv.KAZE_create() # KAZE, AKAZE, ORB, BRISK, xfeatures2d.SURF
keypoints1, descriptors1 = AKAZE.detectAndCompute(image1, None)
keypoints2, descriptors2 = AKAZE.detectAndCompute(image2, None)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
descriptors1 = np.float32(descriptors1)
descriptors2 = np.float32(descriptors2)
FLANN = cv.FlannBasedMatcher(indexParams=index_params,
searchParams=search_params)
matches = FLANN.knnMatch(queryDescriptors=descriptors1,
trainDescriptors=descriptors2,
k=2)
good_matches = []
t = []
for m, n in matches:
if m.distance < ratio_thresh * n.distance:
good_matches.append([m])
t.append(m)
output = cv.drawMatches(img1=image1,
keypoints1=keypoints1,
img2=image2,
keypoints2=keypoints2,
matches1to2=t,
outImg=None,
flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv.namedWindow("drawMatches.jpg", cv.WINDOW_NORMAL)
cv.imshow("drawMatches.jpg", output)
# ----------------------------------FindHomography-------------------------------------------#
HomographyMatrix, Status = FindHomography(good_matches, keypoints1, keypoints2)
BaseImage = image1
SecImage = image2
NewFrameSize, Correction, HomographyMatrix = GetNewFrameSizeAndMatrix(HomographyMatrix, SecImage.shape[:2],
BaseImage.shape[:2])
StitchedImage = cv.warpPerspective(SecImage, HomographyMatrix, (NewFrameSize[1], NewFrameSize[0]))
StitchedImage[Correction[1]:Correction[1] + BaseImage.shape[0],
Correction[0]:Correction[0] + BaseImage.shape[1]] = BaseImage
cv.namedWindow("stisched2.jpg", cv.WINDOW_NORMAL)
cv.imshow("stisched2.jpg", StitchedImage)
cv.imwrite("result.jpg", StitchedImage)
while True:
if cv.waitKey(1) == 27:
break
Aim of my program is find the angle of bending Led.
I got the angle using convexity defects in convex hull but the midpoint is move away from center point of that bend.
original image
original
below image is the output of program
output
black dot is starting point.
red dot is end point.
blue dot is mid point.
Now I want move blue dot to the center of the curve
my code
import cv2
import numpy as np
from math import sqrt
from collections import OrderedDict
def findangle(x1,y1,x2,y2,x3,y3):
ria = np.arctan2(y2 - y1, x2 - x1) - np.arctan2(y3 - y1, x3 - x1)
if ria > 0:
if ria < 3:
webangle = int(np.abs(ria * 180 / np.pi))
elif ria > 3:
webangle = int(np.abs(ria * 90 / np.pi))
elif ria < 0:
if ria < -3:
webangle = int(np.abs(ria * 90 / np.pi))
elif ria > -3:
webangle = int(np.abs(ria * 180 / np.pi))
return webangle
image = cv2.imread("cam/2022-09-27 10:01:57image.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
contours,hie= cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
selected_contour = max(contours, key=lambda x: cv2.contourArea(x))
# Draw Contour
approx = cv2.approxPolyDP(selected_contour, 0.0035 * cv2.arcLength(selected_contour, True), True)
for point in approx:
cv2.drawContours(image, [point], 0, (0, 0, 255), 3)
convexHull = cv2.convexHull(selected_contour,returnPoints=False)
cv2.drawContours(image, cv2.convexHull(selected_contour), 0, (0, 255, 0), 3)
convexHull[::-1].sort(axis=0)
convexityDefects = cv2.convexityDefects(selected_contour, convexHull)
start2,distance=[],[]
for i in range(convexityDefects.shape[0]):
s, e, f, d = convexityDefects[i, 0]
start = tuple(selected_contour[s][0])
end = tuple(selected_contour[e][0])
far = tuple(selected_contour[f][0])
start2.append(start)
cv2.circle(image, start, 2, (255, 0, 0), 3)
cv2.line(image,start,end , (0, 255, 0), 3)
distance.append(d)
distance.sort(reverse=True)
for i in range(convexityDefects.shape[0]):
s, e, f, d = convexityDefects[i, 0]
if distance[0]==d:
defect={"s":s,"e":e,"f":f,"d":d}
cv2.circle(image, selected_contour[defect.get("f")][0], 2, (255, 0, 0), 3)
cv2.circle(image, selected_contour[defect.get("s")][0], 2, (0, 0, 0), 3)
cv2.circle(image, selected_contour[defect.get("e")][0], 2, (0, 0, 255), 3)
x1, y1 = selected_contour[defect.get("f")][0]
x2, y2 = selected_contour[defect.get("e")][0]
x3, y3 = selected_contour[defect.get("s")][0]
cv2.line(image,(x1,y1),(x2,y2),(255,200,0),2)
cv2.line(image,(x1,y1),(x3,y3),(255,200,0),2)
cv2.putText(image, "Web Angle : " + str((findangle(x1,y1,x2,y2,x3,y3))), (50, 200), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 1, (0,0,0),2,cv2.LINE_AA)
cv2.imshow("frame",image)
cv2.waitKey(0)
cv2.destroyAllWindows()
so i want any concept to get exact center of the bend point.
Here is one way to do that in Python/OpenCV. I make no guarantees that it is universal and would work on all such images. I also leave it for others to add trapping for empty arrays/lists and other general best practices.
Read the input
Threshold to binary on white using cv2.inRange()
Apply morphology to close up the gap near the top
Skeletonize the binary image
Get the x and y coordinates of the points of the skeleton
Zip the x and y coordinates
Sort the zipped data by x
Sort another copy of the zipped data by y
Get the first line (end points) from the top for 40% of y from the y sorted data, since that region of the skeleton is nearly straight
Get the first line (end points) from the left for 40% of x from the x sorted data, since that region of the skeleton is nearly straight
Get the intersection point of these two lines
Compute the x and y derivatives of the x coordinates and the y coordinates, respectively
Loop over each point and compute the slope from the derivatives, which will be tangent to the skeleton at the point
Then still in the loop compute the inverse slope of the line from the point to the previously computed intersection point. This will be normal (perpendicular) to this line.
Compute the difference in slopes and find the point where the difference is minimum. This will be the bend point.
Draw relevant lines and points on skeleton and input
Save results
Input:
import cv2
import numpy as np
import skimage.morphology
img = cv2.imread("wire.png")
# create a binary thresholded image
lower = (255,255,255)
upper = (255,255,255)
thresh = cv2.inRange(img, lower, upper)
thresh = (thresh/255).astype(np.float64)
# apply morphology to connect at top
kernel = np.ones((11,11), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# apply skeletonization
skeleton = skimage.morphology.skeletonize(thresh)
skeleton = (255*skeleton).clip(0,255).astype(np.uint8)
# get skeleton points
pts = np.where(skeleton != 0)
x = pts[1]
y = pts[0]
num_pts = len(x)
print(num_pts)
# zip x and y
xy1 = zip(x,y)
xy2 = zip(x,y)
# sort on y
xy_sorty = sorted(xy1, key = lambda x: x[1])
#print(xy_sorty[0])
# sort on x
xy_sortx = sorted(xy2, key = lambda x: x[0])
#print(xy_sortx[0])
# unzip x and y for xy_sortedy
xu1, yu1 = zip(*xy_sorty)
# get first line from top
# find miny from y sort, then get point 40% down from miny
miny = np.amin(yu1)
y1 = miny
[xy1] = [(xi, yi) for (xi, yi) in xy_sorty if abs(yi - y1) <= 0.00001]
x1 = xy1[0]
y1 = xy1[1]
#print(x1,y1)
maxy = np.amax(yu1)
dely = maxy - miny
y2 = int(y1+0.4*dely)
[xy2] = [(xi, yi) for (xi, yi) in xy_sorty if abs(yi - y2) <= 0.00001]
x2 = xy2[0]
y2 = xy2[1]
#print(x2,y2)
# unzip x and y for xy_sortedx
xu2, yu2 = zip(*xy_sortx)
# get first line from left
# find minx from x sort, then get point 40% right from minx
minx = np.amin(xu2)
x3 = minx
[xy3] = [(xi, yi) for (xi, yi) in xy_sortx if abs(xi - x3) <= 0.00001]
x3 = xy3[0]
y3 = xy3[1]
#print(x3,y3)
maxx = np.amax(xu2)
delx = maxx - minx
x4 = int(x3+0.4*delx)
[xy4] = [(xi, yi) for (xi, yi) in xy_sortx if abs(xi - x4) <= 0.00001]
x4 = xy4[0]
y4 = xy4[1]
#print(x4,y4)
# draw lines on copy of skeleton
skeleton_lines = skeleton.copy()
skeleton_lines = cv2.merge([skeleton_lines,skeleton_lines,skeleton_lines])
cv2.line(skeleton_lines, (x1,y1), (x2,y2), (0,0,255), 2)
cv2.line(skeleton_lines, (x3,y3), (x4,y4), (0,0,255), 2)
# get intersection between line1 (x1,y1 to x2,y2) and line2 (x3,y3 to x4,y4) and draw circle
# https://en.wikipedia.org/wiki/Line–line_intersection
den = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
px = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4))/den
py = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4))/den
px = int(px)
py = int(py)
cv2.circle(skeleton_lines, (px,py), 3, (0,255,0), -1)
# compute first derivatives in x and also in y
dx = np.gradient(x, axis=0)
dy = np.gradient(y, axis=0)
# loop over each point
# get the slope of the tangent to the curve
# get the inverse slop of the line from the point to the intersection point (inverse slope is normal direction)
# get difference in slopes and find the point that has the minimum difference
min_diff = 1000000
eps = 0.0000000001
for i in range(num_pts):
slope1 = abs(dy[i]/(dx[i] + eps))
slope2 = abs((px - x[i])/(py - y[i] + eps))
slope_diff = abs(slope1 - slope2)
if slope_diff < min_diff:
min_diff = slope_diff
bend_x = x[i]
bend_y = y[i]
#print(x[i], y[i], min_diff)
bend_x = int(bend_x)
bend_y = int(bend_y)
#print(bend_x, bend_y)
cv2.line(skeleton_lines, (px,py), (bend_x,bend_y), (0,0,255), 2)
cv2.circle(skeleton_lines, (bend_x,bend_y), 3, (0,255,0), -1)
# get end points and bend point and draw on copy of input
result = img.copy()
end1 = (x1,y1)
end2 = (x3,y3)
bend = (bend_x,bend_y)
print("end1:", end1)
print("end2:", end2)
print("bend:", bend)
cv2.circle(result, (end1), 3, (0,0,255), -1)
cv2.circle(result, (end2), 3, (0,0,255), -1)
cv2.circle(result, (bend), 3, (0,0,255), -1)
# save result
cv2.imwrite("wire_skeleton.png", skeleton)
cv2.imwrite("wire_skeleton_lines.png", skeleton_lines)
cv2.imwrite("wire_result.png", result)
# show results
cv2.imshow("thresh", (255*thresh).astype(np.uint8))
cv2.imshow("skeleton", skeleton)
cv2.imshow("skeleton_lines", skeleton_lines)
cv2.imshow("skeleton_result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Skeleton:
Skeleton with lines:
Result showing end points and bend point:
Description:
I have this data represented in a cartesian coordinate system with 256 columns and 640 rows. Each column represents an angle, theta, from -65 deg to 65 deg. Each row represents a range, r, from 0 to 20 m.
An example is given below:
With the following code I try to make a grid and transform each pixel location to the location it would have on a polar grid:
def polar_image(image, bearings):
(h,w) = image.shape
x_max = (np.ceil(np.sin(np.deg2rad(np.max(bearings)))*h)*2+1).astype(int)
y_max = (np.ceil(np.cos(np.deg2rad(np.min(np.abs(bearings))))*h)+1).astype(int)
blank = np.zeros((y_max,x_max,1), np.uint8)
for i in range(w):
for j in range(h):
X = (np.sin(np.deg2rad( bearings[i]))*j)
Y = (-np.cos(np.deg2rad(bearings[i]))*j)
blank[(Y+h).astype(int),(X+562).astype(int)] = image[h-1-j,w-1-i]
return blank
This returns an image as below:
Questions:
This is sort of what I actually want to achieve except from two things:
1) there seem to be some artifacts in the new image and also the mapping seems a bit coarse.
Does someone have a suggestion on how to interpolate to get rid of this?
2) The image remains in a Cartesian representation, meaning that I don't have any polar gridlines, nor can I visualize intervals of range/angle.
Anybody know how to visualize the polar grids with axis ticks in theta and range?
You can use pyplot.pcolormesh() to plot the converted mesh:
import numpy as np
import pylab as pl
img = pl.imread("c:/tmp/Wnov4.png")
angle_max = np.deg2rad(65)
h, w = img.shape
angle, r = np.mgrid[-angle_max:angle_max:h*1j, 0:20:w*1j]
x = r * np.sin(angle)
y = r * np.cos(angle)
fig, ax = pl.subplots()
ax.set_aspect("equal")
pl.pcolormesh(x, y, img, cmap="gray");
or you can use the remap() in OpenCV to convert it to a new image:
import cv2
import numpy as np
from PIL import Image
img = cv2.imread(r"c:/tmp/Wnov4.png", cv2.IMREAD_GRAYSCALE)
angle_max = np.deg2rad(65)
r_max = 20
x = np.linspace(-20, 20, 800)
y = np.linspace(20, 0, 400)
y, x = np.ix_(y, x)
r = np.hypot(x, y)
a = np.arctan2(x, y)
map_x = r / r_max * img.shape[1]
map_y = a / (2 * angle_max) * img.shape[0] + img.shape[0] * 0.5
img2 = cv2.remap(img, map_x.astype(np.float32), map_y.astype(np.float32), cv2.INTER_CUBIC)
Image.fromarray(img2)
So, I want to transform an image but can't really find a proper way to do it using OpenCV.
First thing I have image lets say 500x600px inside of which there is a distorted thing I want to "straighten up" see the image:
I'm obtaining the contour of the sudoku like this:
cropped_image, contours, _ =
cv2.findContours(cropped_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
max_contour = max(contours, key=cv2.contourArea)
Then I'm getting the max_contour and image extreme pixels (top-left, top-right, bottom-right and bottom-left) and getting the transform matrix and transforming the image like this:
x, y = cropped_image.shape
image_extreme_pixels = np.array([[0, y], [x, y], [x, 0], [0, 0]], dtype=np.float32)
c_x, c_y = [], []
for i in contour:
c_x.append(i[0][0])
c_y.append(i[0][1])
contour_extreme_pixels = np.array([
[min(c_x), max(c_y)],
[max(c_x), max(c_y)],
[max(c_x), min(c_y)],
[min(c_x), min(c_y)]],
dtype=np.float32)
t_matrix = cv2.getPerspectiveTransform(contour_extreme_pixels, image_extreme_pixels)
transformed_image = cv2.warpPerspective(cropped_image, t_matrix, (y, x))
plt.imshow(cropped_image, interpolation='nearest', cmap=plt.cm.gray)
But when I view the image it's transformed in a weird fashion. I wanted to stretch the top parts of sudoku so that it's contour are straight.
Could you point what's wron with my code?
I'm assuming it might be the fashion in which I'm creating the 4 extreme pixels that are then put into the getPerspectiveTransform to get the transformation matrix but didn't manage to make it work yet.
Assuming that you have found out the corner points of sudoku accurately you can affine transform the input image as:
# Hard coded the points here assuming that you already have 4 corners of sudoku image
sudoku_corner_points = np.float32([[235, 40], [1022, 55], [190, 875], [1090, 880]])
canvas = np.ones((500, 500), dtype=np.uint8)
dst_points = np.float32([[0, 0], [500, 0], [0, 500], [500, 500]])
t_matrix = cv2.getPerspectiveTransform(sudoku_corner_points, dst_points)
transformed_image = cv2.warpPerspective(img, t_matrix, (500, 500))
So turns out the extreme points I've found were incorrect.
The correct way (one of many) to find 4 extreme points in a shape that we expect to be rectangular would be something like this:
def get_contour_extreme_points(img, contour):
m_point = image_center(img)
l1, l2, l3, l4 = 0, 0, 0, 0
p1, p2, p3, p4 = 0, 0, 0, 0
for point in contour:
d = distance(m_point, point[0])
if inside_bottom_right(m_point, point[0]) and l1 < d:
l1 = d
p1 = point[0]
continue
if inside_bottom_left(m_point, point[0]) and l2 < d:
l2 = d
p2 = point[0]
continue
if inside_top_right(m_point, point[0]) and l3 < d:
l3 = d
p3 = point[0]
continue
if inside_top_left(m_point, point[0]) and l4 < d:
l4 = d
p4 = point[0]
continue
return np.float32([p1, p2, p3, p4])
def inside_bottom_right(center, point):
return center[0] < point[0] and center[1] < point[1]
def inside_bottom_left(center, point):
return center[0] > point[0] and center[1] < point[1]
def inside_top_right(center, point):
return center[0] < point[0] and center[1] > point[1]
def inside_top_left(center, point):
return center[0] > point[0] and center[1] > point[1]
def distance(p1, p2):
return math.sqrt( ((p1[0]-p2[0])**2)+((p1[1]-p2[1])**2) )
def image_center(img):
x, y = img.shape
return tuple([x/2, y/2])
then I would have to be careful about the order of the 4 extreme points of the image. Which should look like this:
x, y = img.shape
img_extreme_points = np.float32([[x, y], [0, y], [x, 0], [0, 0]])
so first is the bottom right extreme point, then bottom left, top right and top left. As long as the extreme points index corresponds correctly the matrix will be computed correctly as well.