简体   繁体   English

在opencv和python中复制图像的一部分

[英]Copy a part of an image in opencv and python

I'm trying to split an image into several sub-images with opencv by identifying templates of the original image and then copy the regions where I matched those templates. 我正在尝试使用opencv通过识别原始图像的模板将图像分为几个子图像,然后复制与这些模板匹配的区域。 I'm a TOTAL newbie to opencv! 我是opencv的新手! I've identified the sub-images using: 我使用以下方法确定了子图像:

result = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED)

After some cleanup I get a list of tuples called points in which I iterate to show the rectangles. 经过一些清理后,我得到了一个称为点的元组列表,在其中迭代显示矩形。 tw and th is the template width and height respectively. tw和th分别是模板的宽度和高度。

for pt in points:
    re = cv2.rectangle(img, pt, (pt[0] + tw, pt[1] + th), 0, 2)
    print('%s, %s' % (str(pt[0]), str(pt[1])))
    count+=1

What I would like to accomplish is to save the octagons ( https://dl.dropbox.com/u/239592/region01.png ) into separated files. 我要完成的工作是将八边形( https://dl.dropbox.com/u/239592/region01.png )保存到单独的文件中。

How can I do this? 我怎样才能做到这一点? I've read something about contours but I'm not sure how to use it. 我已经读过一些有关轮廓的信息,但是我不确定如何使用它。 Ideally I would like to contour the octagon. 理想情况下,我想绘制八边形轮廓。

Thanks a lot for your help! 非常感谢你的帮助!

If template matching is working for you, stick to it. 如果模板匹配对您有用,请坚持下去。 For instance, I considered the following template: 例如,我考虑了以下模板:

在此处输入图片说明

Then, we can pre-process the input in order to make it a binary one and discard small components. 然后,我们可以对输入进行预处理,以使其成为二进制输入并丢弃小的组件。 After this step, the template matching is performed. 在此步骤之后,将执行模板匹配。 Then it is a matter of filtering the matches by means of discarding close ones (I've used a dummy method for that, so if there are too many matches you could see it taking some time). 然后是通过丢弃接近的匹配项来过滤匹配项的问题(为此,我使用了一种伪方法,因此,如果匹配项太多,您可能会花一些时间)。 After we decide which points are far apart (and thus identify different hexagons), we can do minor adjusts to them in the following manner: 在确定了哪些点相距很远(从而确定了不同的六边形)之后,我们可以通过以下方式对其进行较小的调整:

  • Sort by y-coordinate; 按y坐标排序;
  • If two adjacent items start at a y-coordinate that is too close, then set them both to the same y-coord. 如果两个相邻项的y坐标太近,则将它们都设置为相同的y坐标。

Now you can sort this point list in an appropriate order such that the crops are done in raster order. 现在,您可以按适当的顺序对该点列表进行排序,以便以栅格顺序进行裁剪。 The cropping part is easily achieved using slicing provided by numpy . 使用numpy提供的切片可以轻松实现裁剪部分。

import sys
import cv2
import numpy

outbasename = 'hexagon_%02d.png'

img = cv2.imread(sys.argv[1])
template = cv2.cvtColor(cv2.imread(sys.argv[2]), cv2.COLOR_BGR2GRAY)
theight, twidth = template.shape[:2]

# Binarize the input based on the saturation and value.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
saturation = hsv[:,:,1]
value = hsv[:,:,2]
value[saturation > 35] = 255
value = cv2.threshold(value, 0, 255, cv2.THRESH_OTSU)[1]
# Pad the image.
value = cv2.copyMakeBorder(255 - value, 3, 3, 3, 3, cv2.BORDER_CONSTANT, value=0)

# Discard small components.
img_clean = numpy.zeros(value.shape, dtype=numpy.uint8)
contours, _ = cv2.findContours(value, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
for i, c in enumerate(contours):
    area = cv2.contourArea(c)
    if area > 500:
        cv2.drawContours(img_clean, contours, i, 255, 2)


def closest_pt(a, pt):
    if not len(a):
        return (float('inf'), float('inf'))
    d = a - pt
    return a[numpy.argmin((d * d).sum(1))]

match = cv2.matchTemplate(img_clean, template, cv2.TM_CCORR_NORMED)

# Filter matches.
threshold = 0.8
dist_threshold = twidth / 1.5
loc = numpy.where(match > threshold)
ptlist = numpy.zeros((len(loc[0]), 2), dtype=int)
count = 0
print "%d matches" % len(loc[0])
for pt in zip(*loc[::-1]):
    cpt = closest_pt(ptlist[:count], pt)
    dist = ((cpt[0] - pt[0]) ** 2 + (cpt[1] - pt[1]) ** 2) ** 0.5
    if dist > dist_threshold:
        ptlist[count] = pt
        count += 1

# Adjust points (could do for the x coords too).
ptlist = ptlist[:count]
view = ptlist.ravel().view([('x', int), ('y', int)])
view.sort(order=['y', 'x'])
for i in xrange(1, ptlist.shape[0]):
    prev, curr = ptlist[i - 1], ptlist[i]
    if abs(curr[1] - prev[1]) < 5:
        y = min(curr[1], prev[1])
        curr[1], prev[1] = y, y

# Crop in raster order.
view.sort(order=['y', 'x'])
for i, pt in enumerate(ptlist, start=1):
    cv2.imwrite(outbasename % i,
            img[pt[1]-2:pt[1]+theight-2, pt[0]-2:pt[0]+twidth-2])
    print 'Wrote %s' % (outbasename % i)

If you want only the contours of the hexagons, then crop on img_clean instead of img (but then it is pointless to sort the hexagons in raster order). 如果只需要六边形的轮廓,则在img_clean而不是img上进行裁剪(但是按栅格顺序对六边形进行排序是没有意义的)。

Here is a representation of the different regions that would be cut for your two examples without modifying the code above: 这是两个示例在不修改上面的代码的情况下将被剪切的不同区域的表示:

在此处输入图片说明在此处输入图片说明

I am sorry, I didn't understand from your question on how do you relate matchTemplate and Contours. 抱歉,我对您如何将matchTemplate和Contours联系起来不甚了解。

Anyway, below is a small technique using contours. 无论如何,下面是使用轮廓的小技巧。 It is on the assumption that your other images are also like the one you provided. 假设您的其他图像也与您提供的图像相同。 I am not sure if it works with your other images. 我不确定它是否可以与您的其他图像一起使用。 But I think it would help to get a startup. 但是我认为这将对初创公司有所帮助。 Try this yourself and make necessary adjustments and modifications. 自己尝试一下,并进行必要的调整和修改。

What I did : 我做了什么 :

1 - I needed the edge of octagons . 1-我需要八边形的边缘。 So Thresholded Image using Otsu and apply dilation and erosion (or use any method you like that works well for all your images, beware of the edges in left edge of image ). 因此,请使用Otsu进行“阈值图像”处理并应用散布和腐蚀(或使用您喜欢的任何方法都适用于所有图像, beware of the edges in left edge of image图像beware of the edges in left edge of image )。

2 - Then found contours (More about contours : http://goo.gl/r0ID0 2-然后找到轮廓(有关轮廓的更多信息: http : //goo.gl/r0ID0

3 - For each contours, find its convex hull, find its area(A) & perimeter(P) 3-对于每个轮廓,找到其凸包,找到其面积(A)和周长(P)

4 - For a perfect octagon , P*P/A = 13.25 approximately . 4-对于完美的八边形P*P/A = 13.25 approximately I used it here and cut it and saved it. 我在这里使用它并剪切并保存。

5 - You can see cropping it also removes some edges of octagon. 5-您可以看到裁剪它也会删除八边形的某些边缘。 If you want it, adjust the cropping dimension. 如果需要,请调整裁剪尺寸。

Code : 代码:

import cv2
import numpy as np

img = cv2.imread('region01.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
thresh = cv2.dilate(thresh,None,iterations = 2)
thresh = cv2.erode(thresh,None)

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
number = 0

for cnt in contours:
    hull = cv2.convexHull(cnt)
    area = cv2.contourArea(hull)
    P = cv2.arcLength(hull,True)

    if ((area != 0) and (13<= P**2/area <= 14)):
        #cv2.drawContours(img,[hull],0,255,3)
        x,y,w,h = cv2.boundingRect(hull)
        number = number + 1
        roi = img[y:y+h,x:x+w]
        cv2.imshow(str(number),roi)
        cv2.imwrite("1"+str(number)+".jpg",roi)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Those 6 octagons will be stored as separate files. 那六个八边形将作为单独的文件存储。

Hope it helps !!! 希望能帮助到你 !!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM