我正在学习cv2,并尝试从2048 game(动态)检测板载数字磁贴,并以绿色勾勒出轮廓。
首先,我在检测橙色到红色(8、16、32、64)范围内的像素时遇到了麻烦,如果我降低阈值,整个板似乎都包括在内。有时,较小的部分(包括6的圆形部分)或整个图块将被忽略。我将如何检测像这样的板上的瓷砖?
这是我到目前为止的代码:
import cv2
import mss
import time
import numpy as np
# Static screenshot for board
monitor = {"top": 135, "left": 425, "width": 500, "height": 500}
sct = mss.mss()
# Run for a maximum of 150s or until 'q' is pressed
last_time = time.time()
while time.time() - last_time < 150:
img = np.asarray(sct.grab(monitor))
resized_img = cv2.resize(img, (100, 100))
gray_img = cv2.cvtColor(resized_img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray_img, 200, 255, 0)[1]
contours = cv2.findContours(thresh, 1, 2)[1]
for cnt in contours:
if len(cnt) == 4:
cv2.drawContours(resized_img, [cnt], 0, (0, 255, 0), 2)
cv2.imshow("2048", resized_img)
if cv2.waitKey(25) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
样本检测:
编辑:根据要求添加示例输入
样本输入:
示例输出:
感谢您的任何答复,即使它们是正确方向的一个要点
答案 0 :(得分:2)
您可以使用cv2.inRange
对彩色图像进行阈值处理,而不是对灰度图像进行阈值处理。您可以设置允许的颜色的上限和下限,以包括编号的图块,但排除空的图块和边缘。
此外,我假设您正在检查if len(cnt) == 4:
的步骤是仅返回正方形轮廓。但是,调整大小可能会导致轮廓与瓷砖的正方形不完全相同,并且不会通过此检查。相反,您可以通过将findContours
的第二个输入更改为0(contours = cv2.findContours(thresh, 0, 2)[1]
)来获得图块的外部轮廓,这会将检索模式设置为cv2.RETR_EXTERNAL
。
下面是进行了更改的代码,并为您提供的示例图像设置了适当的上下色边界。
import cv2
import mss
import time
import numpy as np
# Static screenshot for board
monitor = {"top": 135, "left": 425, "width": 500, "height": 500}
sct = mss.mss()
# inRange bounds
lower_bound = (0, 0, 210)
upper_bound = (230, 240, 250)
# Run for a maximum of 150s or until 'q' is pressed
last_time = time.time()
while time.time() - last_time < 150:
img = np.asarray(sct.grab(monitor))[:,:,:3]
resized_img = cv2.resize(img, (100, 100))
mask = cv2.inRange(resized_img, lower_bound, upper_bound)
contours = cv2.findContours(mask, 0, 2)[1]
for cnt in contours:
cv2.drawContours(resized_img, [cnt], 0, (0, 255, 0), 2)
cv2.imshow("2048", resized_img)
if cv2.waitKey(25) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
以下是创建的输出图像:
编辑:以下是直接使用示例图片的代码:
import cv2
import numpy as np
img = cv2.imread('2048.jpg')
resized_img = cv2.resize(img, (100, 100))
lower_bound = (0,0,210)
upper_bound = (230,240,250)
mask = cv2.inRange(resized_img, lower_bound, upper_bound)
contours = cv2.findContours(mask, 0, 2)[1]
for cnt in contours:
cv2.drawContours(resized_img, [cnt], 0, (0, 255, 0), 2)
cv2.imshow('2048', resized_img)
cv2.waitKey(0)
答案 1 :(得分:0)
您可以采取的一种方法是,从所有图块都为空的快照中获取该帧的差异。这将以最少的计算为您提供所需的蒙版。
由于您没有在问题中提到可以访问所有空图块快照,因此我还将介绍另一种称为color segmentation
的技术。由于背景颜色是一致的,但是编号的颜色块会发生变化,因此我们将首先分割出背景,然后反转遮罩以找到轮廓:
import cv2
import numpy as np
def threshold_tiles(board_img):
board_bgd_color_low = np.array([155, 170, 140])
board_bgd_color_high = np.array([200, 185, 195])
board_empty_low = np.array([175, 180, 200])
board_empty_high = np.array([185, 195, 210])
mask_bgd = cv2.inRange(board_img, board_bgd_color_low, board_bgd_color_high)
mask_tile = cv2.inRange(board_img, board_empty_low, board_empty_high)
mask = cv2.max(mask_bgd, mask_tile)
kernel = np.ones((7, 7), np.uint8)
mask = cv2.dilate(mask, kernel)
return ~mask
def get_box_contours(mask):
_, cnt, hierarchy = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnt = filter(lambda x:cv2.contourArea(x) > 100, cnt)
return cnt
def main():
game_snapshot = cv2.imread("/path/to/img.jpg")
# Crop the white borders
game_snapshot = game_snapshot[5:-5, 5:-5]
mask = threshold_tiles(game_snapshot)
contours = get_box_contours(mask)
for i in xrange(len(contours)):
cv2.drawContours(game_snapshot, contours, i, (0, 255, 0), 3)
cv2.imwrite("output.png", game_snapshot)
if __name__ == "__main__":
main()
输出: