opencv 查找numpy数组的左下角索引,该数组满足给定大小内的所有元素都等于某个值

vwoqyblh  于 2022-12-19  发布在  其他
关注(0)|答案(1)|浏览(137)

我有一个3D numpy数组(图像),是在白色背景上绘制的opencv轮廓线的结果。基本上图像值是0(黑线)或255(白色背景)。
我想把一些文字在这个补丁,我知道边界框的文字大小。
现在我需要在图像中找到一个大小等于或大于边界框文本大小的部分,其中图像中的该部分是全白色的(在三个RGB通道中的值是255)。找到该部分后,我需要它的左下角索引,以便使用它放置文本。
谢啦,谢啦

jxct1oxe

jxct1oxe1#

我设法解决了它。下面是大量注解的代码,以便于理解。这个问题的核心答案是:

white_patches = np.argwhere(np.lib.stride_tricks.sliding_window_view(patch,(txt_h,txt_w)).all(axis=(-2,-1)))

上面的代码行获取大小为(txt_h,txt_w)的图像中每个窗口的左上角索引,其中窗口中的所有元素都不为零。

完整代码

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

相关问题