예제 #1
0
def run_paikin_tal_driver(img_files, puzzle_type, piece_width):
    """
    Runs the Paikin and Tal image solver.

    Args:
        img_files ([str]): An array of one or more image file path(s).
        puzzle_type (Optional PuzzleType): Type of the puzzle to solve
        piece_width (Optional int): Width of a puzzle piece in pixels.
    """

    image_filenames = config.add_image_folder_path(img_files)

    # Print the names of the images being solved:
    logging.info("Standard Paikin & Tal Driver")
    puzzle_importer.log_puzzle_filenames(image_filenames)

    # When skipping placement, simply import the solved results.
    if SKIP_PLACEMENT:
        paikin_tal_solver = PaikinTalSolver.pickle_import_after_standard_run_placement(image_filenames, puzzle_type)
    else:
        paikin_tal_solver = run_paikin_tal_solver(image_filenames, puzzle_type, piece_width)

    # Get the results
    paikin_tal_solver.segment(color_segments=True, perform_segment_cleaning=True)
    (pieces_partitioned_by_puzzle_id, _) = paikin_tal_solver.get_solved_puzzles()

    timestamp = time.time()
    Puzzle.output_results_information_and_puzzles(
        PuzzleSolver.PaikinTal, image_filenames, paikin_tal_solver, pieces_partitioned_by_puzzle_id, timestamp
    )
예제 #2
0
    def _output_reconstructed_puzzle_image_files(self, output_puzzles):
        """
        Saves images files for each of the final reconstructed output puzzles.

        Args:
            output_puzzles (List[Puzzle]): Set of final solved puzzles
        """
        for puzzle in output_puzzles:
            filename = Puzzle.make_image_filename(PuzzleSolver.MultiPuzzle,
                                                  self._image_filenames,
                                                  "multipuzzle_reconstructed",
                                                  Puzzle.OUTPUT_IMAGE_DIRECTORY,
                                                  self._paikin_tal_solver.puzzle_type,
                                                  self._start_timestamp,
                                                  puzzle_id=puzzle.id_number)
            Puzzle.save_to_file(puzzle, filename)
예제 #3
0
def build_multipuzzle_solver(image_filenames, puzzle_type, piece_width):
    """
    Build the multipuzzle solver object.

    Args:
        image_filenames (List[str]): List of puzzle file paths.
        puzzle_type (PuzzleType): Type of the puzzle to solve.
        piece_width (int): Puzzle piece width in number of pixels

    Returns (MultiPuzzleSolver): The multipuzzle solver object built from the input image files.
    """

    pieces, puzzles = Puzzle.get_combined_pieces_multiple_images(image_filenames, piece_width)

    pickle_filename = PickleHelper.build_filename("multipuzzle_distances", image_filenames, puzzle_type)

    # Initialize the distance information
    if _FORCE_RECALCULATE_DISTANCES or not os.path.exists(pickle_filename):
        multipuzzle_solver = MultiPuzzleSolver(image_filenames, pieces, top_level_calculate_asymmetric_distance,
                                               puzzle_type)

        if _POST_INITIAL_CONSTRUCTION_PICKLE_EXPORT:
            PickleHelper.exporter(multipuzzle_solver, pickle_filename)
        return multipuzzle_solver

    # Read the pickle information from the
    else:
        multipuzzle_solver = PickleHelper.importer(pickle_filename)
        multipuzzle_solver.reset_timestamp()
        return multipuzzle_solver
예제 #4
0
    def test_side_of_primary_adjacent_to_other_piece(self):
        # Check bottom
        primary = PuzzleLocation(1, 47, 48)
        other = PuzzleLocation(1, 48, 48)
        assert PuzzlePieceSide.bottom == Puzzle.get_side_of_primary_adjacent_to_other_piece(primary, other)

        # Check bottom
        primary = PuzzleLocation(1, 48, 48)
        other = PuzzleLocation(1, 47, 48)
        assert PuzzlePieceSide.top == Puzzle.get_side_of_primary_adjacent_to_other_piece(primary, other)

        # Check right
        primary = PuzzleLocation(1, 48, 47)
        other = PuzzleLocation(1, 48, 48)
        assert PuzzlePieceSide.right == Puzzle.get_side_of_primary_adjacent_to_other_piece(primary, other)

        # Check left
        primary = PuzzleLocation(1, 48, 49)
        other = PuzzleLocation(1, 48, 48)
        assert PuzzlePieceSide.left == Puzzle.get_side_of_primary_adjacent_to_other_piece(primary, other)
