opencv:分割透明边框
opencv: Segmenting a transparent border
我正在尝试在这样的游戏屏幕中查找活动对象:
其中active表示它们有一个灰白色的边框,所以这里是左上角的方块和中间的五张牌。
乍一看这看起来很简单,但边框是半透明的渐变色,所以实际的灰度值在很大程度上取决于背景,范围从 ~180 到 240。只要在 Range() 中输入所有这些值就会产生一个很多噪音。边框特写供参考:
然后我尝试了模板匹配,每条边使用一个模板,例如对于右边缘,我采用了一堆黑色边框像素和旁边的 4 个灰色像素的渐变,例如
然后我在模板匹配结果上添加阈值,它有点管用:
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)
但要获得正确的 TM 系数值仍然很困难。此外,当对象更大时,边框渐变比我在模板中使用的灰色像素更宽,因此匹配变得更糟。
所以总而言之,我认为我缺少一种可以匹配不同大小和强度的梯度的智能算法。有什么好的想法吗?
中还有更多此类屏幕截图
好的,这是我的两分钱。这与梯度检测无关,而是关于如何检测这些卡片的另一种想法。
我认为您关于如何检测活动卡的唯一线索就是这个边界。当然,您可以尝试检测梯度和其他东西,但我的解决方案依赖于
a/ 边框可以通过简单的 "inRange()" 与图像的其余部分清楚地分开(作为一个组件)边框而不是渐变]
b/ 边框有一个特定的形状,特别是它周围的边界矩形应该是直的并且有 特定的比例 。我的意思是,由于您总是 select 玩扑克牌,它的 height/width 比率将始终相同。
所以我的想法是
1/ 阈值
2/ 查找组件
3/ 找到这些组件的边界矩形
4/ select 仅具有特定比例的边界矩形
代码如下。有点像 "quick and dirty",有些东西可能会被优化。例如,我没有检查矩形方向,这是一个很好的线索。此外,您可能对卡片的大小有所了解,即使它可能因一张图片而异。此外,您可以消除其他矩形内的矩形,或明显小于其他矩形的矩形...
将此作为 "another way" 探索,而不是交钥匙解决方案:)
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)
结果(必须缩小尺寸才能在此答案中上传):
我正在尝试在这样的游戏屏幕中查找活动对象:
其中active表示它们有一个灰白色的边框,所以这里是左上角的方块和中间的五张牌。
乍一看这看起来很简单,但边框是半透明的渐变色,所以实际的灰度值在很大程度上取决于背景,范围从 ~180 到 240。只要在 Range() 中输入所有这些值就会产生一个很多噪音。边框特写供参考:
然后我尝试了模板匹配,每条边使用一个模板,例如对于右边缘,我采用了一堆黑色边框像素和旁边的 4 个灰色像素的渐变,例如
然后我在模板匹配结果上添加阈值,它有点管用:
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)
但要获得正确的 TM 系数值仍然很困难。此外,当对象更大时,边框渐变比我在模板中使用的灰色像素更宽,因此匹配变得更糟。
所以总而言之,我认为我缺少一种可以匹配不同大小和强度的梯度的智能算法。有什么好的想法吗?
中还有更多此类屏幕截图好的,这是我的两分钱。这与梯度检测无关,而是关于如何检测这些卡片的另一种想法。
我认为您关于如何检测活动卡的唯一线索就是这个边界。当然,您可以尝试检测梯度和其他东西,但我的解决方案依赖于
a/ 边框可以通过简单的 "inRange()" 与图像的其余部分清楚地分开(作为一个组件)边框而不是渐变]
b/ 边框有一个特定的形状,特别是它周围的边界矩形应该是直的并且有 特定的比例 。我的意思是,由于您总是 select 玩扑克牌,它的 height/width 比率将始终相同。
所以我的想法是
1/ 阈值
2/ 查找组件
3/ 找到这些组件的边界矩形
4/ select 仅具有特定比例的边界矩形
代码如下。有点像 "quick and dirty",有些东西可能会被优化。例如,我没有检查矩形方向,这是一个很好的线索。此外,您可能对卡片的大小有所了解,即使它可能因一张图片而异。此外,您可以消除其他矩形内的矩形,或明显小于其他矩形的矩形...
将此作为 "another way" 探索,而不是交钥匙解决方案:)
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)
结果(必须缩小尺寸才能在此答案中上传):