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

Extract a fixed number of squares from an image with Python/OpenCV

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

我找到了 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 的初学者,我还没有找到一种方法来在函数 "find squares" 中给出更多的条件,以便在例程结束时只得到 15 个方块。也许轮廓区域可以最大化?

我也找到了there更详细的代码(和上一个很接近)但是好像是用老版本的Opencv开发的。我还没有设法让它工作(所以修改它)。

如果你的问题是在图像中发现太多的轮廓(边缘),我的建议是先修改找边部分。这将是迄今为止最容易进行的修改。

特别是,您需要更改此调用:

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

cv.Canny() 函数将两个阈值、光圈大小和一个布尔值作为参数,以指示是否使用精确形式的渐变。使用这些参数,我猜你会得到更好的结果。

这是

我用这段代码找到了图像中的轮廓(完整代码可以找到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)

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

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

post-it 注释是正方形,但由于相机扭曲了图像中的对象,因此它们不会显示为正方形。我使用 warpPerspective 制作了 post-it 注释方形。此图中仅显示了其中的几个(还有更多不适合):