使用 OpenCV Python 以与参考图像相同的方向和尺寸转换和显示裁剪图像

Posted

技术标签:

【中文标题】使用 OpenCV Python 以与参考图像相同的方向和尺寸转换和显示裁剪图像【英文标题】:Transform and display cropped image in same orientation and dimension as reference image with OpenCV Python 【发布时间】:2019-09-13 22:24:40 【问题描述】:

我有一个参考图像,我正在使用 ORB 检测在更大的测试图像中找到该参考图像。找到后,我只想将裁剪区域内的区域保存为新图像。我想将其转换为与参考图像相同的尺寸并以相同的方向保存。

到目前为止,我已经匹配了较大图像中的参考图像并将其屏蔽了。但我不知道如何以正确的方向和尺寸仅将裁剪区域显示为自己的图片。我想把庄稼里的东西保存下来,把剩下的扔掉。

任何帮助将不胜感激。谢谢。

import cv2
import numpy as np

#minimum ORB matches required to make a match
MIN_MATCH_COUNT = 10

img1 = cv2.imread("reference.jpg")
img2 = cv2.imread("1.jpg")

orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2, None)

# sorts matches
good = []
for i, m in enumerate(matches):
    if i < len(matches) - 1 and m.distance < 0.7 * matches[i+1].distance:
        good.append(m)

if len(good)>MIN_MATCH_COUNT:
    src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
    dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
    matchesMask = mask.ravel().tolist()
    h,w,d = img1.shape
    pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
    dst = cv2.perspectiveTransform(pts,M)
    mask = np.ones(img2.shape[:2], dtype="uint8") * 255
    rect = cv2.minAreaRect(dst)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    new = cv2.drawContours(mask, [box], -1, 0, -1)
    # remove the contours from the image and show the resulting images
    img = cv2.bitwise_and(img2, img2, mask=mask)
    cv2.imshow("Mask", mask)
    cv2.imshow("After", img)
else:
    print( "Not enough matches are found - /".format(len(good), MIN_MATCH_COUNT) )
    matchesMask = None

#This is for drawing the match lines inbetween the ref and 1.jpg images
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
               singlePointColor = None,
               matchesMask = matchesMask, # draw only inliers
               flags = 2)
img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
cv2.imshow("Matches", img3)

【问题讨论】:

【参考方案1】:

您走在正确的轨道上,并且已经完成了大部分工作。由于您已找到 ROI 蒙版,因此您可以执行透视变换以获得正确的方向。之后,您可以调整图像大小以匹配您的参考/模板图像。

首先我们反转蒙版以获得所需的白色 ROI,然后在此蒙版上找到轮廓。从这里我们使用cv2.arcLength()cv2.approxPolyDP() 找到角落。接下来我们透视变换来得到这个

template shape: (210, 236, 3)
transformed shape: (288, 279, 3)

现在我们有了正确的方向,我们只需要简单地调整大小以匹配模板图像。这是结果(左)和模板图像(右)

template shape: (210, 236, 3)
resized shape: (210, 236, 3)

代码

import cv2
import numpy as np

