def main(): os.chdir(os.path.dirname(__file__)) # Disable cpu frequency scaling subprocess.check_call(r""" for cpu in /sys/devices/system/cpu/cpufreq/policy*; do echo $cpu cat $cpu/scaling_available_governors echo performance | sudo tee $cpu/scaling_governor freq=$(cat $cpu/cpuinfo_max_freq) echo $freq | sudo tee $cpu/scaling_min_freq done >&2 """, shell=True) print "screenshot,reference,min,avg,max" for fname in glob.glob("images/performance/*-frame.png"): tname = fname.replace("-frame.png", "-reference.png") f = stbt.load_image(fname) t = stbt.load_image(tname) # pylint:disable=cell-var-from-loop times = timeit.repeat(lambda: stbt.match(t, f), number=1, repeat=100) print "%s,%s,%f,%f,%f" % (os.path.basename(fname), os.path.basename(tname), min(times), max(times), sum(times) / len(times))
def test_matching_greyscale_array_with_greyscale_frame(match_method): assert stbt.match( cv2.cvtColor(stbt.load_image("videotestsrc-redblue.png"), cv2.COLOR_BGR2GRAY), frame=cv2.cvtColor(stbt.load_image("videotestsrc-full-frame.png"), cv2.COLOR_BGR2GRAY), match_parameters=mp(match_method=match_method))
def test_load_image_with_unicode_filename(): print(sys.getfilesystemencoding()) shutil.copyfile(_find_file("Rothlisberger.png"), _find_file("Röthlisberger.png")) assert stbt.load_image("Röthlisberger.png") is not None assert stbt.load_image(u"Röthlisberger.png") is not None assert stbt.load_image(u"R\xf6thlisberger.png") is not None
def test_load_image_with_unicode_filename(): print sys.getfilesystemencoding() shutil.copyfile(_find_file("Rothlisberger.png"), _find_file("Röthlisberger.png")) assert stbt.load_image("Röthlisberger.png") is not None assert stbt.load_image(u"Röthlisberger.png") is not None assert stbt.load_image(u"R\xf6thlisberger.png") is not None
def test_that_load_image_looks_in_callers_directory(): # See also the test with the same name in ../test_core.py assert numpy.array_equal( stbt.load_image("videotestsrc-redblue.png"), cv2.imread(os.path.join(os.path.dirname(__file__), "../videotestsrc-redblue-flipped.png"))) with pytest.raises(IOError): stbt.load_image("info.png")
def test_that_load_image_looks_in_callers_directory(): # See also the test with the same name in ../test_core.py assert numpy.array_equal( stbt.load_image("videotestsrc-redblue.png"), cv2.imread( os.path.join(os.path.dirname(__file__), "../videotestsrc-redblue-flipped.png"))) with pytest.raises(IOError): stbt.load_image("info.png")
def test_crop(): f = stbt.load_image("action-panel.png") cropped = stbt.crop(f, stbt.Region(x=1045, y=672, right=1081, bottom=691)) reference = stbt.load_image("action-panel-blue-button.png") assert numpy.array_equal(reference, cropped) # It's a view onto the same memory: assert cropped[0, 0, 0] == f[672, 1045, 0] cropped[0, 0, 0] = 0 assert cropped[0, 0, 0] == f[672, 1045, 0] # Region must be inside the frame (unfortunately this means that you can't # use stbt.Region.ALL): with pytest.raises(ValueError): stbt.crop(f, stbt.Region(x=1045, y=672, right=1281, bottom=721))
def test_that_match_text_still_returns_if_region_doesnt_intersect_with_frame( region): frame = load_image("ocr/menu.png") result = stbt.match_text("Onion Bhaji", frame=frame, region=region) assert result.match is False assert result.region is None assert result.text == "Onion Bhaji"
def test_that_text_region_is_correct_even_with_regions_larger_than_frame(): frame = load_image("ocr/menu.png") text, region, _ = list(iterate_menu())[6] result = stbt.match_text( text, frame=frame, region=region.extend(right=+12800)) assert result assert region.contains(result.region)
def __init__(self, region=stbt.Region.ALL, mask=None, timeout_secs=10, stable_secs=1, dut=None): if dut is None: self.dut = stbt else: self.dut = dut if region is not stbt.Region.ALL and mask is not None: raise ValueError( "You can't specify region and mask at the same time") self.region = region self.mask_image = None if isinstance(mask, numpy.ndarray): self.mask_image = mask elif mask: self.mask_image = stbt.load_image(mask) self.timeout_secs = timeout_secs self.stable_secs = stable_secs self.frames = self.dut.frames() self.diff = strict_diff self.expiry_time = None
def test_match_text_stringify_result(): frame = load_image("ocr/menu.png") result = stbt.match_text(u"Onion Bhaji", frame=frame) assert re.match( r"TextMatchResult\(time=None, match=True, region=Region\(.*\), " r"frame=<1280x720x3>, text=u?'Onion Bhaji'\)", str(result))
def test_that_match_text_gives_tesseract_a_hint(): frame = load_image("ocr/itv-player.png") if "ITV Player" in stbt.ocr(frame=frame): raise SkipTest("Tesseract doesn't need a hint") if "ITV Player" not in stbt.ocr(frame=frame, tesseract_user_words=["ITV"]): raise SkipTest("Giving tesseract a hint doesn't help") assert stbt.match_text("ITV Player", frame=frame)
def test_that_ligatures_and_ambiguous_punctuation_are_normalised(): frame = load_image('ocr/ambig.png') text = stbt.ocr(frame) text = text.replace("horizonta|", "horizontal") # for tesseract < 3.03 assert ligature_text == text assert stbt.match_text("em-dash,", frame) assert stbt.match_text(u"em\u2014dash,", frame)
def test_that_match_all_obeys_region(match_method): matches = sorted(m.region for m in stbt.match_all( "button.png", frame=stbt.load_image("buttons.png"), match_parameters=mp(match_method=match_method), region=stbt.Region(x=160, y=60, right=340, bottom=190))) print matches assert matches == [stbt.Region(x, y, width=135, height=44) for x, y in [ (177, 75), (177, 119)]]
def test_that_match_all_obeys_region(match_method): matches = sorted(m.region for m in stbt.match_all( "button.png", frame=stbt.load_image("buttons.png"), match_parameters=mp(match_method=match_method), region=stbt.Region(x=160, y=60, right=340, bottom=190))) print(matches) assert matches == [stbt.Region(x, y, width=135, height=44) for x, y in [ (177, 75), (177, 119)]]
def test_that_match_all_finds_all_matches(match_method): matches = list( m.region for m in stbt.match_all('button.png', frame=stbt.load_image('buttons.png'), match_parameters=mp( match_method=match_method))) print(matches) assert plain_buttons == sorted(matches)
def test_match_all_with_transparent_reference_image(): frame = stbt.load_image("buttons-on-blue-background.png") matches = list(m.region for m in stbt.match_all( "button-transparent.png", frame=frame)) print(matches) assert overlapped_button not in matches assert (sorted(plain_buttons + labelled_buttons + [overlapping_button]) == sorted(matches))
def test_match_text_stringify_result(): frame = load_image("ocr/menu.png") result = stbt.match_text(u"Onion Bhaji", frame=frame) assert re.match( r"TextMatchResult\(time=None, match=True, region=Region\(.*\), " r"frame=<1280x720x3>, text=u'Onion Bhaji'\)", str(result))
def test_that_with_old_tesseract_ocr_raises_an_exception_with_patterns(): # pylint: disable=W0212 if _tesseract_version() >= distutils.version.LooseVersion('3.03'): raise SkipTest('tesseract is too new') stbt.ocr(frame=load_image('ocr/192.168.10.1.png'), mode=stbt.OcrMode.SINGLE_WORD, tesseract_user_patterns=[r'\d\*.\d\*.\d\*.\d\*'])
def test_match_all_with_transparent_reference_image(): frame = stbt.load_image("buttons-on-blue-background.png") matches = list(m.region for m in stbt.match_all( "button-transparent.png", frame=frame)) print matches assert overlapped_button not in matches assert (sorted(plain_buttons + labelled_buttons + [overlapping_button]) == sorted(matches))
def test_ocr_text_color(image, color, expected, region): frame = load_image(image) mode = stbt.OcrMode.SINGLE_LINE assert expected not in stbt.ocr(frame, region, mode) assert expected == stbt.ocr(frame, region, mode, text_color=color) assert not stbt.match_text(expected, frame, region, mode) assert stbt.match_text(expected, frame, region, mode, text_color=color)
def test_ocr_on_static_images(image, expected_text, region, mode): kwargs = {"region": region} if mode is not None: kwargs["mode"] = mode text = stbt.ocr(load_image("ocr/" + image), **kwargs) assert text == expected_text # Don't leak python future newtypes assert type(text).__name__ in ["unicode", "str"]
def test_ocr_text_color_threshold(): f = load_image("ocr/blue-search-white-guide.png") c = (220, 220, 220) assert stbt.ocr(f) != "Guide" assert stbt.ocr(f, text_color=c) != "Guide" assert stbt.ocr(f, text_color=c) != "Guide" assert stbt.ocr(f, text_color=c, text_color_threshold=50) == "Guide" with temporary_config({'ocr.text_color_threshold': '50'}): assert stbt.ocr(f, text_color=c) == "Guide"
def test_is_screen_black_with_numpy_mask_and_region(): frame = stbt.load_image("videotestsrc-full-frame.png") region = stbt.Region(x=160, y=180, right=320, bottom=240) mask = numpy.zeros((60, 160), dtype=numpy.uint8) mask[:, :80] = 255 assert stbt.is_screen_black(frame, mask, 20, region) mask[:, :] = 255 assert not stbt.is_screen_black(frame, mask, 20, region)
def test_that_setting_config_options_has_an_effect(): # Unfortunately there are many tesseract config options and they are very # complicated so it's difficult to write a test that tests that a config # option is having the correct effect. Due to the difficulty in determining # "correctness" instead here we test that setting a config option has an # effect at all. This at least excercises our code which sets config # options. I'm not happy about this and I hope to be able to replace this # once we have more experience with these settings in the real world. if _tesseract_version() >= LooseVersion('3.04'): hocr_mode_config = { "tessedit_create_txt": 0, "tessedit_create_hocr": 1} else: hocr_mode_config = { "tessedit_create_hocr": 1} assert (stbt.ocr(frame=load_image('ocr/ambig.png'), tesseract_config=hocr_mode_config) != stbt.ocr(frame=load_image('ocr/ambig.png')))
def test_tesseract_user_patterns(patterns): # pylint:disable=protected-access if _tesseract_version() < LooseVersion('3.03'): raise SkipTest('tesseract is too old') # Now the real test: assert u'192.168.10.1' == stbt.ocr( frame=load_image('ocr/192.168.10.1.png'), mode=stbt.OcrMode.SINGLE_WORD, tesseract_user_patterns=patterns)
def test_that_setting_config_options_has_an_effect(): # Unfortunately there are many tesseract config options and they are very # complicated so it's difficult to write a test that tests that a config # option is having the correct effect. Due to the difficulty in determining # "correctness" instead here we test that setting a config option has an # effect at all. This at least excercises our code which sets config # options. I'm not happy about this and I hope to be able to replace this # once we have more experience with these settings in the real world. if _tesseract_version() >= LooseVersion('3.04'): hocr_mode_config = { "tessedit_create_txt": 0, "tessedit_create_hocr": 1 } else: hocr_mode_config = {"tessedit_create_hocr": 1} assert (stbt.ocr(frame=load_image('ocr/ambig.png'), tesseract_config=hocr_mode_config) != stbt.ocr(frame=load_image('ocr/ambig.png')))
def test_that_match_text_still_returns_if_region_doesnt_intersect_with_frame( region): frame = load_image("ocr/menu.png") result = stbt.match_text("Onion Bhaji", frame=frame, region=region) assert result.match is False assert result.region is None assert result.text == "Onion Bhaji" # Avoid future.types.newtypes in return values assert type(result.text).__name__ in ["str", "unicode"]
def test_that_match_all_can_find_labelled_matches(match_method): frame = stbt.load_image('buttons.png') matches = list(m.region for m in stbt.match_all( 'button.png', frame=frame, match_parameters=mp(match_method=match_method, confirm_method=stbt.ConfirmMethod.NONE))) print matches assert overlapped_button not in matches assert sorted(plain_buttons + labelled_buttons + [overlapping_button]) == \ sorted(matches)
def test_that_match_all_can_find_labelled_matches(match_method): frame = stbt.load_image('buttons.png') matches = list(m.region for m in stbt.match_all( 'button.png', frame=frame, match_parameters=mp(match_method=match_method, confirm_method=stbt.ConfirmMethod.NONE))) print(matches) assert overlapped_button not in matches assert sorted(plain_buttons + labelled_buttons + [overlapping_button]) == \ sorted(matches)
def test_ocr_text_color_threshold(): f = load_image("ocr/blue-search-white-guide.png") c = (220, 220, 220) assert stbt.ocr(f) != "Guide" # pylint:disable=fixme # TODO: Find an example where text_color_threshold is necessary. Since # tesseract 4.0.0 the default text_color_threshold actually works. # assert stbt.ocr(f, text_color=c) != "Guide" assert stbt.ocr(f, text_color=c, text_color_threshold=50) == "Guide" with temporary_config({'ocr.text_color_threshold': '50'}): assert stbt.ocr(f, text_color=c) == "Guide"
def test_pyramid_roi_too_small(frame, image, match_method, match_threshold): # This is a regression test for an error that was seen with a particular # frame from a single test-run, with SQDIFF_NORMED: # cv2.error: (-215) _img.size().height <= _templ.size().height && # _img.size().width <= _templ.size().width in function matchTemplate with scoped_debug_level(2): stbt.match(image, frame=stbt.load_image(frame), match_parameters=stbt.MatchParameters( match_method=match_method, match_threshold=match_threshold))
def test_match_all_with_an_image_that_matches_everywhere(match_method): matches = sorted(m.region for m in stbt.match_all( "repeating-pattern.png", frame=stbt.load_image("repeating-pattern-full-frame.png"), match_parameters=mp(match_method=match_method))) expected_matches = sorted([stbt.Region(x, y, width=16, height=16) for x in range(0, 320, 16) for y in range(0, 240, 16)]) print matches assert matches == expected_matches
def test_match_all_with_an_image_that_matches_everywhere(match_method): matches = sorted(m.region for m in stbt.match_all( "repeating-pattern.png", frame=stbt.load_image("repeating-pattern-full-frame.png"), match_parameters=mp(match_method=match_method))) expected_matches = sorted([stbt.Region(x, y, width=16, height=16) for x in range(0, 320, 16) for y in range(0, 240, 16)]) print(matches) assert matches == expected_matches
def test_transparent_reference_image_with_hard_edge(): # This is a regression test for a bug in the pyramid optimisation when # the reference image has a very small number of pixels, or the only non- # transparent pixels are near the edges of reference image: # At the smaller pyramid levels, the pixels near the edge of the reference # image won't match exactly because the corresponding pixels in the # down-scaled frame have been blurred. We also blur the reference image # before down-scaling, but since it doesn't know what's outside its edges, # it won't have the blurring near the edge. frame = stbt.load_image("images/regression/roku-tile-frame.png") m = stbt.match("images/regression/roku-tile-selection.png", frame=frame) assert m assert stbt.Region(x=325, y=145, right=545, bottom=325).contains(m.region)
def test_that_match_all_can_be_used_with_ocr_to_read_buttons(): # Demonstrates how match_all can be used with ocr for UIs consisting of text # on buttons frame = stbt.load_image('buttons.png') text = [ stbt.ocr(frame=stbt.crop( frame, m.region.extend(x=30, y=10, right=-30, bottom=-10))) for m in stbt.match_all('button-transparent.png', frame=frame)] text = sorted([t for t in text if t not in ['', '\\s']]) print(text) assert text == [u'Button 1', u'Button 2', u'Buttons']
def test_that_match_all_can_be_used_with_ocr_to_read_buttons(): # Demonstrates how match_all can be used with ocr for UIs consisting of text # on buttons frame = stbt.load_image('buttons.png') text = [ stbt.ocr(frame=stbt.crop( frame, m.region.extend(x=30, y=10, right=-30, bottom=-10))) for m in stbt.match_all('button-transparent.png', frame=frame)] text = sorted([t for t in text if t not in ['', '\\s']]) print text assert text == [u'Button 1', u'Button 2', u'Buttons']
def test_that_ligatures_and_ambiguous_punctuation_are_normalised(): frame = load_image('ocr/ambig.png') text = stbt.ocr(frame) for bad, good in [ # tesseract 3.02 ("horizonta|", "horizontal"), # tesseract 4.00 with tessdata 590567f ("siIIyness", "sillyness"), ("Iigatures", "ligatures"), ]: text = text.replace(bad, good) assert ligature_text == text assert stbt.match_text("em-dash,", frame) assert stbt.match_text(u"em\u2014dash,", frame)
def test_that_ocr_engine_has_an_effect(): if _tesseract_version() < LooseVersion("4.0"): raise SkipTest('tesseract is too old') f = load_image("ocr/ambig.png") # This is a regression in tesseract 4.0's legacy engine, compared to 3.04: assert "sillyness" not in stbt.ocr(f, engine=stbt.OcrEngine.TESSERACT) assert "sillyness" not in stbt.ocr(f) # ...but the new LSTM engine does read it correctly: assert "sillyness" in stbt.ocr(f, engine=stbt.OcrEngine.LSTM) with temporary_config({'ocr.engine': 'LSTM'}): assert "sillyness" in stbt.ocr(f)
def test_transparent_reference_image_false_negative_caused_by_pyramid( frame, image, expected_region): # This is a regression test for a bug in the pyramid optimisation when # the reference image has a very small number of pixels, or the only non- # transparent pixels are near the edges of reference image: # At the smaller pyramid levels, the pixels near the edge of the reference # image won't match exactly because the corresponding pixels in the # down-scaled frame have been blurred. We also blur the reference image # before down-scaling, but since it doesn't know what's outside its edges, # it won't have the blurring near the edge. frame = stbt.load_image(frame) m = stbt.match(image, frame=frame) assert m assert expected_region.contains(m.region)
def test_that_text_location_is_recognised(): frame = load_image("ocr/menu.png") def test(text, region, upsample): result = stbt.match_text(text, frame=frame, upsample=upsample) assert result assert region.contains(result.region) # pylint:disable=no-member for text, region, multiline in iterate_menu(): # Don't currently support multi-line comments if multiline: continue yield (test, text, region, True) yield (test, text, region, False)
def test_is_screen_black_debug(): # So that the output directory name doesn't depend on how many tests # were run before this one. ImageLogger._frame_number = itertools.count(1) # pylint:disable=protected-access f = stbt.load_image("videotestsrc-full-frame.png") with scoped_curdir(), scoped_debug_level(2): stbt.is_screen_black(f) stbt.is_screen_black(f, mask="videotestsrc-mask-non-black.png") stbt.is_screen_black(f, mask="videotestsrc-mask-no-video.png") stbt.is_screen_black(f, region=stbt.Region(0, 0, 160, 120)) files = subprocess.check_output("find stbt-debug | sort", shell=True) assert files == dedent("""\ stbt-debug stbt-debug/00001 stbt-debug/00001/grey.png stbt-debug/00001/index.html stbt-debug/00001/non_black.png stbt-debug/00001/source.png stbt-debug/00002 stbt-debug/00002/grey.png stbt-debug/00002/index.html stbt-debug/00002/mask.png stbt-debug/00002/non_black.png stbt-debug/00002/source.png stbt-debug/00003 stbt-debug/00003/grey.png stbt-debug/00003/index.html stbt-debug/00003/mask.png stbt-debug/00003/non_black.png stbt-debug/00003/source.png stbt-debug/00004 stbt-debug/00004/grey.png stbt-debug/00004/index.html stbt-debug/00004/non_black.png stbt-debug/00004/source.png """) assert_expected("stbt-debug-expected-output/is_screen_black")
def test_that_results_dont_overlap(match_method): # This is a regression test for a bug seen in an earlier implementation of # `match_all`. frame = stbt.load_image("action-panel.png") all_matches = set() for m in stbt.match_all("action-panel-template.png", frame=frame, match_parameters=mp(match_method=match_method)): print m assert m.region not in all_matches, "Match %s already seen:\n %s" % ( m, "\n ".join(str(x) for x in all_matches)) assert all(stbt.Region.intersect(m.region, x) is None for x in all_matches) all_matches.add(m.region) assert all_matches == set([ stbt.Region(x=135, y=433, width=222, height=40), stbt.Region(x=135, y=477, width=222, height=40), stbt.Region(x=135, y=521, width=222, height=40), stbt.Region(x=135, y=565, width=222, height=40), stbt.Region(x=135, y=609, width=222, height=40)])
def test_that_match_fast_path_is_equivalent(): from _stbt.match import _load_image black_reference = black(10, 10) almost_black_reference = black(10, 10, value=1) black_frame = black(1280, 720) almost_black_frame = black(1280, 720, value=2) images = [ ("videotestsrc-redblue.png", "videotestsrc-full-frame.png"), ("action-panel.png", "action-panel.png"), ("videotestsrc-full-frame.png", "videotestsrc-full-frame.png"), ("videotestsrc-redblue-flipped.png", "videotestsrc-full-frame.png"), ("button.png", "black-full-frame.png"), ("completely-transparent.png", "buttons-on-blue-background.png"), ("action-panel-template.png", "action-panel.png"), ("button.png", "buttons.png"), (black_reference, black_frame), (almost_black_reference, black_frame), (almost_black_reference, almost_black_frame), ("repeating-pattern.png", "repeating-pattern-full-frame.png"), ("button-transparent.png", "buttons.png"), ] for reference, frame in images: if isinstance(frame, (str, unicode)): frame = stbt.load_image(frame, cv2.IMREAD_COLOR) reference = _load_image(reference) orig_m = stbt.match(reference, frame=frame) fast_m = stbt.match(reference, frame=frame, region=orig_m.region) assert orig_m.time == fast_m.time assert orig_m.match == fast_m.match assert orig_m.region == fast_m.region assert bool(orig_m) == bool(fast_m) assert orig_m.first_pass_result == pytest.approx( fast_m.first_pass_result, abs=0.0001 if orig_m else 0.05) assert (orig_m.frame == fast_m.frame).all() if isinstance(orig_m.image, numpy.ndarray): assert (orig_m.image == fast_m.image).all() else: assert orig_m.image == fast_m.image
def test_matchresult_region_when_first_pyramid_level_fails_to_match(): f = stbt.load_image("videotestsrc-full-frame.png") r = stbt.match("videotestsrc-redblue-flipped.png", frame=f).region assert r.width == 92 assert r.height == 160
def test_completely_transparent_reference_image(): f = stbt.load_image("buttons-on-blue-background.png") assert len(list(stbt.match_all( "completely-transparent.png", frame=f))) == 18
def test_transparent_reference_image_with_sqdiff_normed_raises_valueerror(): f = stbt.load_image("buttons-on-blue-background.png") with pytest.raises(ValueError): stbt.match("button-transparent.png", f, match_parameters=mp(stbt.MatchMethod.SQDIFF_NORMED))
def test_match_fast_path(): # This is just an example of typical use assert stbt.match("action-panel-prototype.png", frame=stbt.load_image("action-panel.png"))
def test_that_match_rejects_greyscale_array(match_method): grey = cv2.cvtColor(stbt.load_image("black.png"), cv2.COLOR_BGR2GRAY) with pytest.raises(ValueError): stbt.match(grey, frame=black(), match_parameters=mp(match_method=match_method))