def CBN(img, colors):
canvas = np.ones((img.shape[0],img.shape[1],img.shape[2]),dtype='uint8') * 255 #used to draw the final CBN image
#used to draw a negative (black) of the contour to exclude areas not suitable to place text of next contour.
negative = np.ones((img.shape[0],img.shape[1]),dtype='uint8') * 255
#release contours from its hierarchy and have it as an unnested list of contours
contours = []
for ind, color in enumerate(colors):
color = np.asarray(color, dtype='uint8')
mask = cv2.inRange(img, color, color)
cnts,hier = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = [{'cnt':cnt,'ind':ind+1} for cnt in cnts if
cv2.boundingRect(cnt)[2]>10
and cv2.boundingRect(cnt)[3]>10
and cv2.contourArea(cnt,False)>100]
contours.extend(c)
contours = sorted(contours, key= lambda x:cv2.contourArea(x['cnt'],False), reverse=False) #arcLength can also be used
txts = tuple([str(x['ind']) for x in contours]) #texts to be added
contours = tuple([x['cnt'] for x in contours]) #contours to be drawn
for i, cnt in enumerate(contours):
cv2.drawContours(canvas,[cnt],-1,0,thickness=1)
#identify suitable place to put text
cnt_x, cnt_y, cnt_w, cnt_h = cv2.boundingRect(cnt)
patch = negative[cnt_y:cnt_y+cnt_h, cnt_x:cnt_x+cnt_w, :] #get a patch from the negative
font_scale=1
flag = True
while flag:
if font_scale >0.5: #try to find a suitable place to put the text with font scale from 1 to 0.5
txt_w, txt_h = cv2.getTextSize(txts[i], cv2.FONT_HERSHEY_SIMPLEX, font_scale, 1)[0] #get the text size in w x h
if patch.shape[0]>txt_h and patch.shape[1]>txt_w: #check patch is bigger than text
#select indices that when considered as a top-left coordinate for text result in complete white box that is inside the contour
white_patches = np.argwhere(np.lib.stride_tricks.sliding_window_view(patch,(txt_h,txt_w)).all(axis=(-2,-1)))
white_patches = white_patches.tolist()
white_patches = [x for x in white_patches if
cv2.pointPolygonTest(cnt, (x[1]+cnt_x,x[0]+cnt_y), False)>0 #TL of text in contour
and cv2.pointPolygonTest(cnt, (x[1]+cnt_x+txt_w,x[0]+cnt_y), False)>0 #TR of text in contour
and cv2.pointPolygonTest(cnt, (x[1]+cnt_x+txt_w,x[0]+cnt_y+txt_h), False)>0 #BR of text in contour
and cv2.pointPolygonTest(cnt, (x[1]+cnt_x,x[0]+cnt_y+txt_h), False)>0 ] #BL of text in contour
if len(white_patches)>0: # if there are top-left coordinates found, use the first coordinate (any one can be as good) to place text
txt_x = white_patches[0][1]+cnt_x
txt_y = white_patches[0][0]+cnt_y+txt_h
cv2.putText(canvas, txts[i], (txt_x, txt_y), cv2.FONT_HERSHEY_SIMPLEX, font_scale, 0, 1)
flag = False
else: #no top-left coordinates found, decrease font scale and try again
font_scale -=0.1
else: #patch is smaller than text, decrease font and try again
font_scale -=0.1
else: #we reached minimum possible font size. Place text at centroid of contour
M = cv2.moments(cnt) #use contour centroid
txt_x = int(M["m10"] / M['m00'])
txt_y = int(M["m01"] / M['m00'])
cv2.putText(canvas, txts[i], (txt_x, txt_y), cv2.FONT_HERSHEY_SIMPLEX, 0.4, 0, 1)
flag= False
return canvas
1条答案
按热度按时间jxct1oxe1#
我设法解决了它。下面是大量注解的代码,以便于理解。这个问题的核心答案是:
上面的代码行获取大小为
(txt_h,txt_w)
的图像中每个窗口的左上角索引,其中窗口中的所有元素都不为零。完整代码