我正在使用的图片:
我正在尝试查找此图片中的每个框。只要找到的方框在位置/大小上大致正确,结果就不必100%准确。通过玩方形检测的例子,我设法得到轮廓,边界框,角落和盒子的中心。
我遇到了一些问题:
代码生成的图像:
以下是我用来生成上图的代码:
import numpy as np
import cv2
from operator import itemgetter
from glob import glob
def angle_cos(p0, p1, p2):
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 makebin(gray):
bin = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 2)
return cv2.bitwise_not(bin)
def find_squares(img):
img = cv2.GaussianBlur(img, (11, 11), 0)
squares = []
points = []`
for gray in cv2.split(img):
bin = makebin(gray)
contours, hierarchy = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
corners = cv2.goodFeaturesToTrack(gray,len(contours)*4,0.2,15)
cv2.cornerSubPix(gray,corners,(6,6),(-1,-1),(cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS,10, 0.1))
for cnt in contours:
cnt_len = cv2.arcLength(cnt, True)
if len(cnt) >= 4 and cv2.contourArea(cnt) > 200:
rect = cv2.boundingRect(cnt)
if rect not in squares:
squares.append(rect)
return squares, corners, contours
if __name__ == '__main__':
for fn in glob('../1 (Small).jpg'):
img = cv2.imread(fn)
squares, corners, contours = find_squares(img)
for p in corners:
cv2.circle(img, (p[0][0],p[0][3]), 3, (0,0,255),2)
squares = sorted(squares,key=itemgetter(1,0,2,3))
areas = []
moments = []
centers = []
for s in squares:
areas.append(s[2]*s[3])
cv2.rectangle( img, (s[0],s[1]),(s[0]+s[2],s[1]+s[3]),(0,255,0),1)
for c in contours:
moments.append(cv2.moments(np.array(c)))
for m in moments:
centers.append((int(m["m10"] // m["m00"]), int(m["m01"] // m["m00"])))
for cent in centers:
print cent
cv2.circle(img, (cent[0],cent[1]), 3, (0,255,0),2)
cv2.imshow('squares', img)
ch = 0xFF & cv2.waitKey()
if ch == 27:
break
cv2.destroyAllWindows()
答案 0 :(得分:17)
我建议采用更简单的方法作为起点。例如,形态梯度可以作为强边缘的良好局部检测器,并且其上的阈值趋于简单。然后,您可以删除太小的组件,这对您的问题也相对容易。在您的示例中,每个剩余的连接组件都是单个框,因此在此实例中解决了问题。
通过这个简单的程序,您将获得以下内容:
红点代表组件的质心,所以你可以从那里生长另一个盒子,如果黄色的盒子对你不好的话,它会包含在黄色的那个盒子里。
以下是实现这一目标的代码:
import sys
import numpy
from PIL import Image, ImageOps, ImageDraw
from scipy.ndimage import morphology, label
def boxes(orig):
img = ImageOps.grayscale(orig)
im = numpy.array(img)
# Inner morphological gradient.
im = morphology.grey_dilation(im, (3, 3)) - im
# Binarize.
mean, std = im.mean(), im.std()
t = mean + std
im[im < t] = 0
im[im >= t] = 1
# Connected components.
lbl, numcc = label(im)
# Size threshold.
min_size = 200 # pixels
box = []
for i in range(1, numcc + 1):
py, px = numpy.nonzero(lbl == i)
if len(py) < min_size:
im[lbl == i] = 0
continue
xmin, xmax, ymin, ymax = px.min(), px.max(), py.min(), py.max()
# Four corners and centroid.
box.append([
[(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)],
(numpy.mean(px), numpy.mean(py))])
return im.astype(numpy.uint8) * 255, box
orig = Image.open(sys.argv[1])
im, box = boxes(orig)
# Boxes found.
Image.fromarray(im).save(sys.argv[2])
# Draw perfect rectangles and the component centroid.
img = Image.fromarray(im)
visual = img.convert('RGB')
draw = ImageDraw.Draw(visual)
for b, centroid in box:
draw.line(b + [b[0]], fill='yellow')
cx, cy = centroid
draw.ellipse((cx - 2, cy - 2, cx + 2, cy + 2), fill='red')
visual.save(sys.argv[3])
答案 1 :(得分:13)
我知道你已经得到了答案。但我认为OpenCV中有一个更简单,更短,更好的方法来解决这个问题。
在查找轮廓时,您还可以找到轮廓的层次结构。轮廓的层次结构是不同轮廓之间的关系。
因此,您在代码中使用的标志cv2.RETR_TREE
提供了所有层次关系。
cv2.RETR_LIST
不提供层次结构,而cv2.RETR_EXTERNAL
只提供外部轮廓。
最适合你的是cv2.RETR_CCOMP
,它为你提供了所有轮廓,以及两级层次关系。即外轮廓始终为父轮廓,内孔轮廓始终为子轮廓。
有关层次结构的更多信息,请阅读以下文章:Contour - 5 : Hierarchy
因此,轮廓的层次结构是一个4元素数组,其中最后一个元素是指向其父元素的索引指针。 If a contour has no parent, it is external contour and it has a value -1
。如果它是一个内部轮廓,它是一个孩子,它将有一些指向其父级的值。我们将在您的问题中利用此功能。
import cv2
import numpy as np
# Normal routines
img = cv2.imread('square.JPG')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,50,255,1)
# Remove some small noise if any.
dilate = cv2.dilate(thresh,None)
erode = cv2.erode(dilate,None)
# Find contours with cv2.RETR_CCOMP
contours,hierarchy = cv2.findContours(erode,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
for i,cnt in enumerate(contours):
# Check if it is an external contour and its area is more than 100
if hierarchy[0,i,3] == -1 and cv2.contourArea(cnt)>100:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
m = cv2.moments(cnt)
cx,cy = m['m10']/m['m00'],m['m01']/m['m00']
cv2.circle(img,(int(cx),int(cy)),3,255,-1)
cv2.imshow('img',img)
cv2.imwrite('sofsqure.png',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
答案 2 :(得分:0)
此问题与python image recognition有关。 squres.py demo中给出了解决方案。