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

2024-05-19 01:35:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试检测扑克牌,并使用python opencv对它们进行转换,以获得扑克牌的鸟瞰图。我的代码可以很好地处理简单的案例,但我没有停留在简单的案例上,而是想尝试更复杂的案例。我在为卡片寻找正确的轮廓时遇到问题。下面是我试图检测卡片并绘制轮廓的附加图像:

enter image description 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()

结果:

enter image description here

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

其他研究员还要求提供几个例子:

enter image description here

enter image description here


Tags: 代码recttrueimgpltcv2opencv案例
3条回答

有很多方法可以在图像中找到重叠的对象。你可以肯定的是,你的卡片都是长方形的,大部分是白色的,大小都一样。你的变量是亮度,角度,可能是一些透视失真。如果您想要一个健壮的解决方案,您需要解决所有这些问题

我建议使用Hough变换来查找卡片边缘。首先,运行常规的边缘检测。比你需要清理的结果,因为许多短边将属于“脸”卡。我建议结合使用扩张(11)——>;侵蚀(15)——>;扩张(5)。这种组合将填补“脸”卡中的所有空白,然后它会“缩小”斑点,在去除原始边缘的过程中,最终会重新生长并与原始脸图片重叠一点。然后将其从原始图像中删除

现在您有了一个几乎具有所有相关边缘的图像。使用Hough变换查找它们。它会给你一套台词。过滤一点后,你们可以将这些边和卡片的矩形相匹配

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)

这将为您提供以下信息:

enter image description here

我在检测你的形状里面的白色矩形。最终结果是检测到的图像和边界框坐标。脚本还没有完成。在接下来的几天里,我会继续努力

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()

enter image description here

enter image description here

enter image description here

另一种获得更好结果的方法是去掉边缘检测/线条检测部分(我个人更喜欢),在图像预处理后找到轮廓

以下是我的代码和结果:

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)

结果-

card1 outputcard2 output

通过这个,我们得到了图像中卡片的边界。要检测和分离每一张卡,需要一个更复杂的算法,或者可以使用深度学习模型

相关问题 更多 >

    热门问题