def __init__(self): self.lines = None self.score = None self.level = None self.start_level = None self.field = None self.preview = None self.gameid = 0 self.piece_stats = PieceStatAccumulator() self.gamestate = GameState.MENU self.cur_piece = None self.cur_piece_das = None self.instant_das = None self.current_time = 0 self.line_clear_anim = None self.colors = Colors() # initialize palette palette_name = config["calibration.palette"] try: with open("nestris_ocr/palettes/%s.json" % (palette_name, ), "r") as file: palette = json.load(file) self.colors.setPalette(palette) except ValueError: print("Warning: Unable to load palette %s" % (palette_name, ))
class FieldView(ImageCanvas): def __init__(self, root, width, height): super().__init__(root, width, height) self.color_table = Colors() self.current_level = None self.color_table.setLevel(0) def updateField(self, field, level): success, level = tryGetInt(level) if success and level != self.current_level: self.color_table.setLevel(level) field = convert_field(field) lut = np.array(self.color_table.colors) image = lut[field] # TODO: check out lut.take which might be faster image = Image.fromarray(image, "RGB") image = image.resize((self.width, self.height), Image.NEAREST) self.updateImage(image)
def __init__(self): self.lines = None self.score = None self.level = None self.start_level = None self.field = None self.preview = None self.gameid = 0 self.piece_stats = PieceStatAccumulator() self.gamestate = GameState.MENU self.cur_piece = None self.cur_piece_das = None self.instant_das = None self.current_time = 0 self.colors = Colors()
def test(method1, method2, level, debug=False): print("Comparing %s vs %s" % (method1, method2)) print("==========") print("level %d" % (level,)) img_field = Image.open( "nestris_ocr/assets/sample_inputs/easiercap/lvl%d/field.png" % (level,) ) img_col1 = Image.open( "nestris_ocr/assets/sample_inputs/easiercap/lvl%d/color1.png" % (level,) ) img_col2 = Image.open( "nestris_ocr/assets/sample_inputs/easiercap/lvl%d/color2.png" % (level,) ) if debug: img_field.show() img_col1.show() img_col2.show() # this is how the field is looked at to pick 10x20 unique pixels img_field = img_field.resize((10, 20), Image.NEAREST) if debug: img_field.show() getter1 = methods[method1] colorsM1 = Colors() colorsM1.setColor1Color2(getter1(img_col1), getter1(img_col2)) print(method1, colorsM1.black, colorsM1.white, colorsM1.color1, colorsM1.color2) getter2 = methods[method2] colorsM2 = Colors() colorsM2.setColor1Color2(getter2(img_col1), getter2(img_col2)) print(method2, colorsM2.black, colorsM2.white, colorsM2.color1, colorsM2.color2) res = getMatchAndDistance(img_field, colorsM1, colorsM2) printRes(res, method1, method2) return res
def runFor(src, level): img = Image.open(src) scaled_img_old = img.resize((10, 20), Image.NEAREST) o_width = img.width + img.height o_height = img.height * 2 result_img = Image.new("RGB", (o_width, o_height), (0, 0, 0, 255)) result_img.paste(img, (0, 0)) result_img.paste(img, (0, img.height)) result_img.paste( scaled_img_old.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width, 0), ) scaled_img_new = Image.new("RGB", (10, 20), (0, 0, 0, 255)) res_img_old = Image.new("RGB", (10, 20), (0, 0, 0, 255)) res_img_new = Image.new("RGB", (10, 20), (0, 0, 0, 255)) spanx = img.width / 10 spany = img.height / 20 np_img = np.array(img, dtype=np.uint16) np_scaled_img = np.array(scaled_img_old, dtype=np.uint8) colors = Colors() colors.setLevel(level) cols = [colors.black, colors.white, colors.color1, colors.color2] for y in range(20): for x in range(10): pix = np_scaled_img[y, x] col_old = getColor(pix, cols) res_img_old.putpixel((x, y), tuple(cols[col_old])) xidx = round(spanx * (x + 0.5)) yidx = round(spany * (y + 0.5)) target = np_img[yidx - 1:yidx + 2, xidx - 1:xidx + 2] pix = [0, 0, 0] # grab 9 pixels in a 3x3 square # and compute average for i in range(3): for j in range(3): tmp = target[i, j] pix[0] += tmp[0] * tmp[0] pix[1] += tmp[1] * tmp[1] pix[2] += tmp[2] * tmp[2] pix[0] = int(sqrt(pix[0] / 9)) pix[1] = int(sqrt(pix[1] / 9)) pix[2] = int(sqrt(pix[2] / 9)) col_new = getColor(pix, cols) scaled_img_new.putpixel((x, y), tuple(pix)) res_img_new.putpixel((x, y), tuple(cols[col_new])) result_img.paste( scaled_img_new.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width, img.height), ) result_img.paste( res_img_old.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width + floor(img.height / 2), 0), ) result_img.paste( res_img_new.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width + floor(img.height / 2), img.height), ) result_img.show()
def runFor(src, level): img = Image.open(src) scaled_img_old = img.resize((10, 20), Image.NEAREST) o_width = img.width + img.height o_height = img.height * 2 result_img = Image.new("RGB", (o_width, o_height), (0, 0, 0, 255)) result_img.paste(img, (0, 0)) result_img.paste(img, (0, img.height)) result_img.paste( scaled_img_old.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width, 0), ) scaled_img_new = Image.new("RGB", (10, 20), (0, 0, 0, 255)) res_img_old = Image.new("RGB", (10, 20), (0, 0, 0, 255)) res_img_new = Image.new("RGB", (10, 20), (0, 0, 0, 255)) spanx = img.width / 10 spany = img.height / 20 np_img = np.array(img, dtype=np.uint16) np_scaled_img = np.array(scaled_img_old, dtype=np.uint8) colors = Colors() colors.setLevel(level) for y in range(20): for x in range(10): pix = np_scaled_img[y, x] col_old = colors.getClosestColorIndex(pix) res_img_old.putpixel((x, y), tuple(colors.getColorByIndex(col_old))) xidx = round(spanx * (x + 0.5)) yidx = round(spany * (y + 0.5)) pix = [0, 0, 0] # grab 9 pixels in a 3x3 square # and compute average for i in range(xidx - 1, xidx + 2): for j in range(yidx - 1, yidx + 2): tmp = np_img[j, i] pix[0] += tmp[0] * tmp[0] pix[1] += tmp[1] * tmp[1] pix[2] += tmp[2] * tmp[2] pix[0] = round(sqrt(pix[0] / 9)) pix[1] = round(sqrt(pix[1] / 9)) pix[2] = round(sqrt(pix[2] / 9)) col_new = colors.getClosestColorIndex(pix) scaled_img_new.putpixel((x, y), tuple(pix)) res_img_new.putpixel((x, y), tuple(colors.getColorByIndex(col_new))) result_img.paste( scaled_img_new.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width, img.height), ) result_img.paste( res_img_old.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width + floor(img.height / 2), 0), ) result_img.paste( res_img_new.resize((floor(img.height / 2), img.height), Image.NEAREST), (img.width + floor(img.height / 2), img.height), ) result_img.show()
class BaseStrategy(object): def __init__(self): self.lines = None self.score = None self.level = None self.start_level = None self.field = None self.preview = None self.gameid = 0 self.piece_stats = PieceStatAccumulator() self.gamestate = GameState.MENU self.cur_piece = None self.cur_piece_das = None self.instant_das = None self.current_time = 0 self.line_clear_anim = None self.colors = Colors() # initialize palette palette_name = config["calibration.palette"] try: with open("nestris_ocr/palettes/%s.json" % (palette_name, ), "r") as file: palette = json.load(file) self.colors.setPalette(palette) except ValueError: print("Warning: Unable to load palette %s" % (palette_name, )) # todo: don't include items not included in config. def to_dict(self): result = {} result["lines"] = dict_zfill(self.lines, 3) result["score"] = dict_zfill(self.score, 6) result["level"] = dict_zfill(self.level, 2) if config["calibration.capture_field"]: result["field"] = self.field if config["calibration.capture_preview"]: result["preview"] = self.preview result["gameid"] = self.gameid if config["stats.enabled"]: result.update(self.piece_stats.toDict()) if config["calibration.capture_das"]: result["cur_piece"] = self.cur_piece result["cur_piece_das"] = self.cur_piece_das result["instant_das"] = self.instant_das if config["calibration.capture_line_clear"]: result["line_clear"] = self.line_clear_anim return result def update(self, timestamp, frame): self.current_time = timestamp self.current_frame = frame if self.gamestate == GameState.MENU: is_newgame = self.update_menu() if is_newgame: self.on_newgame() else: self.update_ingame() def update_menu(self): # returns whether we started a new game raise NotImplementedError("This is an abstract class, silly") def update_ingame(self): raise NotImplementedError("This is an abstract class, silly") def on_newgame(self): self.gameid += 1
def __init__(self, root, width, height): super().__init__(root, width, height) self.color_table = Colors() self.current_level = None self.color_table.setLevel(0)
return Piece.T if g and o and not b and not r: return Piece.L if g and not o and not b and r: return Piece.J if g and o and b and not r: return Piece.S if not g and not o and b and r: return Piece.Z if not g and not o and not b and not r and k: return Piece.EMPTY else: return Piece.UNKNOWN if __name__ == "__main__": # run this from root directory as "python -m nestris_ocr.ocr_algo.piece_stats_spawn" img = Image.open("nestris_ocr/assets/test/spawn_z.png") import time from nestris_ocr.colors import Colors colors = Colors() iterations = 100000 t = time.time() for i in range(iterations): parseImage(img, colors) print(time.time() - t, (time.time() - t) / iterations)
def getColors(level, field): img = Image.open(field) res_img = Image.new( "RGBA", (img.width, img.height), (0, 0, 0, 255), ) res_img.paste(img.convert("RGBA"), (img.width * 0, 0)) # nes pix size, block span and capture are only valid on perfect calibration # perfect calibration implies capturing the right and bottom black edges # still, since we're capturing a safe zone, things should still work with small discrepancies nes_pixels_to_sample = ( (2, 4.5), (4.5, 2), (4.5, 4.5), ) nes_pix_xsize = img.width / 80 nes_pix_ysize = img.height / 160 spanx = nes_pix_xsize * 8 spany = nes_pix_ysize * 8 corner_highlight = Image.new("RGBA", (1, 1), (255, 0, 0, 128)) capture_highlight = Image.new("RGBA", (1, 1), (0, 255, 0, 128)) np_img = np.array(img, dtype=np.uint16) colors = Colors() colors.setLevel(level) color_instances = ([], [], [], []) for y in range(20): for x in range(10): xidx = spanx * x yidx = spany * y # print the top-left corner res_img.paste(corner_highlight, (round(xidx), round(yidx)), corner_highlight) pixels = [] for offsetx, offsety in nes_pixels_to_sample: right = round(xidx + nes_pix_xsize * offsetx) top = round(yidx + nes_pix_ysize * offsety) left = round(xidx + nes_pix_xsize * (offsetx + 1)) bottom = round(yidx + nes_pix_ysize * (offsety + 1)) cap = capture_highlight.resize((left - right, bottom - top)) res_img.paste(cap, (right, top), cap) # grab all pixels in the capture area for i in range(right, left): for j in range(top, bottom): pixels.append(np_img[j, i]) pix = getAverageColor(pixels) col_index = colors.getClosestColorIndex(pix) color_instances[col_index].extend(pixels) if level == 0: res_img.convert("RGB").resize((img.width * 3, img.height * 3)).show() # average the colors col1 = getAverageColor(color_instances[2]) col2 = getAverageColor(color_instances[3]) return col1, col2
# expecting all 4 colors as np.array(dtype=np.uint8) def parseImage(img, colors): img = np.array(img, dtype=np.uint8) return parseImage2(img, colors.black, colors.white, colors.color1, colors.color2) if __name__ == "__main__": # run this from root directory as "python -m nestris_ocr.ocr_algo.board" from nestris_ocr.colors import Colors import nestris_ocr.utils.time as time img = Image.open("nestris_ocr/assets/test/board_lvl7.png") colors = Colors() colors.setLevel(7) iterations = 25000 start = time.time() for i in range(iterations): parseImage(img, colors) elapsed = time.time() - start print(elapsed, elapsed / iterations) print(parseImage(img, colors))