我如何使用 python opencv 将重叠的卡片彼此分开?

How do i separate overlapping cards from each other using python opencv?

我正在尝试使用 python opencv 检测扑克牌并对其进行转换以鸟瞰扑克牌。我的代码适用于简单的案例,但我并没有停留在简单的案例上,而是想尝试更复杂的案例。我在为 cards.Here 的附加图像找到正确的轮廓时遇到问题,我正在尝试检测卡片并绘制轮廓:

我的代码:

path1 = "F:\ComputerVisionPrograms\images\cards4.jpeg"
g = cv2.imread(path1,0)
img = cv2.imread(path1)

edge = cv2.Canny(g,50,200)

p,c,h = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect = []
for i in c:
    p = cv2.arcLength(i, True)
    ap = cv2.approxPolyDP(i, 0.02 * p, True)
    if len(ap)==4:
        rect.append(i)
cv2.drawContours(img,rect, -1, (0, 255, 0), 3)

plt.imshow(img)
plt.show()

结果:

这不是我想要的,我只想选择矩形卡片,但由于它们相互遮挡,我没有得到我期望的结果。我相信我需要应用形态学技巧或其他操作来将它们分开或使边缘更加突出或可能是其他东西。如果您能分享您解决此问题的方法,我们将不胜感激。

其他同学要求的几个例子:

有很多方法可以找到图像中的重叠对象。你确定的信息是你的卡片都是矩形的,大部分是白色的,并且大小相同。你的变量是亮度,角度,可能是一些透视变形。如果您想要一个可靠的解决方案,则需要解决所有这些问题。

我建议使用霍夫变换来查找卡片边缘。首先,运行一个规则的边缘检测。比您需要清理结果,因为许多短边将属于“面”牌。我建议使用 dilate(11)->erode(15)->dilate(5) 的组合。这种组合将填充“面部”卡片中的所有空白,然后它“缩小”斑点,在去除原始边缘的过程中最后长回来并与原始面部图片重叠一点。然后将其从原始图像中删除。

现在您的图像几乎具有所有相关边缘。使用霍夫变换找到它们。它会给你一组线。在稍微过滤它们之后,您可以将这些边缘调整为卡片的矩形形状。

dst = cv2.Canny(img, 250, 50, None, 3)

cn = cv2.dilate(dst, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11)))
cn = cv2.erode(cn, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15)))
cn = cv2.dilate(cn, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
dst -= cn
dst[dst < 127] = 0

cv2.imshow("erode-dilated", dst)

# Copy edges to the images that will display the results in BGR
cdstP = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)

linesP = cv2.HoughLinesP(dst, 0.7, np.pi / 720, 30, None, 20, 15)

if linesP is not None:
    for i in range(0, len(linesP)):
        l = linesP[i][0]
        cv2.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0, 255, 0), 2, cv2.LINE_AA)

cv2.imshow("Detected edges", cdstP)

这将为您提供以下内容:

另一种获得更好结果的方法是丢弃边缘detection/line检测部分(我个人更喜欢)并在图像预处理后找到轮廓。

下面是我的代码和结果:

img = cv2.imread(<image_name_here>)
imgC = img.copy()

# Converting to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Applying Otsu's thresholding
Retval, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# Finding contours with RETR_EXTERNAL flag to get only the outer contours
# (Stuff inside the cards will not be detected now.)
cont, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Creating a new binary image of the same size and drawing contours found with thickness -1.
# This will colour the contours with white thus getting the outer portion of the cards.
newthresh = np.zeros(thresh.shape, dtype=np.uint8)
newthresh = cv2.drawContours(newthresh, cont, -1, 255, -1)

# Performing erosion->dilation to remove noise(specifically white portions detected of the poker coins).
kernel = np.ones((3, 3), dtype=np.uint8)
newthresh = cv2.erode(newthresh, kernel, iterations=6)
newthresh = cv2.dilate(newthresh, kernel, iterations=6)

# Again finding the final contours and drawing them on the image.
cont, hier = cv2.findContours(newthresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(imgC, cont, -1, (255, 0, 0), 2)

# Showing image
cv2.imshow("contours", imgC)
cv2.waitKey(0)

结果 -

这样,我们就得到了图片中卡片的边界。要检测和分离每张卡片,将需要更复杂的算法,或者可以使用深度学习模型来完成。

我正在检测你形状内的白色矩形。最终结果是检测到的图像和边界框坐标。该脚本尚未完成。这两天我会努力继续的。

import os
import cv2
import numpy as np


def rectangle_detection(img):    
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, binarized = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    cn = cv2.dilate(binarized, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11)), iterations=3)
    cn = cv2.erode(cn, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15)), iterations=3)
    cn = cv2.dilate(cn, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)), iterations=3)

    _, contours, _ = cv2.findContours(binarized, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    # contours = sorted(contours, key=lambda x: cv2.contourArea(x))

    # detect all rectangles
    rois = []
    for contour in contours:
        cont_area = cv2.contourArea(contour)
        approx = cv2.approxPolyDP(contour, 0.02*cv2.arcLength(contour, True), True)
        if 1000 < cont_area < 15000:
            x, y, w, h = cv2.boundingRect(contour)
            rect_area = w * h
            if cont_area / rect_area < 0.6: # check the 'rectangularity'
                continue     
            cv2.drawContours(img, [approx], 0, (0, 255, 0), 2)
            if len(approx) == 4:
                cv2.putText(img, "Rect", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255))
            rois.append((x, y, w, h))
    return img, rois


def main():
    # load and prepare images
    INPUT = 'path'
    img = cv2.imread(INPUT)
    display, rects = rectangle_detection(img)
    cv2.imshow('img', display)
    cv2.waitKey()


if __name__ == "__main__":
    main()