예제 #5
0
    def _build_output_puzzles(self):
        """
        Constructs the final output puzzles that are to be returned by the multiple puzzle solver.

        Returns (List[Puzzle]):
            List of the final solved puzzles.
        """
        solved_puzzles, _ = self._paikin_tal_solver.get_solved_puzzles()

        # Build the results information
        Puzzle.output_results_information_and_puzzles(PuzzleSolver.MultiPuzzle, self._image_filenames,
                                                      self._paikin_tal_solver, solved_puzzles, self._start_timestamp)

        # Merge the pieces into a set of solved puzzles
        output_puzzles = [Puzzle.reconstruct_from_pieces(solved_puzzles[i], i) for i in xrange(0, len(solved_puzzles))]

        # Optionally export the solved image files.
        if MultiPuzzleSolver._SAVE_FINAL_PUZZLE_IMAGES:
            self._output_reconstructed_puzzle_image_files(output_puzzles)

        return output_puzzles
예제 #6
0
    def _make_image_filename(self, filename_descriptor):
        """
        Creates an image file name with the specified descriptor included.

        Args:
            filename_descriptor (str): Filename descriptor for the output file

        Returns (str):
            Standardized filename with directory
        """
        return Puzzle.make_image_filename(PuzzleSolver.MultiPuzzle, self._image_filenames, filename_descriptor,
                                          Puzzle.OUTPUT_IMAGE_DIRECTORY, self._paikin_tal_solver.puzzle_type,
                                          self._start_timestamp)
예제 #7
0
def run_paikin_tal_solver(image_filenames, puzzle_type, piece_width):
    """
    Paikin & Tal Solver

    This function takes a set of inputs and runs the Paikin and Tal solver.  It can be sped-up by importing
    the calculations of distances from existing Pickle files.

    Args:
        image_filenames (List[String]): Path to the image files used to create the puzzles
        puzzle_type (PuzzleType): Type of the puzzle to be solved
        piece_width (int): Width/length of all puzzle pieces

    Returns (PaikinTalSolver):
        Solved Paikin & Tal result.

    """
    pickle_filename = PickleHelper.build_filename(
        PaikinTalSolver.POST_INITIAL_CONSTRUCTOR_PICKLE_FILE_DESCRIPTOR, image_filenames, puzzle_type
    )

    # Optionally import the images from disk
    if FORCE_RECALCULATE_DISTANCES or not os.path.exists(pickle_filename):
        combined_pieces, puzzles = Puzzle.get_combined_pieces_multiple_images(image_filenames, piece_width)

        # Select whether or not to use fixed puzzle dimensions
        puzzle_dimensions = puzzles[0].grid_size if USE_KNOWN_PUZZLE_DIMENSIONS and len(images) == 1 else None

        # Create the Paikin Tal Solver
        logging.info("Beginning calculating of Paikin & Tal inter-piece distance information")
        start_time = time.time()
        paikin_tal_solver = PaikinTalSolver(
            combined_pieces,
            top_level_calculate_asymmetric_distance,
            len(image_filenames),
            puzzle_type,
            fixed_puzzle_dimensions=puzzle_dimensions,
            image_filenames=image_filenames,
        )
        print_elapsed_time(start_time, "Paikin & Tal inter-piece distance calculations")

    else:
        paikin_tal_solver = PaikinTalSolver.pickle_import_after_initial_construction(image_filenames, puzzle_type)

        if config.IS_SOLVER_COMPARISON_RUNNING:
            paikin_tal_solver.reset_actual_puzzle_count()

    # Run the Solver
    paikin_tal_solver.run_standard()

    # Export the solved results
    return paikin_tal_solver