def perspective_transform(image, corners):
    def order_corner_points(corners):
        # Separate corners into individual points
        # Index 0 - top-right
        #       1 - top-left
        #       2 - bottom-left
        #       3 - bottom-right
        corners = [(corner[0][0], corner[0][1]) for corner in corners]
        top_r, top_l, bottom_l, bottom_r = corners[0], corners[1], corners[2], corners[3]
        return (top_l, top_r, bottom_r, bottom_l)

    # Order points in clockwise order
    ordered_corners = order_corner_points(corners)
    top_l, top_r, bottom_r, bottom_l = ordered_corners

    # Determine width of new image which is the max distance between 
    # (bottom right and bottom left) or (top right and top left) x-coordinates
    width_A = np.sqrt(((bottom_r[0] - bottom_l[0]) ** 2) + ((bottom_r[1] - bottom_l[1]) ** 2))
    width_B = np.sqrt(((top_r[0] - top_l[0]) ** 2) + ((top_r[1] - top_l[1]) ** 2))
    width = max(int(width_A), int(width_B))

    # Determine height of new image which is the max distance between 
    # (top right and bottom right) or (top left and bottom left) y-coordinates
    height_A = np.sqrt(((top_r[0] - bottom_r[0]) ** 2) + ((top_r[1] - bottom_r[1]) ** 2))
    height_B = np.sqrt(((top_l[0] - bottom_l[0]) ** 2) + ((top_l[1] - bottom_l[1]) ** 2))
    height = max(int(height_A), int(height_B))

    # Construct new points to obtain top-down view of image in 
    # top_r, top_l, bottom_l, bottom_r order
    dimensions = np.array([[0, 0], [width - 1, 0], [width - 1, height - 1], 
                    [0, height - 1]], dtype = "float32")

    # Convert to Numpy format
    ordered_corners = np.array(ordered_corners, dtype="float32")

    # Find perspective transform matrix
    matrix = cv2.getPerspectiveTransform(ordered_corners, dimensions)

    # Return the transformed image
    return cv2.warpPerspective(image, matrix, (width, height))

#minimum ORB matches required to make a match
MIN_MATCH_COUNT = 10

img1 = cv2.imread("reference.jpg")
img2 = cv2.imread("1.jpg")

orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2, None)

# sorts matches
good = []
for i, m in enumerate(matches):
    if i < len(matches) - 1 and m.distance < 0.7 * matches[i+1].distance:
        good.append(m)

if len(good)>MIN_MATCH_COUNT:
    src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
    dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
    matchesMask = mask.ravel().tolist()
    h,w,d = img1.shape
    pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
    dst = cv2.perspectiveTransform(pts,M)
    mask = np.ones(img2.shape[:2], dtype="uint8") * 255
    rect = cv2.minAreaRect(dst)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    new = cv2.drawContours(mask, [box], -1, 0, -1)
    # remove the contours from the image and show the resulting images
    img = cv2.bitwise_and(img2, img2, mask=mask)
    mask = 255 - mask
    cv2.imshow("After", img)
else:
    print( "Not enough matches are found - /".format(len(good), MIN_MATCH_COUNT) )
    matchesMask = None

#This is for drawing the match lines inbetween the ref and 1.jpg images
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
               singlePointColor = None,
               matchesMask = matchesMask, # draw only inliers
               flags = 2)
img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
cv2.imshow("Matches", img3)

cv2.imshow("Mask", mask)

# Find contour on mask and perform perspective transform
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.015 * peri, True)
    if len(approx) == 4:
        transformed = perspective_transform(img2, approx)

cv2.imshow("transformed", transformed)

print('template shape:', img1.shape)
print('transformed shape:',transformed.shape)

resized = cv2.resize(transformed, (img1.shape[1], img1.shape[0]))
cv2.imshow("resized", resized)
print('template shape:', img1.shape)
print('resized shape:',resized.shape)
cv2.waitKey()

【讨论】:

哇! tysm nathancy。你让这看起来很容易。 目前,使用cv2.resize() 调整大小不保持纵横比。如果您想保持纵横比,请查看imutils.resize()。您可能无法获得与参考图像完全相同的形状尺寸

以上是关于使用 OpenCV Python 以与参考图像相同的方向和尺寸转换和显示裁剪图像的主要内容,如果未能解决你的问题,请参考以下文章

如何使用rawpy对原始图像进行后处理,以与默认输出(如相机中的ISP)具有相同的效果?

以与文件相同的顺序加载图像

我已经设置了 OpenCV 以与 QT 一起使用。我也可以将此安装与 Visual Studio 一起使用吗?

OpenCV-Python之——图像SIFT特征提取

Python,OpenCV进行直方图反投影

Python OpenCV2:OpenCV 图像算数操作