def step_impl(context, switch): assert switch == "on" or switch == "off" press(context, keys.KEY_DOWN, 1) # to show progress bar cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) region = REGION_PROGRESS_BAR frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 if switch == "off": debug(f"match {playback_soft_buttons['cc off unhilite'][1]}") if match(frame, cv2.imread(playback_soft_buttons["cc off unhilite"][1]), region, match_parameter=mp)[0] is True: debug(f"closed caption is already {switch}\n\n") return elif switch == 'on': debug(f"match {playback_soft_buttons['cc on unhilite'][1]}") if match(frame, cv2.imread(playback_soft_buttons["cc on unhilite"][1]), region, match_parameter=mp)[0] is True: debug(f"closed caption is already {switch}") return debug(f"navigate_to {switch}\n\n") if navigate_to(context, "cc on" if switch == "off" else "cc off"): press(context, keys.KEY_SELECT, 1) # press(context, keys.KEY_UP, 1) press(context, keys.KEY_SELECT, 1) return assert False, f"fail to switch {switch} closed caption"
def step_impl(context): press(context, keys.KEY_DOWN, 1) # to show progress bar cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (0, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 video_quality = "None" result = 0.0 assert os.path.exists(playback_soft_buttons["play off unhilitefamily"][1]) found = match(frame, cv2.imread( playback_soft_buttons['family play off unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp) if found[0] is True: debug(f"found family play off\n") return assert os.path.exists(playback_soft_buttons["family play on unhilite"][1]) found = match(frame, cv2.imread( playback_soft_buttons['family play on unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp) if found[0] is True: debug(f"found family play on\n") return debug(f"fail to find family play on the progress barn\n") assert False, "fail to find family play on the progress bar"
def find_video_quality_on_progress_bar(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 video_quality = None found_uhd = match(frame, cv2.imread(playback_soft_buttons['uhd unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] if found_uhd is True: video_quality = "uhd" debug(f"found uhd") found_hdx = match(frame, cv2.imread(playback_soft_buttons['hdx unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] if found_hdx is True: video_quality = "hdx" debug(f"found hdx") found_sd = match(frame, cv2.imread(playback_soft_buttons['sd unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] if found_sd is True: video_quality = "sd" debug(f"found sd") return video_quality
def detect_shoppable_ads(context): debug("=== detect_shoppable_ads ===") frame = cv2.imread(context.frame) ads = "./images/Roku/shoppable_ads.png" ads1 = "./images/Roku/shoppable_ads_1.png" ads2 = "./images/Roku/shoppable_ads_2.png" ads_region = Region(x=1000, y=400, right=1280, bottom=600) ads1_region = Region(x=1000, y=360, right=1280, bottom=620) ads2_region = Region(x=1070, y=260, right=1166, bottom=350) return match(frame, ads, rads_region)[0] or \ match(frame, ads1, ads1_region)[0] or \ match(frame, ads2, ads2_region)[0]
def find_selection_text(frame, left_bk, right_bk=None, x_offset=0, y_offset=5, region=Region(x=0, y=0, right=1280, bottom=720), convert_to_grayscale=True, match_parameter=None): print(f"find_selection_text: region {region}") debug("search for left bracket") if match_parameter is None: match_parameter = get_default_match_parameter() l_result = match(frame, left_bk, region=region, match_parameter=match_parameter) if l_result[0] is False: debug("Error: fail to search left bracket") return None, None if right_bk is None: region = Region(x=l_result[1].x + x_offset, y=l_result[1].y + y_offset, right=l_result[1].right - x_offset, bottom=l_result[1].bottom - y_offset) else: debug("search for right bracket") r_result = match(frame, right_bk, region=region, match_parameter=match_parameter) if r_result[0] is False: debug("Error: fail to search right bracket") return None, None if l_result[1].right + x_offset > r_result[1].x - x_offset: return None, None region = Region(x=l_result[1].x + x_offset, y=l_result[1].y + y_offset, right=r_result[1].right - x_offset, bottom=r_result[1].bottom - y_offset) print(f"find_selection_text: search text in {region}") if convert_to_grayscale is True: text = ocr(frame=cv2.cvtColor( frame[region.y:region.bottom, region.x:region.right], cv2.COLOR_BGR2GRAY)) else: text = ocr(frame=frame[region.y:region.bottom, region.x:region.right]) print(f"find_selection_text: found {text} in selected region") return text, region
def get_current_rental_quality(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) frame = cv2.imread(context.frame) uhd_sym_match = is_uhd_visible(context, frame) hdx_sym_match = is_hdx_visible(context, frame) sd_sym_match = is_sd_visible(context, frame) uhd_set = hdx_set = sd_set = set() if uhd_sym_match is not None and uhd_sym_match[0] is True: uhd_set = set(range(uhd_sym_match[1].y, uhd_sym_match[1].bottom)) if hdx_sym_match is not None and hdx_sym_match[0] is True: hdx_set = set(range(hdx_sym_match[1].y, hdx_sym_match[1].bottom)) if sd_sym_match is not None and sd_sym_match[0] is True: sd_set = set(range(sd_sym_match[1].y, sd_sym_match[1].bottom)) current_quality_match = match(frame, PURCHASE_POPUP_PRICE_20X36, region=REGION_PURCHASE_POPUP_GRID_PRICE) assert current_quality_match[0], "fail to find current purchase" curent_quality_set = set(range(current_quality_match[1].y, current_quality_match[1].bottom)) if len(list(curent_quality_set & uhd_set)) > 0: current_quality = consts.QUALITY_UHD elif len(list(curent_quality_set & hdx_set)) > 0: current_quality = consts.QUALITY_HDX elif len(list(curent_quality_set & sd_set)) > 0: current_quality = consts.QUALITY_SD else: assert False, "=== cannot find selected quality ===" return current_quality
def step_impl(context, switch): press(context, keys.KEY_DOWN, 1) # to show progress bar cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (3, 0.7, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 debug("check current switch\n") if switch == "off": debug(f"match {playback_soft_buttons['family play off unhilite'][1]}") if match(frame, cv2.imread( playback_soft_buttons["family play off unhilite"][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] is True: debug(f"family play is already {switch}\n") return elif switch == "on": debug(f"match {playback_soft_buttons['family play on unhilite'][1]}") if match(frame, cv2.imread( playback_soft_buttons["family play on unhilite"][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] is True: debug(f"family play is already {switch}\n") return debug(f"navigate_to family play {switch}\n") if navigate_to(context, "family play on" if switch == "off" else "family play off"): press(context, keys.KEY_SELECT, 1) # press(context, keys.KEY_UP, 1) press(context, keys.KEY_SELECT, 1) # dismiss family play popup press(context, keys.KEY_SELECT, 1) if switch == "on": press(context, keys.KEY_SELECT, 1) # dismiss family play popup return
def step_impl(context, template, region): print(f"verify template file: {template}") if os.path.isfile(template): where = list(map(int, region.split(","))) # region = vudu_image.Region(where[0], where[1], where[2], where[3]) region = Region(*where) print(f"match in region:{region}") match_result = vudu_image.match(cv2.imread(context.image), cv2.imread(template), region) context.match_result = match_result else: assert False, f"cannot find file: {template}"
def find_video_quality_on_playback_page(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 video_quality = "None" result = 0.0 assert os.path.exists(playback_video_quality["uhd"]) found = match(frame, cv2.imread(playback_video_quality['uhd']), REGION_PLAYBACK_PAGE_TITLE, match_parameter=mp) if found[0] is True and found[2] > result: video_quality = "uhd" result = found[2] debug(f"found uhd") found = match(frame, cv2.imread(playback_video_quality['hdx']), REGION_PLAYBACK_PAGE_TITLE, match_parameter=mp) if found[0] is True and found[2] > result: video_quality = "hdx" result = found[2] debug(f"found hdx") found = match(frame, cv2.imread(playback_video_quality['sd']), REGION_PLAYBACK_PAGE_TITLE, match_parameter=mp) if found[0] is True and found[2] > result: video_quality = "sd" result = found[2] debug(f"found sd") print("\n\n") return video_quality
def find_current_chapters(context): press(context, keys.KEY_SELECT, 1) # to show chapter grid cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 debug(f"match {playback_chapter['chapter']}") result = match(frame, cv2.imread(playback_chapter["chapter"]), REGION_CHAPTER_GRID, match_parameter=mp) print("\n\n") assert result[0] is True, f"fail to find current chapter" # find current chapter chapter_grids_width = REGION_CHAPTER_GRID.right - REGION_CHAPTER_GRID.x chapter_grids_height = REGION_CHAPTER_GRID.bottom - REGION_CHAPTER_GRID.top chapter_width = chapter_grids_width // CHAPTER_GRID_COLS chapter_height = chapter_grids_height // CHAPTER_GRID_ROWS chapters_regions = [] for row in range(CHAPTER_GRID_ROWS): for col in range(CHAPTER_GRID_COLS): chapters_regions.append( Region(REGION_CHAPTER_GRID.x + col * chapter_width, REGION_CHAPTER_GRID.y + row * chapter_height, REGION_CHAPTER_GRID.x + (col + 1) * chapter_width, REGION_CHAPTER_GRID.y + (row + 1) * chapter_height)) # debug(f"chapter: {len(chapters_regions) + 1}, {chapters_regions[-1]}") found_chapters = [] focused_chapter = 0 focused_chapter_area = 0 for chp in range(len(chapters_regions)): area = Region.intersect_area(result[1], chapters_regions[chp]) # debug(f"intersect area {result[1]} and {chapters_regions[chp]}, area: {area}") if area > 0: found_chapters.append((chp, area)) debug(f"chapter {chp + 1}, area: {area}") if area > focused_chapter_area: focused_chapter = chp + 1 focused_chapter_area = area return focused_chapter
def is_single_purchase_price_buy_4k_visible(context, frame): # focused buy return match(frame, PURCHASE_POPUP_BUY_4K)[0]
def is_single_purchase_price_un_focus_buy_visible(context, frame): # unfocused buy return match(frame, PURCHASE_POPUP_BUY_UH)[0]
def is_discount_price_visible(context, frame): """ :return: if discount_price (orange price) is visible. """ return match(frame, ORANGE_DISCOUNT_PRICE_DETAILS)
def check_discount_price(context, frame): """ :return: if discount_price is visible. """ return (match(frame, PURCHASE_ORANGE_DISCOUNT)[0]) or \ (match(frame, PURCHASE_ORANGE_DISCOUNT_UH)[0])
def is_uhd_visible(context, frame): """ :return: un-highlighted SD icon on screen. """ return match(frame, PURCHASE_POPUP_UHD_UH, REGION_PURCHASE_POPUP_GRID_VIDEO_QAULITY)
def is_sd_visible(context, frame): """ :return: SD icon on screen. """ return match(frame, PURCHASE_POPUP_SD_UH, REGION_PURCHASE_POPUP_GRID_VIDEO_QAULITY)
def is_hdx_visible(context, frame): """ :return: HDX icon on screen """ return match(frame, PURCHASE_POPUP_HDX_UH, REGION_PURCHASE_POPUP_GRID_VIDEO_QAULITY)
def check_cancel_button_visible(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) frame = cv2.imread(context.frame) return match(frame, MOVIE_PURCHASE_POPUP_CANCEL_BUTTON)
def is_single_purchase_price_rent_4k_visible(context, frame): return match(frame, PURCHASE_POPUP_RENT_4K)[0]
def is_single_purchase_price_buy_hdx_visible(context, frame): return match(frame, PURCHASE_POPUP_BUY_HD)[0]
def is_single_purchase_price_rent_visible(context, frame=None): if frame is None: cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) return match(context.frame, PURCHASE_POPUP_RENT)
def search_by_image(context, region, target, key_to_press, max_tries, interval_secs=1, mp=None, hist=None, key_to_press_func=None, focus_threshold=None): """ This function is mainly used in the menu or sub-menu where direction could be horizontal or vertical. :param mp: the match_parameters :param region: narrow down the region to search :param target: target image :param key_to_press: direction to press the arrow key on the remote control :param max_tries: maximum number of presses :param mp: match parameters for image match :param hist: use historgram/brightness to compare the difference to avoid false positive :return: (bool) whether image is found. """ debug( f" Search by image: {target}, region: {region}, max_tries: {max_tries}, mp: {mp}, hist: {hist}" ) MIN_DIFFERENCE = 0.15 ex_per_pixel = None ex_region = None found = 0 cam = context.cam for tries in range(max_tries): path = get_frame_name(context, f"{WORK_DIR}/_frame.png") cam.get_frame(path) _frame = cv2.imread(path) if mp is not None: result = match(frame=_frame, image=cv2.imread(target), region=region, match_parameter=mp) else: result = match(frame=_frame, image=cv2.imread(target), region=region) if result[0] is True: found += 1 # if wait_until(lambda: match(image=target), timeout_secs=1): if hist is None: print("\n\n==== Found image !! returning... ====\n\n") return True else: print(f"\n\n==== Found image !! check hist... {tries}====\n\n") # copy the detected image found_target = _frame[result[1].y:result[1].bottom, result[1].x:result[1].right] debug( f"frame shape{_frame.shape}, found target shape:{found_target.shape}, " f"region: {result[1]}, size: {found_target.size}") size = found_target.shape[0] * found_target.shape[1] if hist != SEARCH_BY_IMAGE_USE_BRIGHTNESS: total_r = total_g = total_b = 0 for r in (found_target): for c in (r): total_b += c[0] total_g += c[1] total_r += c[2] debug( f"total r, g, b: {total_r, total_g, total_b}, avg. r, g, b " f"{total_r/size, total_g/size, total_b/size}") if hist == SEARCH_BY_IMAGE_USE_RED_COLOR: total = total_r diff = 1.0 - (total_g + total_b) / 2 / total_r debug(f"diff = {diff}") elif hist == SEARCH_BY_IMAGE_USE_GREEN_COLOR: total = total_g diff = 1.0 - (total_r + total_b) / 2 / total_g debug(f"diff = {diff}") elif hist == SEARCH_BY_IMAGE_USE_BLUE_COLOR: total = total_b diff = 1.0 - (total_r + total_g) / 2 / total_b debug(f"diff = {diff}") per_pixel = total / size threshold = 15.0 if focus_threshold is not None else focus_threshold else: # path = f"{WORK_DIR}/_frame_{tries}_c.png" # cv2.imwrite(path, found_target) greyscale_found_target = cv2.cvtColor( found_target, cv2.COLOR_BGR2GRAY) # path = f"{WORK_DIR}/_frame_{tries}_g.png" # cv2.imwrite(path, greyscale_found_target) debug( f"greyscale found target shape: {greyscale_found_target.shape}" ) total = 0 for i in range(greyscale_found_target.shape[0]): total += sum(greyscale_found_target[i]) per_pixel = total / size threshold = 40.0 if focus_threshold is None else focus_threshold debug(f"total: {total}, per pixel avg: {per_pixel}") # continue if ex_per_pixel is None: ex_per_pixel = per_pixel ex_region = result[1] else: debug( f"=== Region.intersect: {Region.intersect(ex_region,result[1])}" ) if Region.intersect(ex_region, result[1]) is False: print(f"=== result region is changed, reset ") ex_per_pixel = per_pixel ex_region = result[1] else: debug( f"ex per pixel: {ex_per_pixel}, per pixel: {per_pixel}, diff: " f"{(per_pixel - ex_per_pixel)/per_pixel * 100} %") if per_pixel > ex_per_pixel and ( (per_pixel - ex_per_pixel) / per_pixel * 100 > threshold): if hist == SEARCH_BY_IMAGE_USE_BRIGHTNESS: print( "\n\n==== Found image in current position !! returning... ====\n\n" ) return True else: # Due to the background video can introduce brightness influnce, # if the difference between the other two colors are too low, # we will just assume it is a false positive if diff > MIN_DIFFERENCE: print( f"\n\n==== Found image in current position, diff: {diff} !! returning... " f"====\n\n") return True # elif tries == 1 and per_pixel < ex_per_pixel and ( elif per_pixel < ex_per_pixel and ( # if highlight is the previous one, it is moving away instead of moving in (ex_per_pixel - per_pixel) / per_pixel * 100 > threshold): debug( "\n\n==== Found image in last position ? verifying ... ====\n\n" ) if hist == SEARCH_BY_IMAGE_USE_BRIGHTNESS or ( hist != SEARCH_BY_IMAGE_USE_BRIGHTNESS and diff > MIN_DIFFERENCE ): # diff is used to make sure the difference is not introduced by fade in effect debug( "\n\n==== Found image in last position!! verified and returning... ====\n\n" ) if key_to_press == keys.KEY_RIGHT: if not key_to_press_func: press(context, keys.KEY_LEFT) else: key_to_press_func(keys.KEY_LEFT, 2) elif key_to_press == keys.KEY_LEFT: if not key_to_press_func: press(context, keys.KEY_RIGHT) else: key_to_press_func(keys.KEY_LEFT, 2) elif key_to_press == keys.KEY_UP: if not key_to_press_func: press(context, keys.KEY_DOWN) else: key_to_press_func(keys.KEY_LEFT, 2) elif key_to_press == keys.KEY_DOWN: if not key_to_press_func: press(context, keys.KEY_UP) else: key_to_press_func(keys.KEY_LEFT, 2) else: assert_that( False, "dont know how to navigate back to previous position" ) return True else: debug( f"\n\n=== Still not sure why we come here! per_pixel:{per_pixel}, ex_per_pixel{ex_per_pixel}, diff: {(ex_per_pixel - per_pixel) / per_pixel * 100 } ===\n\n" ) ex_per_pixel = per_pixel stable_secs = 1.0 if not key_to_press_func: press(context, key_to_press, 1) if interval_secs > stable_secs: time.sleep(interval_secs - stable_secs) # helper.press_key_and_wait(key_to_press, 2.0) else: key_to_press_func(context, key_to_press) # To Be improved. If this button is the only button, there is no actions to compare. -Henry if found == max_tries: debug("Found image but no actions detected !! returning...") return True return False