
ç®æšãæ¢ããŠããŸã
ããŒãå¥ãµã€ãminiclip.comã«ã¢ã¯ã»ã¹ããŠãã¿ãŒã²ãããæ¢ããŸããéžæã¯ãããºã«ã»ã¯ã·ã§ã³ã®Coloruid 2ã«ã©ãŒããºã«ã«åœãŠã¯ãŸããŸããããã®ããºã«ã§ã¯ãäžããããåæ°ã®åãã§ãäžžãç«¶æå Žã1ã€ã®è²ã§åããå¿ èŠããããŸãã
ç»é¢äžéšã§éžæããè²ã§ä»»æã®é åãå¡ãã€ã¶ãããåãè²ã®é£æ¥ããé åã1ã€ã«çµ±åãããŸãã

ãã¬ãŒãã³ã°
Pythonã䜿çšããŸãããããã¯æè²ç®çã§ã®ã¿äœæãããŸããããã®èšäºã¯ãç§èªèº«ãã³ã³ãã¥ãŒã¿ããžã§ã³ã®åå¿è ã察象ãšããŠããŸãã
ã²ãŒã ã¯ããã«ãã
ãŸããããã®GitHubã¯ããã«ãããŸã
ããããæ©èœããã«ã¯ã次ã®ã¢ãžã¥ãŒã«ãå¿ èŠã§ãã
- opencv-python
- æ
- ã»ã¬ã³
ãããã¯ãUbuntu20.04.1äžã®Python3.8çšã«äœæããã³ãã¹ããããŠããŸããå¿ èŠãªã¢ãžã¥ãŒã«ãä»®æ³ç°å¢ã«ããŸãã¯pipinstallãä»ããŠã€ã³ã¹ããŒã«ããŸããããã«ãSeleniumãæ©èœãããã«ã¯ãFireFoxçšã®geckodriverãå¿ èŠã§ããgithub.com/ mozilla / geckodriver / releasesããããŠã³ããŒãã§ããŸãã
ãã©ãŠã¶å¶åŸ¡
ç§ãã¡ã¯ãªã³ã©ã€ã³ã²ãŒã ãæ±ã£ãŠããã®ã§ãæåã«ãã©ãŠã¶ãšã®çžäºäœçšãæŽçããŸãããã®ç®çã®ããã«ãFireFoxã管çããããã®APIãæäŸããSeleniumã䜿çšããŸããã²ãŒã ããŒãžã®ã³ãŒãã調ã¹ããããºã«ã¯ãã£ã³ãã¹ã§ãããiframeå ã«é 眮ãããŠããŸãã
id = iframe-gameã®ãã¬ãŒã ãããŒãããããã©ã€ããŒã³ã³ããã¹ããããã«åãæ¿ããã®ãåŸ ã¡ãŸããæ¬¡ã«ããã£ã³ãã¹ãåŸ ã¡ãŸããããã¯ãã¬ãŒã å ã§å¯äžã®ãã®ã§ãããXPath / html / body / canvasããå ¥æã§ããŸãã
wait(self.__driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "iframe-game")))
self.__canvas = wait(self.__driver, 20).until(EC.visibility_of_element_located((By.XPATH, "/html/body/canvas")))
次ã«ãself .__ canvasããããã£ãä»ããŠãã£ã³ãã¹ã䜿çšã§ããããã«ãªããŸãããã©ãŠã¶ãæäœããããã®ãã¹ãŠã®ããžãã¯ã¯ããã£ã³ãã¹ã®ã¹ã¯ãªãŒã³ã·ã§ãããæ®ããç¹å®ã®åº§æšã§ã¯ãªãã¯ããããšã§ãã
å®å šãªBrowser.pyã³ãŒãïŒ
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as wait
from selenium.webdriver.common.by import By
class Browser:
def __init__(self, game_url):
self.__driver = webdriver.Firefox()
self.__driver.get(game_url)
wait(self.__driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "iframe-game")))
self.__canvas = wait(self.__driver, 20).until(EC.visibility_of_element_located((By.XPATH, "/html/body/canvas")))
def screenshot(self):
return self.__canvas.screenshot_as_png
def quit(self):
self.__driver.quit()
def click(self, click_point):
action = webdriver.common.action_chains.ActionChains(self.__driver)
action.move_to_element_with_offset(self.__canvas, click_point[0], click_point[1]).click().perform()
ã²ãŒã ã®ç¶æ
ã²ãŒã èªäœã«åãæãããŸãããããã¹ãŠã®ãããããžãã¯ã¯Robotã¯ã©ã¹ã«å®è£ ãããŸããã²ãŒã ãã¬ã€ã7ã€ã®ç¶æ ã«åå²ããããããåŠçããããã®ã¡ãœãããå²ãåœãŠãŸãããããã¬ãŒãã³ã°ã¬ãã«ãåå¥ã«åŒ·èª¿ããŠã¿ãŸããããã¯ãªãã¯ããå Žæã瀺ã倧ããªçœãã«ãŒãœã«ãå«ãŸããŠãããããã²ãŒã ãæ£ããèªèãããŸããã
- ããããç»é¢
- ã¬ãã«éžæç»é¢
- ãã¥ãŒããªã¢ã«ã¬ãã«ã§ã®è²ã®éžæ
- æè²ã¬ãã«ã§ã®é åã®éžæ
- è²ã®éžæ
- å°åã®éžæ
- 移転ã®çµæ
class Robot:
STATE_START = 0x01
STATE_SELECT_LEVEL = 0x02
STATE_TRAINING_SELECT_COLOR = 0x03
STATE_TRAINING_SELECT_AREA = 0x04
STATE_GAME_SELECT_COLOR = 0x05
STATE_GAME_SELECT_AREA = 0x06
STATE_GAME_RESULT = 0x07
def __init__(self):
self.states = {
self.STATE_START: self.state_start,
self.STATE_SELECT_LEVEL: self.state_select_level,
self.STATE_TRAINING_SELECT_COLOR: self.state_training_select_color,
self.STATE_TRAINING_SELECT_AREA: self.state_training_select_area,
self.STATE_GAME_RESULT: self.state_game_result,
self.STATE_GAME_SELECT_COLOR: self.state_game_select_color,
self.STATE_GAME_SELECT_AREA: self.state_game_select_area,
}
ãããã®å®å®æ§ãé«ããããã«ãã²ãŒã ç¶æ ã®å€æŽãæ£åžžã«è¡ããããã©ããã確èªããŸããself.state_timeoutäžã«self.state_next_success_conditionãTrueãè¿ããªãå Žåã¯ãçŸåšã®ç¶æ ã®åŠçãç¶è¡ããŸãããã以å€ã®å Žåã¯ãself.state_nextã«åãæ¿ããŸãããŸããSeleniumããåãåã£ãã¹ã¯ãªãŒã³ã·ã§ãããOpenCVãçè§£ã§ãã圢åŒã«å€æããŸãã
import time
import cv2
import numpy
from PIL import Image
from io import BytesIO
class Robot:
def __init__(self):
# âŠ
self.screenshot = []
self.state_next_success_condition = None
self.state_start_time = 0
self.state_timeout = 0
self.state_current = 0
self.state_next = 0
def run(self, screenshot):
self.screenshot = cv2.cvtColor(numpy.array(Image.open(BytesIO(screenshot))), cv2.COLOR_BGR2RGB)
if self.state_current != self.state_next:
if self.state_next_success_condition():
self.set_state_current()
elif time.time() - self.state_start_time >= self.state_timeout
self.state_next = self.state_current
return False
else:
try:
return self.states[self.state_current]()
except KeyError:
self.__del__()
def set_state_current(self):
self.state_current = self.state_next
def set_state_next(self, state_next, state_next_success_condition, state_timeout):
self.state_next_success_condition = state_next_success_condition
self.state_start_time = time.time()
self.state_timeout = state_timeout
self.state_next = state_next
ç¶æ åŠçã¡ãœããã«ãã§ãã¯ãå®è£ ããŸããããã¹ã¿ãŒãç»é¢ã®ãåçããã¿ã³ãåŸ ã£ãŠã¯ãªãã¯ããŸãã10ç§ä»¥å ã«ã¬ãã«éžæç»é¢ã衚瀺ãããªãå Žåã¯ãåã®ã¹ããŒãžself.STATE_STARTã«æ»ããŸãããã以å€ã®å Žåã¯ãself.STATE_SELECT_LEVELã®åŠçã«é²ã¿ãŸãã
# âŠ
class Robot:
DEFAULT_STATE_TIMEOUT = 10
# âŠ
def state_start(self):
# Play
# âŠ
if button_play is False:
return False
self.set_state_next(self.STATE_SELECT_LEVEL, self.state_select_level_condition, self.DEFAULT_STATE_TIMEOUT)
return button_play
def state_select_level_condition(self):
#
# âŠ
ãããããžã§ã³
ç»åã®ãããå€
ã²ãŒã ã§äœ¿çšãããè²ãå®çŸ©ããŸãããããããã¯ã5ã€ã®åçå¯èœãªè²ãšããã¥ãŒããªã¢ã«ã¬ãã«ã®ã«ãŒãœã«ã®è²ã§ããè²ã«é¢ä¿ãªãããã¹ãŠã®ãªããžã§ã¯ããæ€çŽ¢ããå¿ èŠãããå Žåã¯ãCOLOR_ALLã䜿çšããŸãããŸãããã®ã±ãŒã¹ãæ€èšããŸãã
COLOR_BLUE = 0x01
COLOR_ORANGE = 0x02
COLOR_RED = 0x03
COLOR_GREEN = 0x04
COLOR_YELLOW = 0x05
COLOR_WHITE = 0x06
COLOR_ALL = 0x07
ãªããžã§ã¯ããèŠã€ããã«ã¯ãæåã«ç»åãåçŽåããå¿ èŠããããŸããããšãã°ãèšå·ã0ããååŸããŠãããå€ãé©çšããŸããã€ãŸãããªããžã§ã¯ããèæ¯ããåé¢ããŸãããã®æ®µéã§ã¯ãã·ã³ãã«ã®è²ã¯é¢ä¿ãããŸããããŸããç»åãçœé»ã«å€æããŠã1ãã£ã³ãã«ã«ããŸããã°ã¬ãŒã¹ã±ãŒã«å€æãæ åœãã2çªç®ã®åŒæ°cv2.COLOR_BGR2GRAYãæã€cv2.cvtColor颿°ã¯ãããã«åœ¹ç«ã¡ãŸããæ¬¡ã«ãcv2.thresholdã䜿çšããŠãããå€ãå®è¡ããŸããç¹å®ã®ãããå€ãäžåãç»åã®ãã¹ãŠã®ãã¯ã»ã«ã¯0ã«èšå®ããããããè¶ ãããã®ã¯ãã¹ãŠ-ãã255ã«èšå®ãããŸããcv2.threshold颿°ã®2çªç®ã®åŒæ°ã¯ããããå€ãæ åœããŸãããã®å Žåãcv2.THRESH_OTSUã䜿çšãããããä»»æã®æ°ãæå®ã§ããŸãã ãããŠã颿°èªäœããç»åãã¹ãã°ã©ã ã«åºã¥ããŠå€§æŽ¥æ³ã䜿çšããŠæé©ãªãããå€ã決å®ããŸãã
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)