예제 #8
0
    def __init__(self, image_file_path, piece_width, puzzle_type, distance_function):

        # Store the information about the input image
        self._filename_root = Puzzle.get_filename_without_extension(image_file_path)
        self._file_extension = Puzzle.get_file_extension(image_file_path)
        # File extension should not include the period
        assert "." not in self._file_extension

        logging.info("Performing best buddy analysis for image: %s" % self._filename_root)

        self.puzzle_type = puzzle_type

        # Consider both interior and exterior best buddies.
        self._numb_wrong_exterior_bb = 0
        self._total_numb_interior_bb = 0
        self._numb_wrong_interior_bb = 0

        # Build a puzzle
        self._puzzle = Puzzle(0, image_file_path, piece_width)

        self.numb_pieces = self._puzzle.numb_pieces

        # Get the piece IDs
        self._puzzle.assign_all_piece_id_numbers_to_original_id()
        self._puzzle.assign_all_pieces_to_original_location()
        self._puzzle.assign_all_pieces_to_same_rotation(PuzzlePieceRotation.degree_0)

        # Calculate the inter-piece distance
        self._interpiece_distance = InterPieceDistance(self._puzzle.pieces, distance_function, puzzle_type)

        # Get the link between number of test buddies and accuracy
        self._numb_best_buddies_versus_accuracy = np.zeros((PuzzlePieceSide.get_numb_sides() + 1,
                                                            PuzzlePieceSide.get_numb_sides() + 1,
                                                            PuzzlePieceSide.get_numb_sides() + 1),
                                                           np.uint32)
        # Store the location of each piece
        self._piece_locations, _ = self._puzzle.build_placed_piece_info()
예제 #9
0
    def output_results_image(self):
        """
        Creates an image showing the best buddy accuracy distribution of an image.
        """
        # Create a time stamp for the results
        timestamp = time.time()
        # Build the original filename
        orig_img_filename = self._filename_root + "." + self._file_extension

        descriptor = "image_best_buddies"
        output_filename = Puzzle.make_image_filename(PuzzleSolver.PaikinTal, [orig_img_filename], descriptor,
                                                     Puzzle.OUTPUT_IMAGE_DIRECTORY, self.puzzle_type,
                                                     timestamp, orig_img_filename=orig_img_filename)
        # Stores the results to a file.
        self._puzzle.build_puzzle_image(use_results_coloring=True)
        self._puzzle.save_to_file(output_filename)
예제 #10
0
    def build_filename(pickle_descriptor, image_filenames, puzzle_type):
        """
        Creates the filename of the pickle output file.

        Args:
            pickle_descriptor (string): Descriptor of the pickle contents
            image_filenames (List[string]): Image file names
            puzzle_type (PuzzleType): Type of the puzzle being solved

        Returns (str): Pickle filename
        """
        assert PickleHelper.ENABLE_PICKLE
        pickle_root_filename = ""
        for i in range(0, len(image_filenames)):
            # Get the root of the filename (i.e. without path and file extension
            img_root_filename = Puzzle.get_filename_without_extension(image_filenames[i])
            # Append the file name to the information
            pickle_root_filename += "_" + img_root_filename

        pickle_root_filename += "_type_" + str(puzzle_type.value) + ".pk"
        return PickleHelper._PICKLE_DIRECTORY + pickle_descriptor + pickle_root_filename
