如果模板匹配正在爲您工作,請堅持下去。舉例來說,我認爲下面的模板:
然後,我們可以預處理,以使之成爲一個二進制文件和丟棄的小部件的輸入。在此步驟之後,執行模板匹配。然後就是通過丟棄關鍵字來過濾比賽(我使用了虛擬方法,所以如果比賽太多,你可能會花費一些時間)。在我們確定哪些點相距很遠(從而識別不同的六邊形)之後,我們可以通過以下方式對它們進行微調:
- 按y座標排序;
- 如果兩個相鄰的項目在太接近的y座標處開始,則將它們都設置爲相同的y座標。
現在,您可以按適當的順序對這個點列表進行排序,以便按照光柵順序完成這些作物。使用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)
如果你只想要六邊形的輪廓,然後再剪裁上img_clean
而不是img
(但當時是沒有意義的光柵順序六邊形排序)。
這裏是一個將被削減爲你的兩個例子,而無需修改上面的代碼不同區域的表示:
非常感謝您的回答!我得到以下錯誤:第23行,在 _,l [i] = cv2.threshold(cv2.COLt_BGR2GRAY), 錯誤:/ home/abuild/rpmbuild/BUILD/OpenCV -2.4.1/modules/imgproc/src/color.cpp:3205:error:(-215)scn == 3 || scn == 4在函數cvtColor中,我真的不知道如何解決它...你對什麼問題有什麼想法嗎? –
2013-02-16 17:18:49
@AlbertVonpupp是因爲模板或輸入已經有單個通道而引起的。如果你的模板圖像只有一個通道並且已經是二進制的,你可以添加在'_,l [i] = cv2.threshold(...)'之後有一個'break'語句,這樣只會處理輸入,在這種情況下,你可以重寫那個部分來消除'for'循環。 – mmgp 2013-02-16 18:04:42
這是一個愚蠢的錯誤,我使用了一個錯誤的文件名。我修復並在第50行(在第一個循環之後)用一個斷點運行它。 e ptlist應該有些東西,但是是空的。如果我將閾值設置爲0.95,它似乎只適用於第二列八角形。當我用這個圖像運行它時https://github.com/vonpupp/opencv-learning/blob/master/test01.jpeg它不會產生threshold = 0.99的任何輸出,並且它似乎與閾值= 0.95一起掛起。我將在稍後將代碼上傳到git倉庫併發布鏈接。 – 2013-02-16 18:40:09