使用Python / OpenCV从图像中提取固定数量的正方形

时间:2019-04-24 14:23:50

标签: python opencv template-matching

我有几张要使用Python / Opencv计算的扫描图像。这些图像中的每一个(请参见下面的示例)都包含n行彩色正方形。这些正方形均具有相同的大小。目标是裁剪每个正方形并从中提取数据。

Image with squares to extract

我发现there是一个能够从图像中提取正方形的代码。

这是我使用过的代码:

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

def angle_cos(p0, p1, p2):
    import numpy as np

    d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float')
    return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) )

def find_squares(img):
    import cv2 as cv
    import numpy as np

    img = cv.GaussianBlur(img, (5, 5), 0)
    squares = []
    for gray in cv.split(img):
        for thrs in range(0, 255, 26):
            if thrs == 0:
                bin = cv.Canny(gray, 0, 50, apertureSize=5)
                bin = cv.dilate(bin, None)
            else:
                _retval, bin = cv.threshold(gray, thrs, 255, cv.THRESH_BINARY)
            contours, _hierarchy = cv.findContours(bin, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
            for cnt in contours:
                cnt_len = cv.arcLength(cnt, True)
                cnt = cv.approxPolyDP(cnt, 0.02*cnt_len, True)
                if len(cnt) == 4 and cv.contourArea(cnt) > 1000 and cv.isContourConvex(cnt):
                    cnt = cnt.reshape(-1, 2)
                    max_cos = np.max([angle_cos( cnt[i], cnt[(i+1) % 4], cnt[(i+2) % 4] ) for i in range(4)])
                    if max_cos < 0.1:
                        squares.append(cnt)
    print(len(squares))
    return squares

img = cv2.imread("test_squares.jpg",1)

plt.axis("off")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

squares = find_squares(img)
cv2.drawContours( img, squares, -1, (0, 255, 0), 1 )
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

但是,它找到两个很多正方形(100个而不是15个!!)。查看图像,似乎Opencv为每个正方形找到了很多轮廓。

我很确定可以对其进行优化,因为正方形的大小大致相同,并且彼此之间相距甚远。作为Opencv的初学者,我还没有找到在函数“查找平方”中给出更多条件的方法,以便在例程结束时仅获得15个平方。也许轮廓区域可以最大化?

我还发现there的代码更详细(非常接近先前的代码),但是它似乎是在旧版本的Opencv中开发的。我没有设法使其正常工作(因此无法对其进行修改)。

2 个答案:

答案 0 :(得分:1)

这是another more robust method

我使用此代码在图像中找到轮廓(完整代码可在in this gist中找到):

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

# Define square size
min_square_size = 987
# Read Image
img = cv2.imread('/home/stephen/Desktop/3eY0k.jpg')
# Threshold and find edges
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold the image - segment white background from post it notes
_, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY_INV);
# Find the contours
_, contours, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

我遍历了轮廓。我只看了合理大小的轮廓。我找到了每个轮廓的四个角。

corners

# Create a list for post-it images
images = []
# Iterate through the contours in the image
for contour in contours:
    area = cv2.contourArea(contour)
    # If the contour is not really small, or really big
    h,w = img.shape[0], img.shape[1]
    if area > min_square_size and area < h*w-(2*(h+w)):
        # Get the four corners of the contour
        epsilon = .1 * cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(contour, epsilon, True)
        # Draw the point
        for point in approx: cv2.circle(img, tuple(point[0]), 2, (255,0,0), 2)
        # Warp it to a square
        pts1 = np.float32(approx)
        pts2 = np.float32([[0,0],[300,0],[300,300],[0,300]])
        M = cv2.getPerspectiveTransform(pts1,pts2)
        dst = cv2.warpPerspective(img,M,(300,300))
        # Add the square to the list of images
        images.append(dst.copy())

便利贴为正方形,但由于相机扭曲图像中的对象,因此它们不会显示为正方形。我使用warpPerspective使便签纸成为正方形。此图中仅显示了其中的一些(还有更多不适合的显示): plot

答案 1 :(得分:0)

如果您的问题是在图像中发现太多的轮廓(边缘),我的建议是先修改边缘查找部分。到目前为止,这将是最简单的修改。

尤其是,您需要更改此呼叫:

bin = cv.Canny(gray, 0, 50, apertureSize=5)

cv.Canny()函数将两个阈值,孔径大小和布尔值作为参数,以指示是否使用了精确的渐变形式。使用这些参数,我的猜测是,您将获得更好的结果。