예제 #11
0
    def _save_stitching_piece_solved_puzzle_to_file(self, stitching_piece_segment_info):
        """
        Saves the solved image when perform single puzzle solving to a file.

        Args:
            stitching_piece_segment_info (StitchingPieceInfo): Information on the stitching piece information.
        """
        solved_puzzle, _ = self._paikin_tal_solver.get_solved_puzzles()
        # Reconstruct the puzzle
        new_puzzle = Puzzle.reconstruct_from_pieces(solved_puzzle[0], 0)

        max_numb_zero_padding = 4

        # Store the reconstructed image
        segment_id = stitching_piece_segment_info.segment_numb
        filename_descriptor = "segment_" + str(segment_id).zfill(max_numb_zero_padding)

        stitching_piece_id = stitching_piece_segment_info.piece_id
        filename_descriptor += "_stitching_piece_id_" + str(stitching_piece_id).zfill(max_numb_zero_padding)

        # Build the filename and output to a file
        new_puzzle.save_to_file(self._make_image_filename(filename_descriptor))
예제 #12
0
    def _save_single_solved_puzzle_to_file(self, segmentation_round):
        """
        Saves the solved image when perform single puzzle solving to a file.

        Args:
            segmentation_round (int): iteration count for the segmentation
        """
        solved_puzzle, _ = self._paikin_tal_solver.get_solved_puzzles()
        # Reconstruct the puzzle
        new_puzzle = Puzzle.reconstruct_from_pieces(solved_puzzle[0], 0)

        # Store the reconstructed segmented image
        max_numb_zero_padding = 4
        filename_descriptor = "single_puzzle_round_" + str(segmentation_round).zfill(max_numb_zero_padding)
        new_puzzle.save_to_file(self._make_image_filename(filename_descriptor))

        # Store the best buddy image.
        filename_descriptor += "_best_buddy_acc"
        output_filename = self._make_image_filename(filename_descriptor)
        self._paikin_tal_solver.best_buddy_accuracy.output_results_images(PuzzleSolver.MultiPuzzle,
                                                                          self._image_filenames, [new_puzzle],
                                                                          self._paikin_tal_solver.puzzle_type,
                                                                          self._start_timestamp,
                                                                          output_filenames=[output_filename])
