【问题标题】:Python OpenCV Perspective Correction for Rectangle With Rounded Corners带有圆角的矩形的 Python OpenCV 透视校正
【发布时间】:2019-01-12 07:42:36
【问题描述】:

下面的脚本(从 pyimagesearch.com 修改)尝试纠正扫描卡片的透视以旋转和裁剪图像。我有完成转换的最终代码,但在此之前绘制的边界矩形没有按预期运行。这是原图:

代码:

# import the necessary packages
from skimage.filters import threshold_local
import numpy as np
import cv2
import imutils

# load the image and compute the ratio of the old height
# to the new height, clone it, and resize it
image = cv2.imread('cards/red8.jpg')
ratio = image.shape[0] / 500.0
orig = image.copy()
image = imutils.resize(image, height = 500)

# convert the image to grayscale, blur it, and find edges
# in the image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
blur = cv2.GaussianBlur(gray,(1,1),1000)
edged = cv2.Canny(gray, 75, 200)

# find the contours in the edged image, keeping only the
# largest ones, and initialize the screen contour
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]

# loop over the contours
for c in cnts:
    # approximate the contour
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)

    # if our approximated contour has four points, then we
    # can assume that we have found our screen
    if len(approx) == 4:
        screenCnt = approx
        break

cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)

绘制框的输出似乎被圆角甩掉了,并甩掉了透视校正,与卡片直边的轻微部分对接。

我可以做些什么来得到一个适当的边界矩形来解释弯曲的边缘吗?

谢谢!

【问题讨论】:

  • 所有样本图像的背景是否都是灰色的?
  • 是的,它们是一样的。每张图片有九张卡片。我试图让代码为一个人工作。

标签: python python-2.7 opencv


【解决方案1】:

您的算法似乎还不错。

我可以做些什么来得到一个合适的边界矩形来解释弯曲的边缘吗?

是的。首先,opencv 的 findCountours 已经有了你想要的——它在变量c 中。使用它代替approx(参见代码)。现在对于边界矩形,使用cv2.boundingRect,它将边界矩形的左上角、宽度和高度返回给给定的点集。

# ...    
# loop over the contours
for c in cnts:
    # approximate the contour
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)

    if len(approx) == 4:
        # Found the countour is "like" rectangle, use that countour
        screenCnt = c # <-- Take contour Here
        break

cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)

x,y,w,h = cv2.boundingRect(screenCnt) # <-- Get rectangle here
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)

cv2.imshow("image",image);
while(0xff & cv2.waitKey(1) != ord('q')):pass
cv2.destroyAllWindows();

【讨论】:

  • 如果对象接近完美的矩形,它就可以工作。考虑用相机拍摄的图像,它可能会产生透视失真。然后矩形不适合
【解决方案2】:

我使用一些先验来处理图像的检测和校正(变形)。 我在 cmets 中详细介绍了主要步骤及其作用。 唯一的事情是您需要 OpenCV 的贡献模块才能使其正常工作。

import numpy as np
import cv2
from matplotlib import pyplot as plt


def show(I):
    plt.figure(figsize=(10,10))
    if I.ndim == 2:
        plt.imshow(I,cmap='gray')
    else:
        plt.imshow(cv2.cvtColor(I,cv2.COLOR_BGR2RGB))

def process_borders(I, lower_intensity=90,upper_intensity=100):
    I = cv2.bilateralFilter(I,-1,3,5)

    channels = cv2.split(I)

    borders = 255*np.ones(I.shape[:2],np.uint8)

    for channel in channels:
    #   Apply a prior on about the colours of interest on each channel.    
        mask = cv2.inRange(channel,lower_intensity,upper_intensity)


    #   Refine the contours
        mask = cv2.morphologyEx(mask,cv2.MORPH_CLOSE,None,iterations=4)
    #   Only the pixel which have been on each mask are kept
        borders = cv2.bitwise_and(borders,mask)

    # Bitwise not in order to segment the card and not the borders
    borders = cv2.bitwise_not(borders)

    # Use a edge detector on the segmentation image in order to find the contours
    sx = cv2.Sobel(borders,cv2.CV_32F,1,0)
    sy = cv2.Sobel(borders,cv2.CV_32F,0,1)
    m = cv2.magnitude(sx,sy)
    # Refine the contours thickness
    m = cv2.normalize(m,None,0.,255.,cv2.NORM_MINMAX,cv2.CV_8U)
    m = cv2.ximgproc.thinning(m)

    _, contours, hierarchy = cv2.findContours(m, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    tmp = np.zeros(m.shape,np.uint8)
    borders = cv2.drawContours(tmp,contours,-1,255,hierarchy=hierarchy)

    return borders, contours[0]

def remove_rotation(I,borders, contours):
    # From the contours points find the angle
    ((_,_),(_,_),angle) = cv2.minAreaRect(contours)

    rows, cols = borders.shape

    # From the angle get and apply the transformation matrix to the original image and the border image
    mat = cv2.getRotationMatrix2D((cols//2,rows//2), angle, 1.0)
    I = cv2.warpAffine(I,mat,(cols,rows))
    mat = cv2.getRotationMatrix2D((cols//2,rows//2), angle, 1.0)
    borders = cv2.warpAffine(borders,mat,(cols,rows))

    # Update the contours.
    _, contours, _ = cv2.findContours(borders, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    return I,borders,contours


I = cv2.imread('/home/smile/Downloads/card.jpg')

show(I)

borders, contours = process_borders(I)

I,borders,contours = remove_rotation(I,borders,contours)

I = cv2.drawContours(I,contours,-1,(0,255,0))

# Fine and rectified borders
show(I)

【讨论】:

    【解决方案3】:

    使用原始轮廓点代替近似值。

    if len(approx) == 4:
        screenCnt = approx
        ((x,y),(w,h),angle) = cv2.minAreaRect(c)
        rows,cols = image.shape[:2]
        M = cv2.getRotationMatrix2D((int(cols/2),int(rows/2)), angle, 1.0)
        nimg = cv2.warpAffine(image,M,(cols,rows))          
        break
    

    【讨论】:

    • 我无法重新生成该图像结果。 @zindarod,你还有什么改变吗?另外,这种方法是否可以只裁剪到卡片(去除除了角落的灰色背景)?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多