opencv:分割透明边框

时间:2017-03-30 11:12:44

标签: opencv computer-vision image-segmentation

我试图在游戏画面中找到活动对象,如下所示:

enter image description here

活跃意味着它们有一个灰白色的边框,所以这里左上方的方块和中间的五张卡片。

这首先看起来很简单但是边框是半透明的和渐变的,所以实际的灰度值很大程度上取决于背景,范围从~18​​0到240.只需inRange()所有这些值产生很多噪音。这里是边界的特写,供参考:

enter image description here

然后我尝试使用每个边缘的一个模板进行模板匹配,例如:对于右边缘,我采用了一堆黑色边框像素和旁边的4个灰色像素的渐变,例如。

enter image description here

然后我在模板匹配结果上添加阈值,它有点工作:

    k = ['right', 'left', 'top', 'bottom']
    mode = cv2.TM_CCOEFF_NORMED
    matches = {}
    addimg = []

    for side in k:
        template = cv2.imread('./img/ab_' + side + '.png', cv2.IMREAD_GRAYSCALE)
        matches[side] = cv2.matchTemplate(im0, template, mode)
        v  = cv2.inRange(matches[side], 0.987, 1)
        #Tools.show(side, v)
        addimg.append(v)

    im1 = sum(addimg)

enter image description here

但是,为TM系数获得正确的价值仍然很有意义。此外,当对象较大时,边框渐变比我在模板中使用的灰色像素宽,因此匹配变得更糟。

总而言之,我认为我错过了一个可以匹配不同大小和强度的渐变的智能算法。有什么好主意吗?

PS还有https://github.com/rc9000/modoscrape/tree/master/img

中的更多屏幕截图

1 个答案:

答案 0 :(得分:2)

好的,这是我的两分钱。这与梯度检测无关,而是如何检测这些卡的另一种想法 我认为你对如何检测有效卡片的唯一线索就是这个边界。当然,你可以尝试检测渐变和东西,但我的解决方案依赖于这样的事实 a /边界与图像的其余部分明显可分离(作为一个组成部分),带有一个简单的" inRange()" [在Piglet的评论之后编辑:这也可以工作 - 并且可能更容易 - 使用黑色边框而不是渐变] b /边框具有特定形状,尤其是围绕它的边界矩形将是直的并具有特定比例。我的意思是,既然你总是选择一张扑克牌,它的高/宽比总是一样的。

所以我的想法是 1 /门槛
enter image description here
2 /找到组件
3 /找到这些组件的边界矩形
enter image description here
4 /仅选择具有特定比例的边界矩形 enter image description here

代码如下。它有点快速和肮脏"有些东西可能会被优化。例如,我没有检查矩形方向,这是一个很好的线索。此外,即使卡片的图像因图像而异,您也可能对卡片的尺寸有所了解。此外,你可以消除其他矩形内的矩形,或明显小于其他矩形的矩形......

将此作为"另一种方式"探索,而不是一个交钥匙解决方案:)

import cv2
import sys
import numpy as np
import csv

#just converting formats of numpy arrays to pass it from one cv2 function to another.
def convert_for_bounding(coords):
    nb_pts=len(coords[0])
    coordz=np.zeros((nb_pts,2))
    for i in range(nb_pts):
        coordz[i,:]=np.array([int(coords[0][i]),int(coords[1][i])])
    return coordz

#finding width and length of bounding boxes
def find_wid(xs):
    maxx=0
    for i in range(4):
        for j in range(i+1,4):
            if abs(xs[i]-xs[j])>=maxx:
                maxx=abs(xs[i]-xs[j])
    return maxx


img=cv2.imread(your image)
orig=np.copy(img)
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
h,w=img.shape

#thresholding with your "180 - 240" range
img = cv2.inRange(img, 180, 240)

#finding all components
nb_edges, output, stats, centroids = cv2.connectedComponentsWithStats(img, connectivity=8)
size_edges = stats[1:, -1]; nb_edges = nb_edges - 1
contours=[]
for i in range(0, nb_edges):
    #eliminating small components
    if size_edges[i]>=100:
        img2=np.zeros((h,w))
        img2[output == i + 1] = 255
        contours.append(convert_for_bounding(np.nonzero(img2)))



#finding bounding rectangle for each component
for i in range(0,len(contours)):
    c=np.array(contours[i]).astype(int)
    ar=cv2.minAreaRect(c)
    box = cv2.boxPoints(ar)
    box = np.int0([box[:,1],box[:,0]]).T
    xs=box[:,0]
    ys=box[:,1]
    wid=find_wid(xs)
    hei=find_wid(ys)

    #for each rectangle, we'll check if its ratio is like a card one
    card_ratio = 285 / 205
    if hei!=0:
        if hei/wid <=card_ratio*1.05 and hei/wid >= card_ratio*0.95:
            cv2.drawContours(orig, [box], -1, (0,0,255), 2)

结果(在此答案中不得不缩小上传内容): enter image description here