обнаружение плиточных квадратов в 2048 году с помощью cv2 - PullRequest
0 голосов
/ 23 ноября 2018

Я изучаю cv2 и пытаюсь обнаружить бортовые номера (динамически) из игры 2048 , и выделяю их зеленым цветом.

Во-первых, у меня возникают проблемы с обнаружениемте, которые находятся в диапазоне от оранжевого до красного (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()

определение образца:

enter image description here

РЕДАКТИРОВАТЬ: добавлен выборочный ввод по запросу

выборочный ввод:

enter image description here

выборкавывод:

enter image description here

Спасибо за любые ответы, даже если они являются точкой в ​​правильном направлении

Ответы [ 2 ]

0 голосов
/ 23 ноября 2018

Один из подходов, который вы можете предпринять, - это взять разность этого кадра со снимка, где все плитки пусты.Это даст вам необходимую маску с минимальными вычислениями.

Поскольку вы не упомянули в вопросе, что у вас есть доступ ко всем пустым снимкам тайла, я бы также представил другую технику, известную как 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()

Промежуточные маски:

enter image description here + enter image description here = enter image description here

Окончательная маска для контуров:

enter image description here

Выход:

enter image description here

0 голосов
/ 23 ноября 2018

Вместо выполнения установки порогового значения для изображения в градациях серого можно выполнить настройку порогового значения для цветного изображения с помощью 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()

Вот созданное выходное изображение:

Contoured 2048

Редактировать: Вот код для непосредственного использования примера изображения:

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)
...