def prepare_image(input_image, logger, options): dithered_image = input_image.convert(mode="1", dither=Image.FLOYDSTEINBERG) if options.auto_crop: bbox = dithered_image.getbbox() if bbox is not None: dithered_image = dithered_image.crop(bbox) size = dithered_image.size logger.debug("Cropped image has size: %s" % str(size)) new_size = fix_size_to_block_multiple(size) if new_size != size: logger.info("Resizing picture to: %s" % str(new_size)) new_image = Image.new("1", new_size) new_image.paste(dithered_image, box=(0, 0, dithered_image.size[0], dithered_image.size[1])) dithered_image = new_image else: logger.error("Cannot crop a blank image") block_image = BlockImage(dithered_image) block_image.deduplicate_blocks() return block_image, dithered_image
def test_deduplicated_images_is_unaltered(self): input_image = construct_redundant_image() im = BlockImage(input_image) im.deduplicate_blocks() new_image = im.get_image() are_images_equal(input_image, new_image)
def test_random_search(self): print('\nTests random search for a single block') block = BlockImage(1, 1) self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) random_search(block, BlockPattern.WhiteSquare, []) self.assertEqual(block.get_pattern(), BlockPattern.WhiteSquare)
def test_memory_search(self): print('\nTests memory search') actions = memory_search(BlockImage(), BlockPattern.BlackTopLeftCornerSquare, []) block = BlockImage() visited = [(block.get_face(), block.get_pattern())] for action in actions: block.execute_action(action) visited.append((block.get_face(), block.get_pattern())) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopLeftCornerSquare) self.assertEqual(len(visited), len(set(visited)))
def _setup_puzzle(self): self.block = None self.image_path = PUZZLE_OPTIONS[self.name] self.action_history = [] self.num_rows, self.num_cols, self.bgr_len = (imread( self.image_path).shape) # Glance factor presumes that the puzzle is a square min_glance_factor = float(BLOCK_LENGTH - 10) / self.num_cols if not self.glance_factor >= min_glance_factor: raise Exception( "Specified glance factor {} must be at least {} to cover one square" .format(self.glance_factor, min_glance_factor)) # Start with a blank slate self.image = np.zeros((self.num_rows, self.num_cols, self.bgr_len), np.uint8) self.unsolved_pieces = [] # Represents as top left coordinate for r in range(0, self.num_rows - EDGE_OFFSET, BLOCK_LENGTH): for c in range(0, self.num_cols - EDGE_OFFSET, BLOCK_LENGTH): self.unsolved_pieces.append((r, c)) self.block_bank = [ BlockImage(1, i + 1, self) for i in range(len(self.unsolved_pieces)) ] self.solved_pieces = {piece: None for piece in self.unsolved_pieces} self.puzzle_memory_loss_counter = 0
def test_blocks_are_cut_from_input_image(self): input_image = construct_test_image() im = BlockImage(input_image) list_of_ones = [(0, 0), (7, 0), (1, 0), (6, 0), (2, 0), (5, 0)] for block_index in range(im.block_count): block_to_test = im.blocks[block_index] self.assertEqual(block_to_test.size, (BLOCK_WIDTH, BLOCK_HEIGHT), "Block %i" % (block_index, )) self.assertEqual(block_to_test.mode, "1", "Block %i" % (block_index, )) for y in range(BLOCK_HEIGHT): for x in range(BLOCK_WIDTH): pixel = block_to_test.getpixel((x, y)) if (x, y) == list_of_ones[block_index]: self.assertEqual( pixel, 1, "Coordinates %i (%i, %i)" % (block_index, x, y)) else: self.assertEqual( pixel, 0, "Coordinates %i (%i, %i)" % (block_index, x, y))
def test_basic_outputs_index_data(self): im = BlockImage(construct_redundant_image()) basic = ImageToBasic(im) found_data = False for line in basic: if "DATA 32,32,33,33,34,34" in line: found_data = True self.assertTrue(found_data, "DATA section was not found")
def test_beeline_search_complex(self): print('\nTests beeline search, complex sequence') actions = beeline_search(BlockImage(3), BlockPattern.BlackTopLeftCornerSquare, []) block = BlockImage(3) self.assertEqual(block.get_pattern(), BlockPattern.WhiteSquare) self.assertTrue(len(actions) == 2) for action in actions: block.execute_action(action) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopLeftCornerSquare)
def test_random_search(self): print('\nTests random search') actions = random_search(BlockImage(), BlockPattern.BlackTopLeftCornerSquare, []) block = BlockImage() for action in actions: block.execute_action(action) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopLeftCornerSquare)
def test_peek(self): print('\nTests peek') block = BlockImage(6) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomRightCornerSquare) self.assertEqual(block.peek_action(BlockAction.RotateRight), (6, BlockPattern.BlackBottomLeftCornerSquare)) self.assertEqual(block.peek_action(BlockAction.RotateLeft), (6, BlockPattern.BlackTopRightCornerSquare)) self.assertEqual(block.peek_action(BlockAction.GoToFaceThree), (3, BlockPattern.WhiteSquare)) with self.assertRaises(Exception) as context: block.peek_action(BlockAction.GoToFaceOne) self.assertEqual( str(context.exception), "Invalid action BlockAction.GoToFaceOne for " + "Block,1,Face,6,Pattern,BlackBottomRightCornerSquare")
def test_invalid_sequence(self): print('\nTests invalid block sequence') block = BlockImage() self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) with self.assertRaises(Exception) as context: block.execute_action(BlockAction.GoToFaceSix) self.assertEqual(str(context.exception), "Can't go from 1 to 6")
def test_basic_centers_the_image(self): im = BlockImage(construct_redundant_image()) basic = ImageToBasic(im) found_x_cursor_shift = False found_y_cursor_shift = False x_cursor_shift = "CURSORX X+19" y_cursor_shift = "CURSORY Y+11" for line in basic: if x_cursor_shift in line: found_x_cursor_shift = True if y_cursor_shift in line: found_y_cursor_shift = True self.assertTrue(found_x_cursor_shift, "cursor shift for X not found") self.assertTrue(found_y_cursor_shift, "cursor shift for Y not found")
def test_basic_contains_loops_for_blocks(self): im = BlockImage(construct_redundant_image()) basic = ImageToBasic(im) found_x_loop = False found_y_loop = False x_loop = "FOR X=0 TO 1" y_loop = "FOR Y=0 TO 2" for line in basic: if x_loop in line: found_x_loop = True if y_loop in line: found_y_loop = True self.assertTrue(found_x_loop, "for loop for X not found") self.assertTrue(found_y_loop, "for loop for Y not found")
def test_reduce_blocks_removes_duplicates(self): input_image = construct_redundant_image() im = BlockImage(input_image) self.assertEqual(im.block_count, 6) self.assertEqual(im.get_unique_block_count(), 6) im.deduplicate_blocks() self.assertEqual(im.blocks[0], im.blocks[1]) self.assertNotEqual(im.blocks[1], im.blocks[2]) self.assertEqual(im.blocks[2], im.blocks[3]) self.assertNotEqual(im.blocks[3], im.blocks[4]) self.assertEqual(im.blocks[4], im.blocks[5]) self.assertEqual(im.get_unique_block_count(), 3)
def test_basic_outputs_encoded_blocks(self): im = BlockImage(construct_redundant_image()) basic = ImageToBasic(im) character_count = 0 found_char_1 = False found_char_2 = False found_char_3 = False for line in basic: if "SETEG " in line: character_count += 1 if "80000000000000000000" in line: found_char_1 = True if "40000000000000000000" in line: found_char_2 = True if "00100000000000000000" in line: found_char_3 = True self.assertEqual(3, character_count) self.assertTrue(found_char_1, "Did not found Encoded Character 1") self.assertTrue(found_char_2, "Did not found Encoded Character 2") self.assertTrue(found_char_3, "Did not found Encoded Character 3")
def test_block_image_asks_for_input_image(self): input_image = Image.new("1", (16, 30)) im = BlockImage(input_image) self.assertEqual(im.size, (16, 30)) self.assertEqual(im.block_size, (2, 3)) self.assertEqual(im.block_count, 6)
def test_complex_sequence(self): print('\nTests block complex sequence') block = BlockImage() self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) block.execute_action(BlockAction.GoToFaceFour) self.assertEqual(block.get_face(), 4) self.assertEqual(block.get_pattern(), BlockPattern.BlackSquare) block.execute_action(BlockAction.GoToFaceThree) self.assertEqual(block.get_face(), 3) self.assertEqual(block.get_pattern(), BlockPattern.WhiteSquare) block.execute_action(BlockAction.RotateRight) self.assertEqual(block.get_face(), 3) self.assertEqual(block.get_pattern(), BlockPattern.WhiteSquare) block.execute_action(BlockAction.GoToFaceOne) self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomRightCornerSquare) block.execute_action(BlockAction.GoToFaceFive) self.assertEqual(block.get_face(), 5) self.assertEqual(block.get_pattern(), BlockPattern.BlackSquare) block.execute_action(BlockAction.GoToFaceSix) self.assertEqual(block.get_face(), 6) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomLeftCornerSquare) block.execute_action(BlockAction.RotateRight) self.assertEqual(block.get_face(), 6) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopLeftCornerSquare) block.execute_action(BlockAction.GoToFaceThree) self.assertEqual(block.get_face(), 3) self.assertEqual(block.get_pattern(), BlockPattern.WhiteSquare) block.execute_action(BlockAction.GoToFaceOne) self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomLeftCornerSquare) block.execute_action(BlockAction.RotateLeft) self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomRightCornerSquare)
def test_simple_sequence_two(self): print('\nTests simple sequence two') block = BlockImage() self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) block.execute_action(BlockAction.RotateRight) self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomRightCornerSquare) block.execute_action(BlockAction.GoToFaceFour) self.assertEqual(block.get_face(), 4) self.assertEqual(block.get_pattern(), BlockPattern.BlackSquare) block.execute_action(BlockAction.GoToFaceSix) self.assertEqual(block.get_face(), 6) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomLeftCornerSquare)
def test_beeline_search_rotations(self): print('\nTests beeline search, rotations') actions = beeline_search(BlockImage(), BlockPattern.BlackTopLeftCornerSquare, []) block = BlockImage() self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) self.assertEqual(len(actions), 1) block.execute_action(actions[0]) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopLeftCornerSquare) actions = beeline_search(BlockImage(), BlockPattern.BlackBottomLeftCornerSquare, []) block = BlockImage() self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) # Since beeline search is somewhat stochastic, these test rely on heuristics self.assertEqual(len(actions), 2) for action in actions: block.execute_action(action) self.assertEqual(block.get_pattern(), BlockPattern.BlackBottomLeftCornerSquare)
def test_basic_first_line_is_10(self): im = BlockImage(construct_redundant_image()) basic = ImageToBasic(im) all_lines = "\n".join(basic) self.assertEqual("10 ", all_lines[:3])
def test_simple_sequence_one(self): print('\nTests simple block sequence one') block = BlockImage() self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare) block.execute_action(BlockAction.GoToFaceFour) self.assertEqual(block.get_face(), 4) self.assertEqual(block.get_pattern(), BlockPattern.BlackSquare) block.execute_action(BlockAction.GoToFaceFive) self.assertEqual(block.get_face(), 5) self.assertEqual(block.get_pattern(), BlockPattern.BlackSquare) block.execute_action(BlockAction.GoToFaceOne) self.assertEqual(block.get_face(), 1) self.assertEqual(block.get_pattern(), BlockPattern.BlackTopRightCornerSquare)
def test_image_to_basic_takes_a_block_image(self): im = BlockImage(construct_redundant_image()) basic = ImageToBasic(im) self.assertGreater(basic.get_line_count(), 0)