è²åã
ããã«è峿·±ããã¿ã¹ã¯ãè€éã«ããŠãã¬ãã«éžæç»é¢ã§ãã¹ãŠã®èµ€ãèšå·ãèŠã€ããŸãããã

ããã©ã«ãã§ã¯ããã¹ãŠã®OpenCVã€ã¡ãŒãžã¯BGR圢åŒã§ä¿åãããŸããHSVïŒè²çžã圩床ãå€-è²çžã圩床ãå€ïŒã¯ãè²ã®ã»ã°ã¡ã³ããŒã·ã§ã³ã«é©ããŠããŸããRGBã«å¯Ÿãããã®å©ç¹ã¯ãHSVãåœ©åºŠãšæããããè²ãåé¢ããããšã§ããè²çžã¯ã1ã€ã®è²çžãã£ãã«ã«ãã£ãŠãšã³ã³ãŒããããŸããèç·è²ã®é·æ¹åœ¢ãäŸã«ãšããåŸã ã«æãããäžããŠãããŸãããã

RGBãšã¯ç°ãªãããã®å€æã¯HSVã§ã¯çŽæçã«èŠããŸããValueãŸãã¯Brightnessãã£ãã«ã®å€ãæžããã ãã§ããããã§ãåç §ã¢ãã«ã§ã¯ãè²çžã·ã§ãŒãã¹ã±ãŒã«ã0ã360°ã®ç¯å²ã§å€åããããšã«æ³šæããŠãã ãããç§ãã¡ã®èç·è²ã¯90°ã«å¯Ÿå¿ããŸãããã®å€ã8ããããã£ãã«ã«åãããã«ã¯ã2ã§å²ãå¿ èŠããããŸãã
è²ã®ã»ã°ã¡ã³ããŒã·ã§ã³ã¯ãåäžã®è²ã§ã¯ãªããç¯å²ã§æ©èœããŸããç¯å²ã¯çµéšçã«æ±ºå®ã§ããŸãããå°ããªã¹ã¯ãªãããäœæããæ¹ãç°¡åã§ãã
import cv2
import numpy as numpy
image_path = "tests_data/SELECT_LEVEL.png"
hsv_max_upper = 0, 0, 0
hsv_min_lower = 255, 255, 255
def bite_range(value):
value = 255 if value > 255 else value
return 0 if value < 0 else value
def pick_color(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
global hsv_max_upper
global hsv_min_lower
global image_hsv
hsv_pixel = image_hsv[y, x]
hsv_max_upper = bite_range(max(hsv_max_upper[0], hsv_pixel[0]) + 1), \
bite_range(max(hsv_max_upper[1], hsv_pixel[1]) + 1), \
bite_range(max(hsv_max_upper[2], hsv_pixel[2]) + 1)
hsv_min_lower = bite_range(min(hsv_min_lower[0], hsv_pixel[0]) - 1), \
bite_range(min(hsv_min_lower[1], hsv_pixel[1]) - 1), \
bite_range(min(hsv_min_lower[2], hsv_pixel[2]) - 1)
print('HSV range: ', (hsv_min_lower, hsv_max_upper))
hsv_mask = cv2.inRange(image_hsv, numpy.array(hsv_min_lower), numpy.array(hsv_max_upper))
cv2.imshow("HSV Mask", hsv_mask)
image = cv2.imread(image_path)
cv2.namedWindow('Original')
cv2.setMouseCallback('Original', pick_color)
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
cv2.imshow("Original", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
ã¹ã¯ãªãŒã³ã·ã§ããã§èµ·åããŸãããã

èµ€ãè²ãã¯ãªãã¯ããŠãçµæã®ãã¹ã¯ã確èªããŸããåºåãç§ãã¡ã«åããªãå Žåã¯ãèµ€ã®è²åããéžæããŠããã¹ã¯ã®ç¯å²ãšé¢ç©ãå¢ãããŸãããã®ã¹ã¯ãªããã¯ãcv2.inRange颿°ã«åºã¥ããŠããŸãããã®é¢æ°ã¯ãã«ã©ãŒãã£ã«ã¿ãŒãšããŠæ©èœããæå®ãããè²ç¯å²ã®ãããå€ç»åãè¿ããŸãã
次ã®ç¯å²ã«ã€ããŠè©³ããèŠãŠãããŸãããã
COLOR_HSV_RANGE = {
COLOR_BLUE: ((112, 151, 216), (128, 167, 255)),
COLOR_ORANGE: ((8, 251, 93), (14, 255, 255)),
COLOR_RED: ((167, 252, 223), (171, 255, 255)),
COLOR_GREEN: ((71, 251, 98), (77, 255, 211)),
COLOR_YELLOW: ((27, 252, 51), (33, 255, 211)),
COLOR_WHITE: ((0, 0, 159), (7, 7, 255)),
}
茪éãèŠã€ãã
ã¬ãã«éžæç»é¢ã«æ»ããŸããããå®çŸ©ããã°ããã®èµ€ã®ç¯å²ã®ã«ã©ãŒãã£ã«ã¿ãŒãé©çšããèŠã€ãã£ããããå€ãcv2.findContoursã«æž¡ããŸãããã®é¢æ°ã¯ãèµ€ãèŠçŽ ã®èŒªéãèŠã€ããŸãã2çªç®ã®åŒæ°ãšããŠcv2.RETR_EXTERNALãæå®ããŸã-å€åŽã®èŒªéã®ã¿ãå¿ èŠã§ã 3çªç®ã®åŒæ°ãšããŠcv2.CHAIN_APPROX_SIMPLE-çŽç·ã®èŒªéã«é¢å¿ããããã¡ã¢ãªãç¯çŽãããããã®é ç¹ã®ã¿ãä¿åããŸãã
thresh = cv2.inRange(image, self.COLOR_HSV_RANGE[self.COLOR_RED][0], self.COLOR_HSV_RANGE[self.COLOR_RED][1])
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE

ãã€ãºã®é€å»
çµæãšããŠåŸããã茪éã«ã¯ãå€ãã®ããã¯ã°ã©ãŠã³ããã€ãºãå«ãŸããŠããŸãããããåé€ããã«ã¯ãnumbersã®ããããã£ã䜿çšããŸãããããã¯ã座æšè»žã«å¹³è¡ãªé·æ¹åœ¢ã§æ§æãããŠããŸãããã¹ãŠã®ãã¹ãç¹°ãè¿ããcv2.minAreaRectã䜿çšããŠãããããæå°ã®é·æ¹åœ¢ã«åãããŸããé·æ¹åœ¢ã¯4ç¹ã§å®çŸ©ãããŸããé·æ¹åœ¢ã軞ã«å¹³è¡ã§ããå Žåããã€ã³ãã®åãã¢ã®åº§æšã®1ã€ãäžèŽããå¿ èŠããããŸããããã¯ãé·æ¹åœ¢ã®åº§æšã1次å é åãšããŠè¡šãå Žåãæå€§4ã€ã®äžæã®å€ãæã€ããšãæå³ããŸããããã«ãã¢ã¹ãã¯ãæ¯ã3察1ãã倧ããé·ãããé·æ¹åœ¢ãé€å€ããŸãããããè¡ãã«ã¯ãcv2.boundingRectã䜿çšããŠå¹ ãšé·ããèŠã€ããŸãã
squares = []
for cnt in contours:
rect = cv2.minAreaRect(cnt)
square = cv2.boxPoints(rect)
square = numpy.int0(square)
(_, _, w, h) = cv2.boundingRect(square)
a = max(w, h)
b = min(w, h)
if numpy.unique(square).shape[0] <= 4 and a <= b * 3:
squares.append(numpy.array([[square[0]], [square[1]], [square[2]], [square[3]]]))

茪éãçµã¿åããã
ä»ã¯ãŸããæ¬¡ã«ãèŠã€ãã£ãé·æ¹åœ¢ãçµã¿åãããŠãã·ã³ãã«ã®å ±éã®ã¢ãŠãã©ã€ã³ã«ããå¿ èŠããããŸããäžéç»åãå¿ èŠã§ããnumpy.zeros_likeã§äœæããŸãããããã®é¢æ°ã¯ã圢ç¶ãšãµã€ãºãç¶æããªãããããªãã¯ã¹ã€ã¡ãŒãžã®ã³ããŒãäœæããããããŒãã§åããŸããã€ãŸããå ã®ç»åã®ã³ããŒãé»ã®èæ¯ã§å¡ãã€ã¶ããŸãããããã1ãã£ã³ãã«ã«å€æããcv2.drawContoursã䜿çšããŠèŠã€ãã£ã茪éãé©çšããçœã§å¡ãã€ã¶ããŸããcv2.dilateãé©çšã§ãããã€ããªãããå€ãååŸããŸãããã®é¢æ°ã¯ã5ãã¯ã»ã«ä»¥å ã®è·é¢ã«ããå¥ã ã®é·æ¹åœ¢ãæ¥ç¶ããããšã«ãããçœãé åãæ¡åŒµããŸããããäžåºŠcv2.findContoursãåŒã³åºããŠãèµ€ãæ°åã®èŒªéãååŸããŸãã
image_zero = numpy.zeros_like(image)
image_zero = cv2.cvtColor(image_zero, cv2.COLOR_BGR2RGB)
cv2.drawContours(image_zero, contours_of_squares, -1, (255, 255, 255), -1)
_, thresh = cv2.threshold(image_zero, 0, 255, cv2.THRESH_OTSU)
kernel = numpy.ones((5, 5), numpy.uint8)
thresh = cv2.dilate(thresh, kernel, iterations=1)
dilate_contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

æ®ãã®ãã€ãºã¯ãcv2.contourAreaã䜿çšããŠèŒªéé åã«ãã£ãŠãã£ã«ã¿ãªã³ã°ãããŸãã500ãã¯ã»ã«Â²æªæºã®ãã¹ãŠãåé€ããŸãã
digit_contours = [cnt for cnt in digit_contours if cv2.contourArea(cnt) > 500]

ä»ã§ã¯çŽ æŽãããã§ããäžèšã®ãã¹ãŠãRobotã¯ã©ã¹ã«å®è£ ããŸãããã
# ...
class Robot:
# ...
def get_dilate_contours(self, image, color_inx, distance):
thresh = self.get_color_thresh(image, color_inx)
if thresh is False:
return []
kernel = numpy.ones((distance, distance), numpy.uint8)
thresh = cv2.dilate(thresh, kernel, iterations=1)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return contours
def get_color_thresh(self, image, color_inx):
if color_inx == self.COLOR_ALL:
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
else:
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
thresh = cv2.inRange(image, self.COLOR_HSV_RANGE[color_inx][0], self.COLOR_HSV_RANGE[color_inx][1])
return thresh
def filter_contours_of_rectangles(self, contours):
squares = []
for cnt in contours:
rect = cv2.minAreaRect(cnt)
square = cv2.boxPoints(rect)
square = numpy.int0(square)
(_, _, w, h) = cv2.boundingRect(square)
a = max(w, h)
b = min(w, h)
if numpy.unique(square).shape[0] <= 4 and a <= b * 3:
squares.append(numpy.array([[square[0]], [square[1]], [square[2]], [square[3]]]))
return squares
def get_contours_of_squares(self, image, color_inx, square_inx):
thresh = self.get_color_thresh(image, color_inx)
if thresh is False:
return False
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours_of_squares = self.filter_contours_of_rectangles(contours)
if len(contours_of_squares) < 1:
return False
image_zero = numpy.zeros_like(image)
image_zero = cv2.cvtColor(image_zero, cv2.COLOR_BGR2RGB)
cv2.drawContours(image_zero, contours_of_squares, -1, (255, 255, 255), -1)
dilate_contours = self.get_dilate_contours(image_zero, self.COLOR_ALL, 5)
dilate_contours = [cnt for cnt in dilate_contours if cv2.contourArea(cnt) > 500]
if len(dilate_contours) < 1:
return False
else:
return dilate_contours
æ°åã®èªè
æ°åãèªèããæ©èœã远å ããŸãããããªãç§ãã¡ã¯ãããå¿ èŠãªã®ã§ããïŒ
# âŠ
class Robot:
# ...
SQUARE_BIG_SYMBOL = 0x01
SQUARE_SIZES = {
SQUARE_BIG_SYMBOL: 9,
}
IMAGE_DATA_PATH = "data/"
def __init__(self):
# ...
self.dilate_contours_bi_data = {}
for image_file in os.listdir(self.IMAGE_DATA_PATH):
image = cv2.imread(self.IMAGE_DATA_PATH + image_file)
contour_inx = os.path.splitext(image_file)[0]
color_inx = self.COLOR_RED
dilate_contours = self.get_dilate_contours_by_square_inx(image, color_inx, self.SQUARE_BIG_SYMBOL)
self.dilate_contours_bi_data[contour_inx] = dilate_contours[0]
def get_dilate_contours_by_square_inx(self, image, color_inx, square_inx):
distance = math.ceil(self.SQUARE_SIZES[square_inx] / 2)
return self.get_dilate_contours(image, color_inx, distance)
OpenCVã¯ãcv2.matchShapes颿°ã䜿çšããŠãHuã¢ãŒã¡ã³ãã«åºã¥ããŠèŒªéãæ¯èŒããŸãã2ã€ã®ãã¹ãå ¥åãšããŠåãåããæ¯èŒçµæãæ°å€ãšããŠè¿ãããšã«ãããå®è£ ã®è©³çްãé衚瀺ã«ããŸããå°ããã»ã©ã茪éã¯äŒŒãŠããŸãã
cv2.matchShapes(dilate_contour, self.dilate_contours_bi_data['digit_' + str(digit)], cv2.CONTOURS_MATCH_I1, 0)
çŸåšã®èŒªédigit_contourããã¹ãŠã®æšæºãšæ¯èŒããcv2.matchShapesã®æå°å€ãèŠã€ããŸããæå°å€ã0.15æªæºã®å Žåããã®æ¡ã¯èªèããããšèŠãªãããŸããæå°å€ã®ãããå€ã¯çµéšçã«èŠã€ãããŸããããŸããééã®çãæåã1ã€ã®æ°åã«ãŸãšããŸãããã
# âŠ
class Robot:
# âŠ
def scan_digits(self, image, color_inx, square_inx):
result = []
contours_of_squares = self.get_contours_of_squares(image, color_inx, square_inx)
before_digit_x, before_digit_y = (-100, -100)
if contours_of_squares is False:
return result
for contour_of_square in reversed(contours_of_squares):
crop_image = self.crop_image_by_contour(image, contour_of_square)
dilate_contours = self.get_dilate_contours_by_square_inx(crop_image, self.COLOR_ALL, square_inx)
if (len(dilate_contours) < 1):
continue
dilate_contour = dilate_contours[0]
match_shapes = {}
for digit in range(0, 10):
match_shapes[digit] = cv2.matchShapes(dilate_contour, self.dilate_contours_bi_data['digit_' + str(digit)], cv2.CONTOURS_MATCH_I1, 0)
min_match_shape = min(match_shapes.items(), key=lambda x: x[1])
if len(min_match_shape) > 0 and (min_match_shape[1] < self.MAX_MATCH_SHAPES_DIGITS):
digit = min_match_shape[0]
rect = cv2.minAreaRect(contour_of_square)
box = cv2.boxPoints(rect)
box = numpy.int0(box)
(digit_x, digit_y, digit_w, digit_h) = cv2.boundingRect(box)
if abs(digit_y - before_digit_y) < digit_y * 0.3 and abs(
digit_x - before_digit_x) < digit_w + digit_w * 0.5:
result[len(result) - 1][0] = int(str(result[len(result) - 1][0]) + str(digit))
else:
result.append([digit, self.get_contour_centroid(contour_of_square)])
before_digit_x, before_digit_y = digit_x + (digit_w / 2), digit_y
return result
åºåã§ãself.scan_digitsã¡ãœããã¯ãèªèãããæ¡ãšãã®ã¯ãªãã¯ã®åº§æšãå«ãé åãè¿ããŸããã¯ãªãã¯ãã€ã³ãã¯ããã®ã¢ãŠãã©ã€ã³ã®äžå¿ã«ãªããŸãã
# âŠ
class Robot:
# âŠ
def get_contour_centroid(self, contour):
moments = cv2.moments(contour)
return int(moments["m10"] / moments["m00"]), int(moments["m01"] / moments["m00"])
ç§ãã¡ã¯åãåã£ãæ°åèªèããŒã«ãåãã§ããŸãããé·ãã¯ãããŸãããHuã¢ãŒã¡ã³ãã¯ãã¹ã±ãŒã«ã¯å¥ãšããŠãå転ãšé¡é¢åå°æ§ã«å¯ŸããŠãäžå€ã§ãããããã£ãŠããããã¯çªå·6ãš9/2ãš5ãæ··åããŸãããããã®ã·ã³ãã«ã®é ç¹ãã§ãã¯ã远å ããŸãããã6ãš9ã¯å³äžã®ç¹ã§åºå¥ãããŸããæ°Žå¹³æ¹åã®äžå¿ããäžã«ããå Žåã¯ãå察ã®å Žåã¯6ãš9ã§ãããã¢2ãš5ã®å Žåãå³äžã®ç¹ãã·ã³ãã«ã®å³ã®å¢çã«ãããã©ããã確èªããŸãã
if digit == 6 or digit == 9:
extreme_bottom_point = digit_contour[digit_contour[:, :, 1].argmax()].flatten()
x_points = digit_contour[:, :, 0].flatten()
extreme_right_points_args = numpy.argwhere(x_points == numpy.amax(x_points))
extreme_right_points = digit_contour[extreme_right_points_args]
extreme_top_right_point = extreme_right_points[extreme_right_points[:, :, :, 1].argmin()].flatten()
if extreme_top_right_point[1] > round(extreme_bottom_point[1] / 2):
digit = 6
else:
digit = 9
if digit == 2 or digit == 5:
extreme_right_point = digit_contour[digit_contour[:, :, 0].argmax()].flatten()
y_points = digit_contour[:, :, 1].flatten()
extreme_top_points_args = numpy.argwhere(y_points == numpy.amin(y_points))
extreme_top_points = digit_contour[extreme_top_points_args]
extreme_top_right_point = extreme_top_points[extreme_top_points[:, :, :, 0].argmax()].flatten()
if abs(extreme_right_point[0] - extreme_top_right_point[0]) > 0.05 * extreme_right_point[0]:
digit = 2
else:
digit = 5


ç«¶æå Žã®åæ
ãã¬ãŒãã³ã°ã¬ãã«ãã¹ãããããŸããããçœãã«ãŒãœã«ãã¯ãªãã¯ããŠã¹ã¯ãªãããäœæããåçãéå§ããŸãã
ç«¶æå Žããããã¯ãŒã¯ãšããŠæ³åããŠã¿ãŸããããè²ã®åé åã¯ã飿¥ãããã€ããŒã«ãªã³ã¯ãããŠããããŒãã«ãªããŸããã«ã©ãŒãšãªã¢/ããŒããèšè¿°ããã¯ã©ã¹self.ColorAreaãäœæããŸãããã
class ColorArea:
def __init__(self, color_inx, click_point, contour):
self.color_inx = color_inx #
self.click_point = click_point #
self.contour = contour #
self.neighbors = [] #
self.color_areas ããŒãã®ãªã¹ããšããã¬ã€ãã£ãŒã«ãself.color_areas_color_countã«è²ã衚瀺ãããé »åºŠã®ãªã¹ããå®çŸ©ããŸãããããã£ã³ãã¹ã®ã¹ã¯ãªãŒã³ã·ã§ãããããã¬ã€ãã£ãŒã«ããåãåããŸãã
image[pt1[1]:pt2[1], pt1[0]:pt2[0]]
ããã§ãpt1ãpt2ã¯ãã¬ãŒã ã®æ¥µå€ã§ããã²ãŒã ã®ãã¹ãŠã®è²ãç¹°ãè¿ããããããã«self.get_dilate_contoursã¡ãœãããé©çšããŸããããŒãã®èŒªéãèŠã€ããããšã¯ãã·ã³ãã«ã®äžè¬çãªèŒªéãæ¢ãæ¹æ³ãšäŒŒãŠããŸãããç«¶æå Žã«ãã€ãºããªããšããéãããããŸããããŒãã®åœ¢ç¶ã¯å¹ç¶ãŸãã¯ç©Žãããå¯èœæ§ããããããã»ã³ããã€ãã¯åœ¢ç¶ããå€ããã¯ãªãã¯ã®åº§æšãšããŠã¯é©ããŠããŸããããããè¡ãã«ã¯ãæäžäœã®ãã€ã³ããèŠã€ããŠ20ãã¯ã»ã«ããããããŸãããã®æ¹æ³ã¯æ®éçã§ã¯ãããŸããããç§ãã¡ã®å Žåã¯æ©èœããŸãã
self.color_areas = []
self.color_areas_color_count = [0] * self.SELECT_COLOR_COUNT
image = self.crop_image_by_rectangle(self.screenshot, numpy.array(self.GAME_MAIN_AREA))
for color_inx in range(1, self.SELECT_COLOR_COUNT + 1):
dilate_contours = self.get_dilate_contours(image, color_inx, 10)
for dilate_contour in dilate_contours:
click_point = tuple(
dilate_contour[dilate_contour[:, :, 1].argmin()].flatten() + [0, int(self.CLICK_AREA)])
self.color_areas_color_count[color_inx - 1] += 1
color_area = self.ColorArea(color_inx, click_point, dilate_contour)
self.color_areas.append(color_area)

ãªã³ã¯ãšãªã¢
茪ééã®è·é¢ã15ãã¯ã»ã«ä»¥å ã®å Žåããã®é åã飿¥é åãšèŠãªããŸããåããŒããããããã§ç¹°ãè¿ããè²ãäžèŽããå Žåã¯æ¯èŒãã¹ãããããŸãã
blank_image = numpy.zeros_like(image)
blank_image = cv2.cvtColor(blank_image, cv2.COLOR_BGR2GRAY)
for color_area_inx_1 in range(0, len(self.color_areas)):
for color_area_inx_2 in range(color_area_inx_1 + 1, len(self.color_areas)):
color_area_1 = self.color_areas[color_area_inx_1]
color_area_2 = self.color_areas[color_area_inx_2]
if color_area_1.color_inx == color_area_2.color_inx:
continue
common_image = cv2.drawContours(blank_image.copy(), [color_area_1.contour, color_area_2.contour], -1, (255, 255, 255), cv2.FILLED)
kernel = numpy.ones((15, 15), numpy.uint8)
common_image = cv2.dilate(common_image, kernel, iterations=1)
common_contour, _ = cv2.findContours(common_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(common_contour) == 1:
self.color_areas[color_area_inx_1].neighbors.append(color_area_inx_2)
self.color_areas[color_area_inx_2].neighbors.append(color_area_inx_1)

æé©ãªåããæ¢ããŠããŸã
ç«¶æå Žã«é¢ãããã¹ãŠã®æ å ±ããããŸããåããéžã³å§ããŸãããããã®ããã«ã¯ãããŒãã€ã³ããã¯ã¹ãšè²ãå¿ èŠã§ããç§»åãªãã·ã§ã³ã®æ°ã¯ã次ã®åŒã§æ±ºå®ã§ããŸãã
ç§»åãªãã·ã§ã³=ããŒãã®æ°*è²ã®æ°-1
åã®ãã¬ã€ãã£ãŒã«ãã«ã¯ã7 *ïŒ5-1ïŒ= 28ã®ãªãã·ã§ã³ããããŸãããããã®æ°ã¯å°ãªãã®ã§ããã¹ãŠã®åããç¹°ãè¿ããŠæé©ãªãã®ãéžæã§ããŸãããªãã·ã§ã³ããããªãã¯ã¹
select_color_weightsãšããŠå®çŸ©ããŸãããããã®è¡ã¯ãããŒãã€ã³ããã¯ã¹ãã«ã©ãŒã€ã³ããã¯ã¹åãããã³ç§»åãŠã§ã€ãã»ã«ã«ãªããŸããããŒãã®æ°ã1ã€ã«æžããå¿ èŠããããããããŒãäžã§åºæã®è²ãæã¡ãç§»åãããšæ¶ããé åãåªå ããŸããäžæã®è²ãæã€ãã¹ãŠã®ããŒãè¡ã®éã¿ã«+10ãäžããŸããããè²ãç«¶æå Žã«çŸããé »åºŠã¯ã以åã«åéãããã®ã§ããself.color_areas_color_count
if self.color_areas_color_count[color_area.color_inx - 1] == 1:
select_color_weight = [x + 10 for x in select_color_weight]
次ã«ã飿¥ããé åã®è²ãèŠãŠã¿ãŸããããããŒãã«color_inxã®ãã€ããŒãããããããã®æ°ãç«¶æå Žã§ã®ãã®è²ã®ç·æ°ãšçããå Žåã¯ãã»ã«ã®éã¿ã«+10ãå²ãåœãŠãŸããããã«ãããcolor_inxã«ã©ãŒããã£ãŒã«ãããåé€ãããŸãã
for color_inx in range(0, len(select_color_weight)):
color_count = select_color_weight[color_inx]
if color_count != 0 and self.color_areas_color_count[color_inx] == color_count:
select_color_weight[color_inx] += 10
åãè²ã®é£äººããšã«ã»ã«ã®éã¿ã«+1ãäžããŸããããã€ãŸãã3ã€ã®èµ€ãé£äººãããå Žåãèµ€ãã»ã«ã¯ãã®éã¿ã«å¯ŸããŠ+3ãåãåããŸãã
for select_color_weight_inx in color_area.neighbors:
neighbor_color_area = self.color_areas[select_color_weight_inx]
select_color_weight[neighbor_color_area.color_inx - 1] += 1
ãã¹ãŠã®éã¿ãåéããåŸãæå€§ã®éã¿ãæã€åããèŠã€ããŸããã©ã®ããŒããšã©ã®è²ã«å±ããããå®çŸ©ããŸãããã
max_index = select_color_weights.argmax()
self.color_area_inx_next = max_index // self.SELECT_COLOR_COUNT
select_color_next = (max_index % self.SELECT_COLOR_COUNT) + 1
self.set_select_color_next(select_color_next)
æé©ãªåããæ±ºå®ããããã®å®å šãªã³ãŒãã
# âŠ
class Robot:
# âŠ
def scan_color_areas(self):
self.color_areas = []
self.color_areas_color_count = [0] * self.SELECT_COLOR_COUNT
image = self.crop_image_by_rectangle(self.screenshot, numpy.array(self.GAME_MAIN_AREA))
for color_inx in range(1, self.SELECT_COLOR_COUNT + 1):
dilate_contours = self.get_dilate_contours(image, color_inx, 10)
for dilate_contour in dilate_contours:
click_point = tuple(
dilate_contour[dilate_contour[:, :, 1].argmin()].flatten() + [0, int(self.CLICK_AREA)])
self.color_areas_color_count[color_inx - 1] += 1
color_area = self.ColorArea(color_inx, click_point, dilate_contour, [0] * self.SELECT_COLOR_COUNT)
self.color_areas.append(color_area)
blank_image = numpy.zeros_like(image)
blank_image = cv2.cvtColor(blank_image, cv2.COLOR_BGR2GRAY)
for color_area_inx_1 in range(0, len(self.color_areas)):
for color_area_inx_2 in range(color_area_inx_1 + 1, len(self.color_areas)):
color_area_1 = self.color_areas[color_area_inx_1]
color_area_2 = self.color_areas[color_area_inx_2]
if color_area_1.color_inx == color_area_2.color_inx:
continue
common_image = cv2.drawContours(blank_image.copy(), [color_area_1.contour, color_area_2.contour],
-1, (255, 255, 255), cv2.FILLED)
kernel = numpy.ones((15, 15), numpy.uint8)
common_image = cv2.dilate(common_image, kernel, iterations=1)
common_contour, _ = cv2.findContours(common_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(common_contour) == 1:
self.color_areas[color_area_inx_1].neighbors.append(color_area_inx_2)
self.color_areas[color_area_inx_2].neighbors.append(color_area_inx_1)
def analysis_color_areas(self):
select_color_weights = []
for color_area_inx in range(0, len(self.color_areas)):
color_area = self.color_areas[color_area_inx]
select_color_weight = numpy.array([0] * self.SELECT_COLOR_COUNT)
for select_color_weight_inx in color_area.neighbors:
neighbor_color_area = self.color_areas[select_color_weight_inx]
select_color_weight[neighbor_color_area.color_inx - 1] += 1
for color_inx in range(0, len(select_color_weight)):
color_count = select_color_weight[color_inx]
if color_count != 0 and self.color_areas_color_count[color_inx] == color_count:
select_color_weight[color_inx] += 10
if self.color_areas_color_count[color_area.color_inx - 1] == 1:
select_color_weight = [x + 10 for x in select_color_weight]
color_area.set_select_color_weights(select_color_weight)
select_color_weights.append(select_color_weight)
select_color_weights = numpy.array(select_color_weights)
max_index = select_color_weights.argmax()
self.color_area_inx_next = max_index // self.SELECT_COLOR_COUNT
select_color_next = (max_index % self.SELECT_COLOR_COUNT) + 1
self.set_select_color_next(select_color_next)
ã¬ãã«éãç§»åããŠçµæã楜ããæ©èœã远å ããŸãããããããã¯å®å®ããŠåäœãã1åã®ã»ãã·ã§ã³ã§ã²ãŒã ãå®äºããŸãã
åºå
äœæããããããã¯å®çšçã§ã¯ãããŸãããããããèšäºã®èè ã¯ãOpenCVã®åºæ¬ååã®è©³çްãªèª¬æããåå¿è ãåææ®µéã§ãã®ã©ã€ãã©ãªãæ±ãã®ã«åœ¹ç«ã€ããšãå¿ããæãã§ããŸãã