Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Constructing Photo Mosaics Using Python Image Processing

Tech 1

Loading Source Tiles

Begin by ingesting the candidate images that will serve as mosaic tiles. The following implementation scans a directory and loads valid image files into memory:

def load_tile_library(directory):
    """
    Scan directory for image files and load them into memory.
    """
    tile_collection = []
    path_obj = Path(directory)
    
    for file_entry in path_obj.iterdir():
        if file_entry.is_file():
            try:
                with open(file_entry, "rb") as file_handle:
                    tile_img = Image.open(file_handle)
                    tile_img.load()  # Force load before closing file
                    tile_collection.append(tile_img)
            except (IOError, OSError):
                continue
    
    return tile_collection

Color Signature Extraction

Each tile and target cell requires a color fingerprint for matching. This implemantation calculates mean RGB values using NumPy operations:

def extract_mean_color(pil_image):
    """
    Compute dominant color via mean RGB values.
    """
    pixel_array = np.asarray(pil_image)
    height, width, channels = pixel_array.shape
    flattened = pixel_array.reshape(height * width, channels)
    mean_values = np.mean(flattened, axis=0)
    return tuple(mean_values.astype(int))

Grid Decomposition

Segment the target image into an M×N matrix of cells. Each cell will later be replaced by a matching tile:

def segment_into_cells(source_img, grid_dimensions):
    """
    Divide source image into MxN segments.
    """
    img_width, img_height = source_img.size
    rows, cols = grid_dimensions
    cell_width = img_width // cols
    cell_height = img_height // rows
    
    cell_list = []
    for row_idx in range(rows):
        for col_idx in range(cols):
            left = col_idx * cell_width
            upper = row_idx * cell_height
            right = left + cell_width
            lower = upper + cell_height
            cell = source_img.crop((left, upper, right, lower))
            cell_list.append(cell)
    
    return cell_list

Optimal Tile Matching

Locate the tile with the closest color signature to each tarrget cell using Euclidean distance in RGB space:

def locate_optimal_tile(target_color, color_catalog):
    """
    Find tile with minimum Euclidean distance in RGB space.
    """
    best_idx = 0
    min_distance = float('inf')
    
    for idx, candidate_color in enumerate(color_catalog):
        delta_r = candidate_color[0] - target_color[0]
        delta_g = candidate_color[1] - target_color[1]
        delta_b = candidate_color[2] - target_color[2]
        distance = delta_r**2 + delta_g**2 + delta_b**2
        
        if distance < min_distance:
            min_distance = distance
            best_idx = idx
            
    return best_idx

Canvas Assembly

Arrange selected tiles into the final composite image grid:

def assemble_composite(tiles, layout):
    """
    Arrange tiles into final mosaic canvas.
    """
    rows, cols = layout
    assert len(tiles) == rows * cols
    
    tile_width = max(t.size[0] for t in tiles)
    tile_height = max(t.size[1] for t in tiles)
    
    canvas_width = cols * tile_width
    canvas_height = rows * tile_height
    canvas = Image.new('RGB', (canvas_width, canvas_height))
    
    for pos, tile in enumerate(tiles):
        row = pos // cols
        col = pos % cols
        x_offset = col * tile_width
        y_offset = row * tile_height
        canvas.paste(tile, (x_offset, y_offset))
    
    return canvas

Mosaic Generation Pipeline

Orchestrate the complete workflow from input to output:

def generate_mosaic(target_path, tile_sources, subdivisions, allow_reuse=True):
    """
    Main pipeline for mosaic generation.
    """
    print("Decomposing target image...")
    target_cells = segment_into_cells(target_path, subdivisions)
    
    print("Analyzing color profiles...")
    tile_signatures = [extract_mean_color(tile) for tile in tile_sources]
    
    selected_tiles = []
    total_cells = len(target_cells)
    milestone = max(1, total_cells // 10)
    
    for iteration, cell in enumerate(target_cells):
        cell_signature = extract_mean_color(cell)
        match_index = locate_optimal_tile(cell_signature, tile_signatures)
        selected_tiles.append(tile_sources[match_index])
        
        if not allow_reuse:
            tile_sources.pop(match_index)
            tile_signatures.pop(match_index)
        
        if iteration % milestone == 0 and iteration > 0:
            print(f"Progress: {iteration}/{total_cells}")
    
    print("Compositing final image...")
    return assemble_composite(selected_tiles, subdivisions)

Command Line Interface

Configure runtime parameters through command-line arguments:

argument_parser = argparse.ArgumentParser(description="Generate photo mosaics from image collections")
argument_parser.add_argument("--source", dest="target_image", required=True, help="Path to base image")
argument_parser.add_argument("--tiles", dest="tile_directory", required=True, help="Directory containing tile images")
argument_parser.add_argument("--grid", nargs=2, type=int, dest="grid_dims", required=True, help="Grid dimensions (rows cols)")
argument_parser.add_argument("--output", dest="output_path", default="mosaic.png", help="Output filename")

Dimension Normalization

Prevent output bloat by scaling tiles to match grid cell dimensions before processing:

print("Normalizing tile dimensions...")
grid_rows, grid_cols = grid_dimensions
target_width = target_img.size[0] // grid_cols
target_height = target_img.size[1] // grid_rows
max_tile_size = (target_width, target_height)

for tile in tile_collection:
    tile.thumbnail(max_tile_size)

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.