Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

openCV: How to use getPerspectiveTransform

Tags:

python

opencv

I have an image represented by a matrix of size n * n

I've created a transform matrix

M = cv2.getPerspectiveTransform(...)

And I can transform the image with the shape defined in M using

cv2.warpPerspective(image, M, image_shape)

According to this, I should be able to multiply the matrix with a point and get the new location of that point, after the transformation. I tried that:

point = [100, 100, 0]
x, y, z = M.dot(point)

But I got the wrong results. (In this case [112.5 12.5 0])

What am I doing wrong?


For more clarity, here's what I'm doing:

I have this image, with the lines and the square on different layers

before warp

I warp the lines and get this:

warped lines

And now I want to get the coordinates for putting the square like this:

expected

What I have is the warp matrix I used for the lines, and the coordinates of the square in the first picture


Note: one option is to create an image with a single dot, just warp it with the first method and find the non-zero cells in the new image. I'm looking for something more idiomatic than that, hopefully

like image 519
Dotan Avatar asked Jul 10 '17 11:07

Dotan


People also ask

What does cv2 warpPerspective do?

We make use of a function called warpPerspective() function to fit the size of the resulting image by using the getPerspectiveTransform() function to the size of the original image or video. The warpPerspective() function returns an image or video whose size is the same as the size of the original image or video.

What does cv2 warpAffine do?

OpenCV provides a function cv2. warpAffine() that applies an affine transformation to an image. You just need to provide the transformation matrix (M). The basic syntax for the function is given below.


1 Answers

The last point of a homogeneous coordinate should never be 0 unless it is specifically referencing a point at infinity. For your purposes, it should be 1. You should also scale the transformed pixels x and y by the last value z. See my answer here for an in-depth explanation.

For a single point:

point = np.array([100, 100])
homg_point = [point[0], point[1], 1] # homogeneous coords
transf_homg_point = M.dot(homg_point) # transform
transf_homg_point /= transf_homg_point[2] # scale
transf_point = transf_homg_point[:2] # remove Cartesian coords

For multiple points (with the standard way that OpenCV writes points):

points = np.array([[[100, 100]], [[150,100]], [[150,150]], [[150,100]]])
homg_points = np.array([[x, y, 1] for [[x, y]] in points]).T
transf_homg_points = M.dot(homg_points)
transf_homg_points /= transf_homg_points[2]
transf_points = np.array([[[x,y]] for [x, y] in transf_homg_points[:2].T])

A minimal example using points grabbed from an OpenCV function:

import numpy as np 
import cv2

# generate random noise image, draw a white box
img = np.random.rand(512,512,3)
img[128:384, 64:196] = [1,1,1]

# create a Euclidean transformation which rotates by 30 deg + translates by (100,100)
theta = 30*np.pi/180
H = np.array([
    [ np.cos(theta),np.sin(theta),100.],
    [-np.sin(theta),np.cos(theta),100.],
    [0.,0.,1.]])

# find the white box
bin_img = cv2.inRange(img, np.ones(3), np.ones(3))
contour_points = cv2.findContours(bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1][0]

# transform the location of the box
homg_points = np.array([[x, y, 1] for [[x, y]] in contour_points]).T
transf_homg_points = H.dot(homg_points)
transf_homg_points /= transf_homg_points[2]
transf_rect = np.array([[[x,y]] for [x, y] in transf_homg_points[:2].T], dtype=np.int32)

# draw the transformed box as a green outline
cv2.polylines(img, [transf_rect], isClosed=True, color=[0,1,0], thickness=2)

Yielding an image with random noise, a white box, and a green outline which shows the transformation applied to the box contours.

Transformed contour

like image 156
alkasm Avatar answered Sep 30 '22 04:09

alkasm