我有几张要使用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的初学者,我还没有找到在函数“查找平方”中给出更多条件的方法,以便在例程结束时仅获得15个平方。也许轮廓区域可以最大化?
我还发现there的代码更详细(非常接近先前的代码),但是它似乎是在旧版本的Opencv中开发的。我没有设法使其正常工作(因此无法对其进行修改)。
答案 0 :(得分:1)
我使用此代码在图像中找到轮廓(完整代码可在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())
便利贴为正方形,但由于相机扭曲图像中的对象,因此它们不会显示为正方形。我使用warpPerspective使便签纸成为正方形。此图中仅显示了其中的一些(还有更多不适合的显示):
答案 1 :(得分:0)
如果您的问题是在图像中发现太多的轮廓(边缘),我的建议是先修改边缘查找部分。到目前为止,这将是最简单的修改。
尤其是,您需要更改此呼叫:
bin = cv.Canny(gray, 0, 50, apertureSize=5)
cv.Canny()
函数将两个阈值,孔径大小和布尔值作为参数,以指示是否使用了精确的渐变形式。使用这些参数,我的猜测是,您将获得更好的结果。