예제 #13
0
class ImageBestBuddyStatistics(object):
    """
    Class used to get the best buddy accuracy statistics for any image.
    """

    def __init__(self, image_file_path, piece_width, puzzle_type, distance_function):

        # Store the information about the input image
        self._filename_root = Puzzle.get_filename_without_extension(image_file_path)
        self._file_extension = Puzzle.get_file_extension(image_file_path)
        # File extension should not include the period
        assert "." not in self._file_extension

        logging.info("Performing best buddy analysis for image: %s" % self._filename_root)

        self.puzzle_type = puzzle_type

        # Consider both interior and exterior best buddies.
        self._numb_wrong_exterior_bb = 0
        self._total_numb_interior_bb = 0
        self._numb_wrong_interior_bb = 0

        # Build a puzzle
        self._puzzle = Puzzle(0, image_file_path, piece_width)

        self.numb_pieces = self._puzzle.numb_pieces

        # Get the piece IDs
        self._puzzle.assign_all_piece_id_numbers_to_original_id()
        self._puzzle.assign_all_pieces_to_original_location()
        self._puzzle.assign_all_pieces_to_same_rotation(PuzzlePieceRotation.degree_0)

        # Calculate the inter-piece distance
        self._interpiece_distance = InterPieceDistance(self._puzzle.pieces, distance_function, puzzle_type)

        # Get the link between number of test buddies and accuracy
        self._numb_best_buddies_versus_accuracy = np.zeros((PuzzlePieceSide.get_numb_sides() + 1,
                                                            PuzzlePieceSide.get_numb_sides() + 1,
                                                            PuzzlePieceSide.get_numb_sides() + 1),
                                                           np.uint32)
        # Store the location of each piece
        self._piece_locations, _ = self._puzzle.build_placed_piece_info()

    def calculate_results(self):
        """
        Calculates the best buddy accuracy results.
        """

        # Calculate the best buddy information for each piece.
        for piece in self._puzzle.pieces:
            self.analyze_piece_best_buddy_info(piece)

        # Output the best buddy accuracy image.
        self.output_results_image()

        # Clear up the memory
        self._puzzle = None
        self._interpiece_distance = None

    @property
    def filename_root(self):
        """
        Returns the file name of the original image used without file extension of path information.

        Returns (str):
            Filename of the original image with the file extension and file path removed.
        """
        return self.filename_root

    @property
    def file_extension(self):
        """
        Returns the file extension (e.g. "jpg", "bmp", "png", etc.)

        Returns (str):
            File extension of the original image
        """
        return self._file_extension

    def analyze_piece_best_buddy_info(self, piece):
        """
        Analyze the best buddy information for a single piece.

        Args:
            piece (PuzzlePiece): Puzzle piece whose best buddy info will be analyzed
        """

        # Get the neighbor location and sides
        neighbor_loc_and_sides = piece.get_neighbor_locations_and_sides()
        original_neighbor_id_and_side = piece.original_neighbor_id_numbers_and_sides

        # Initialize the counters for the piece on the total number of best best buddies and how many are wrong
        numb_piece_bb = 0
        numb_wrong_interior_bb = 0
        numb_wrong_exterior_bb = 0

        # Reset the image coloring
        piece.reset_image_coloring_for_polygons()

        # Iterate through all sides
        for i in xrange(PuzzlePieceSide.get_numb_sides()):

            # Get the neighbor location
            (neighbor_loc, piece_side) = neighbor_loc_and_sides[i]
            neighbor_id_and_side = original_neighbor_id_and_side[i]

            # Assert neighbor and piece side are complementary
            if neighbor_id_and_side is not None:
                (neighbor_id, neighbor_side) = neighbor_id_and_side
                assert piece_side == neighbor_side
            else:
                neighbor_id = -sys.maxint

            # Get the best buddy information for the piece.
            bb_info = self._interpiece_distance.best_buddies(piece.id_number, piece_side)
            if not bb_info:
                piece.results_image_polygon_coloring(piece_side, PieceSideBestBuddyAccuracyResult.no_best_buddy)
                continue
            # Increment the best buddy count
            numb_piece_bb += 1

            # Check if there is a neighbor
            if (neighbor_loc[0] < 0 or neighbor_loc[0] >= self._puzzle.grid_size[0]
                    or neighbor_loc[1] < 0 or neighbor_loc[1] >= self._puzzle.grid_size[1]
                    or self._piece_locations[neighbor_loc] == Puzzle.MISSING_PIECE_PUZZLE_INFO_VALUE):
                # If the neighboring cell is empty and it has a best buddy, it is wrong
                self._numb_wrong_exterior_bb += 1
                numb_wrong_exterior_bb += 1
                piece.results_image_polygon_coloring(piece_side, PieceSideBestBuddyAccuracyResult.wrong_best_buddy)

            # Piece has a neighbor
            else:
                # Increment interior best buddy count
                self._total_numb_interior_bb += 1
                # TODO currently only supports single best buddy
                bb_info = bb_info[0]
                if neighbor_id != bb_info[0] or piece_side.complementary_side != bb_info[1]:
                    numb_wrong_interior_bb += 1
                    self._numb_wrong_interior_bb += 1
                    piece.results_image_polygon_coloring(piece_side, PieceSideBestBuddyAccuracyResult.wrong_best_buddy)
                else:
                    piece.results_image_polygon_coloring(piece_side, PieceSideBestBuddyAccuracyResult.correct_best_buddy)
        # Update the master data structure showing the best buddy distribution
        numpy_index = ImageBestBuddyStatistics.best_buddies_versus_accuracy_tuple(numb_piece_bb,
                                                                                  numb_wrong_interior_bb,
                                                                                  numb_wrong_exterior_bb)
        # Increment the statistics
        self._numb_best_buddies_versus_accuracy[numpy_index] += 1

    @staticmethod
    def best_buddies_versus_accuracy_tuple(numb_bb, numb_wrong_interior_bb, numb_wrong_exterior_bb):
        """

        Args:
            numb_bb (int): Total number of best buddies for a piece
            numb_wrong_interior_bb (int): Number of best buddies for a piece that were wrong on an internal
              location (i.e. where it had a neighbor)
            numb_wrong_exterior_bb (int): Number of best buddies for a piece that were wrong when it had no neighbor

        Returns (Tuple[int]):
            Tuple for accessing the numpy array

        """
        assert numb_bb >= numb_wrong_interior_bb + numb_wrong_exterior_bb
        return numb_bb, numb_wrong_interior_bb, numb_wrong_exterior_bb

    def print_results(self):
        """
        Prints the best buddy results to the console.
        """

        string_io = cStringIO.StringIO()
        print >>string_io, "\nBest Buddy Results for Image:\t" + self._filename_root
        print >>string_io, "\tFile Extension:\t" + self._file_extension
        print >>string_io, "\tNumber of Pieces:\t%d" % self.numb_pieces

        # Total number of best buddies
        print >>string_io, "\tTotal Number of Best Buddies:\t%d" % self.total_number_of_best_buddies
        print >>string_io, "\tTotal Best Buddy Accuracy:\t\t%1.2f%%" % (100 * self.total_accuracy)

        print >>string_io, "\tBest Buddy Density:\t\t\t\t%1.2f%%" % (100 * self.density)
        print >>string_io, "\tInterior Best Buddy Accuracy:\t%1.2f%%" % (100 * self.interior_accuracy)
        print >>string_io, ""
        print >>string_io, "\tNumber of Wrong Interior Best Buddies:\t%d" % self._numb_wrong_interior_bb
        print >>string_io, "\tNumber of Wrong Exterior Best Buddies:\t%d" % self._numb_wrong_exterior_bb

        # log the result
        logging.info(string_io.getvalue())
        string_io.close()

    @property
    def density(self):
        """
        Calculates the best buddy density for the original image.  It is defined as the total number of best
        buddies divided by the total number of possible best buddies (i.e. number of pieces multiplied by the number
        of sides per piece).

        Returns (float):
            Best buddy density across the entire image.

        """
        return 1.0 * self.total_number_of_best_buddies / (self.numb_pieces * PuzzlePieceSide.get_numb_sides())

    @property
    def total_number_of_best_buddies(self):
        """
        Gets the total of best buddies (both interior/exterior and right/wrong).

        Returns (int):
            Number of best buddies in the image
        """
        return self._numb_wrong_exterior_bb + self._total_numb_interior_bb

    @property
    def total_accuracy(self):
        """
        Gets the best buddy accuracy across the entire image  It is defined as:

        :math:`accuracy = 1 - (numb_wrong_interior_bb + numb_wrong_exterior_bb)/(total_numb_best_buddy)`

        Returns (float):
            Best buddy accuracy for the entire image
        """
        return 1 - 1.0 * (self._numb_wrong_interior_bb + self._numb_wrong_exterior_bb) / self.total_number_of_best_buddies

    @property
    def interior_accuracy(self):
        """
        Gets the best buddy accuracy considering only interior best buddies.  It is defined as:

        :math:`interior_accuracy = 1 - (numb_wrong_interior_bb)/(total_numb_interior_best_buddy)`

        Returns (float):
            Best buddy accuracy considering only interior pieces.
        """
        return 1 - 1.0 * self._numb_wrong_interior_bb / self._total_numb_interior_bb

    def output_results_image(self):
        """
        Creates an image showing the best buddy accuracy distribution of an image.
        """
        # Create a time stamp for the results
        timestamp = time.time()
        # Build the original filename
        orig_img_filename = self._filename_root + "." + self._file_extension

        descriptor = "image_best_buddies"
        output_filename = Puzzle.make_image_filename(PuzzleSolver.PaikinTal, [orig_img_filename], descriptor,
                                                     Puzzle.OUTPUT_IMAGE_DIRECTORY, self.puzzle_type,
                                                     timestamp, orig_img_filename=orig_img_filename)
        # Stores the results to a file.
        self._puzzle.build_puzzle_image(use_results_coloring=True)
        self._puzzle.save_to_file(output_filename)