cv2.warpPerspective does not work correctly. | opencv-python

405 Views Asked by At

I put the dots: Top left, Bottom left, Bottom right, Top right. The "Warped" window just shows a white screen. I wanted the Warped window to display a distorted image. In the "four_point_transform" function, the "maxWidth" variable almost always gave 0, I do not know what this is related to.

Windows 11 64-bit, python 3.10.7, opencv-contrib-python 4.55.62

Image with error Image

import numpy
import cv2
import numpy as np


def on_click(event, x, y, flags, param):
    global a_, b_, c_, d_, to_set
    if event == cv2.EVENT_LBUTTONDOWN:
        print("click")
        if to_set == 0:
            to_set = 1
            a_ = [x, y]
        elif to_set == 1:
            to_set = 2
            b_ = [x, y]
        elif to_set == 2:
            to_set = 3
            c_ = [x, y]
        elif to_set == 3:
            to_set = 0
            d_ = [x, y]


def order_points(pts):
    # initialzie a list of coordinates that will be ordered
    # such that the first entry in the list is the top-left,
    # the second entry is the top-right, the third is the
    # bottom-right, and the fourth is the bottom-left
    rect = np.zeros((4, 2), dtype="float32")

    # the top-left point will have the smallest sum, whereas
    # the bottom-right point will have the largest sum
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    # now, compute the difference between the points, the
    # top-right point will have the smallest difference,
    # whereas the bottom-left will have the largest difference
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]

    # return the ordered coordinates
    return rect


def four_point_transform(image, pts):
    # obtain a consistent order of the points and unpack them
    # individually
    rect = order_points(pts)
    (tl, tr, br, bl) = rect

    # compute the width of the new image, which will be the
    # maximum distance between bottom-right and bottom-left
    # x-coordiates or the top-right and top-left x-coordinates
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    # compute the height of the new image, which will be the
    # maximum distance between the top-right and bottom-right
    # y-coordinates or the top-left and bottom-left y-coordinates
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    # now that we have the dimensions of the new image, construct
    # the set of destination points to obtain a "birds eye view",
    # (i.e. top-down view) of the image, again specifying points
    # in the top-left, top-right, bottom-right, and bottom-left
    # order
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype="float32")

    # compute the perspective transform matrix and then apply it
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    # return the warped image
    return warped


#cap = cv2.VideoCapture(1, cv2.CAP_DSHOW)
cv2.namedWindow("Corner points")
cv2.setMouseCallback("Corner points", on_click)
to_set = 0
a_ = b_ = c_ = d_ = [0, 0]
while True:
    big_img = cv2.imread("Test.png")
    #_, big_img = cap.read()
    ratio = big_img.shape[0] / 500.0
    org = big_img.copy()
    img = imutils.resize(big_img, height=500)
    cv2.putText(img, f"{to_set}", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0))
    values = numpy.array([a_,
                          b_,
                          c_,
                          d_], dtype="float32")
    warped = four_point_transform(org, values * ratio)
    cv2.circle(img, a_, 5, (0, 0, 255))
    cv2.circle(img, b_, 5, (0, 0, 255))
    cv2.circle(img, c_, 5, (0, 0, 255))
    cv2.circle(img, d_, 5, (0, 0, 255))
    cv2.imshow("Warped", warped)
    cv2.imshow('Corner points', img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()
1

There are 1 best solutions below

0
YScharf On BEST ANSWER

The code you based your question is taken from an article by Adrian

When trying to debug the code, there seemed to be an error in the order_points function. Turns out that Adrian himself has spotted the error.

He actually wrote an article lining out the error and an improved version of the order_points function.

The working code:

import numpy
import cv2
import numpy as np
from scipy.spatial import distance as dist

def on_click(event, x, y, flags, param):
    global a_, b_, c_, d_, to_set
    if event == cv2.EVENT_LBUTTONDOWN:
        print("click")
        if to_set == 0:
            to_set = 1
            a_ = [x, y]
        elif to_set == 1:
            to_set = 2
            b_ = [x, y]
        elif to_set == 2:
            to_set = 3
            c_ = [x, y]
        elif to_set == 3:
            to_set = 0
            d_ = [x, y]



def order_points(pts):
    # sort the points based on their x-coordinates
    xSorted = pts[np.argsort(pts[:, 0]), :]
    # grab the left-most and right-most points from the sorted
    # x-roodinate points
    leftMost = xSorted[:2, :]
    rightMost = xSorted[2:, :]
    # now, sort the left-most coordinates according to their
    # y-coordinates so we can grab the top-left and bottom-left
    # points, respectively
    leftMost = leftMost[np.argsort(leftMost[:, 1]), :]
    (tl, bl) = leftMost
    # now that we have the top-left coordinate, use it as an
    # anchor to calculate the Euclidean distance between the
    # top-left and right-most points; by the Pythagorean
    # theorem, the point with the largest distance will be
    # our bottom-right point
    D = dist.cdist(tl[np.newaxis], rightMost, "euclidean")[0]
    (br, tr) = rightMost[np.argsort(D)[::-1], :]
    # return the coordinates in top-left, top-right,
    # bottom-right, and bottom-left order
    return np.array([tl, tr, br, bl], dtype="float32")


def four_point_transform(image, pts):
    # obtain a consistent order of the points and unpack them
    # individually
    rect = order_points(pts)
    (tl, tr, br, bl) = rect

    # compute the width of the new image, which will be the
    # maximum distance between bottom-right and bottom-left
    # x-coordiates or the top-right and top-left x-coordinates
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    # compute the height of the new image, which will be the
    # maximum distance between the top-right and bottom-right
    # y-coordinates or the top-left and bottom-left y-coordinates
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    # now that we have the dimensions of the new image, construct
    # the set of destination points to obtain a "birds eye view",
    # (i.e. top-down view) of the image, again specifying points
    # in the top-left, top-right, bottom-right, and bottom-left
    # order
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype="float32")

    # compute the perspective transform matrix and then apply it
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    # return the warped image
    return warped


#cap = cv2.VideoCapture(1, cv2.CAP_DSHOW)
cv2.namedWindow("Corner points")
cv2.setMouseCallback("Corner points", on_click)
to_set = 0
a_ = b_ = c_ = d_ = [0, 0]
while True:
    img = cv2.imread("test1.png")
    #_, big_img = cap.read()
    ratio = img.shape[0] / 500.0
    org = img.copy()
    #img = cv2.resize(big_img, fx=ratio, fy=ratio)
    cv2.putText(img, f"{to_set}", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0))
    values = numpy.array([a_,
                          b_,
                          c_,
                          d_], dtype="float32")
    
    # values = numpy.array(([[160. ,251.],
    #                         [560., 217.],
    #                         [241. ,304.],
    #                         [615., 257.]]))
    print(values)
    print('\n')
    warped = four_point_transform(org, values)
    cv2.circle(img, a_, 5, (0, 0, 255))
    cv2.circle(img, b_, 5, (0, 0, 255))
    cv2.circle(img, c_, 5, (0, 0, 255))
    cv2.circle(img, d_, 5, (0, 0, 255))
    cv2.imshow("Warped", warped)
    cv2.imshow('Corner points', img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()

works