Home > Net >  How to split an image into multiple images based on white borders between them
How to split an image into multiple images based on white borders between them

Time:12-28

I need to split an image into multiple images, based on the white borders between them.

for example:

enter image description here

output:

enter image description here

enter image description here

enter image description here

enter image description here

using Python, I don't know how to start this mission.

CodePudding user response:

Here is a solution for the "easy" case where we know the grid configuration. I provide this solution even though I doubt this is what you were asked to do.

In your example image of the cat, if we are given the grid configuration, 2x2, we can do:

from PIL import Image

def subdivide(file, nx, ny):
    im = Image.open(file)
    wid, hgt = im.size # Size of input image
    w = int(wid/nx) # Width of each subimage
    h = int(hgt/ny) # Height of each subimage
    for i in range(nx):
        x1 = i*w  # Horicontal extent...
        x2 = x1 w # of subimate
        for j in range(ny):
            y1 = j*h # Certical extent...
            y2 = y1 h # of subimate
            subim = im.crop((x1, y1, x2, y2))
            subim.save(f'{i}x{j}.png')

subdivide("cat.png", 2, 2)

The above will create these images:

Upper left

enter image description here

enter image description here

enter image description here

CodePudding user response:

My previous answer depended on knowing the grid configuration of the input image. This solution does not.

The main challenge is to detect where the borders are and, thus, where the rectangles that contain the images are located.

To detect the borders, we'll look for (vertical and horizontal) image lines where all pixels are "white". Since the borders in the image are not really pure white, we'll use a value less than 255 as the whiteness threshold (WHITE_THRESH in the code.)

The gist of the algorithm is in the following lines of code:

whitespace = [np.all(gray[:,i] > WHITE_THRESH) for i in range(gray.shape[1])]

Here "whitespace" is a list of Booleans that looks like

TTTTTFFFFF...FFFFFFFFTTTTTTTFFFFFFFF...FFFFTTTTT 

where "T" indicates the corresponding horizontal location is part of the border (white).

We are interested in the x-locations where there are transitions between T and F. The call to the function slices(whitespace) returns a list of tuples of indices

[(x1, x2), (x1, x2), ...]

where each (x1, x2) pair indicates the xmin and xmax location of images in the x-axis direction.

The slices function finds the "edges" where there are transitions between True and False using the exclusive-or operator and then returns the locations of the transitions as a list of tuples (pairs of indices).

Similar code is used to detect the vertical location of borders and images.

The complete runnable code below takes as input the OP's image "cat.png" and:

  1. Extracts the sub-images into 4 PNG files "fragment-0-0.png", "fragment-0-1.png", "fragment-1-0.png" and "fragment-1-1.png".
  2. Creates a (borderless) version of the original image by pasting together the above fragments.

The runnable code and resulting images follow. The program runs in about 0.25 seconds.

from PIL import Image
import numpy as np

def slices(lst):
    """ Finds the indices where lst changes value and returns them in pairs
        lst is a list of booleans
    """
    edges = [lst[i-1] ^ lst[i] for i in range(len(lst))]
    indices = [i for i,v in enumerate(edges) if v]
    pairs = [(indices[i], indices[i 1]) for i in range(0, len(indices), 2)]
    return pairs

def extract(xx_locs, yy_locs, image, prefix="image"):
    """ Locate and save the subimages """
    data = np.asarray(image)
    for i in range(len(xx_locs)):
        x1,x2  = xx_locs[i]
        for j in range(len(yy_locs)):
            y1,y2  = yy_locs[j]
            arr = data[y1:y2, x1:x2, :]
            Image.fromarray(arr).save(f'{prefix}-{i}-{j}.png')
                                        
def assemble(xx_locs, yy_locs, prefix="image", result='composite'):
    """ Paste the subimages into a single image and save """
    wid = sum([p[1]-p[0] for p in xx_locs])
    hgt = sum([p[1]-p[0] for p in yy_locs])
    dst = Image.new('RGB', (wid, hgt))
    x = y = 0
    for i in range(len(xx_locs)):
        for j in range(len(yy_locs)):
            img = Image.open(f'{prefix}-{i}-{j}.png')
            dst.paste(img, (x,y))
            y  = img.height
        x  = img.width
        y = 0
    dst.save(f'{result}.png')

WHITE_THRESH = 110  # The original image borders are not actually white
image_file = 'cat.png'

image = Image.open(image_file)

# To detect the (almost) white borders, we make a grayscale version of the image
gray = np.asarray(image.convert('L'))

# Detect location of images along the x axis
whitespace = [np.all(gray[:,i] > WHITE_THRESH) for i in range(gray.shape[1])]
xx_locs = slices(whitespace)

# Detect location of images along the y axis
whitespace = [np.all(gray[i,:] > WHITE_THRESH) for i in range(gray.shape[0])]
yy_locs = slices(whitespace)

extract(xx_locs, yy_locs, image, prefix='fragment')

assemble(xx_locs, yy_locs, prefix='fragment', result='composite')

Individual fragments:

enter image description here

enter image description here

enter image description here

enter image description here

The composite image:

enter image description here

  • Related