5 Best Ways to Divide Images into Equal Parts Using OpenCV in Python

πŸ’‘ Problem Formulation:

When working with image processing, a common challenge is dividing an image into smaller, equal-sized parts. This can be for tasks such as image analysis, batch processing, or creating a puzzle effect. Given an image, we might want to slice it vertically and horizontally into N equal pieces, without losing any part of the original image. OpenCV, a powerful computer vision library in Python, allows us to achieve this efficiently.

Method 1: Using Horizontal and Vertical Slicing

This method employs the slicing capabilities of OpenCV and Numpy to partition the image into equal horizontal and vertical segments. Specifically, it calculates the width and height of each segment by dividing the total dimensions of the image by the desired number of pieces. cv2.imread is used to read the image, and array slicing is applied to divide it.

Here’s an example:

import cv2
import numpy as np

def split_image(image_path, h_splits, v_splits):
    image = cv2.imread(image_path)
    height, width = image.shape[:2]
    h_step = height // h_splits
    v_step = width // v_splits
    
    parts = [(image[i:i+h_step, j:j+v_step]) for i in range(0, height, h_step) for j in range(0, width, v_step)]
    return parts

parts = split_image('image.jpg', 2, 3)
for idx, part in enumerate(parts):
    cv2.imwrite(f'part_{idx}.jpg', part)

The output of this code will be six images, each representing a part of the original image, saved as ‘part_0.jpg’ to ‘part_5.jpg’.

In the code above, split_image reads an image and divides it into the specified number of horizontal (h_splits) and vertical (v_splits) splits. List comprehension creates the split images, and each part is saved to a file.

Method 2: Dividing into Equal Squares

This method divides an image into squares of equal size. It calculates the size of the squares based on the smallest dimension of the image. Consequently, some parts of the image may be discarded if the image is not a perfect square itself.

Here’s an example:

import cv2
import numpy as np

def split_image_into_squares(image_path, num_of_squares):
    image = cv2.imread(image_path)
    min_dim = min(image.shape[:2])
    step = min_dim // num_of_squares
    
    squares = [image[i:i+step, j:j+step] for i in range(0, min_dim, step) for j in range(0, min_dim, step)]
    return squares

squares = split_image_into_squares('image.jpg', 4)
for idx, square in enumerate(squares):
    cv2.imwrite(f'square_{idx}.jpg', square)

The output is 16 square images if the input image is square, and proportionally less if it is not perfectly square.

The function split_image_into_squares determines the number of squares that can fit into the smallest dimension of the image and generates a list of square images. These squares are extracted using array slicing and then saved as individual files.

Method 3: Using Grid Lines

This method involves creating a grid overlay on the image and then cropping the image by the lines in the grid. This is done by calculating the intervals for the grid lines and then using these intervals to slice the image accordingly.

Here’s an example:

import cv2
import numpy as np

def split_image_with_grid(image_path, grid_size):
    image = cv2.imread(image_path)
    height, width = image.shape[:2]
    h_lines = np.linspace(0, height, grid_size[0]+1, dtype=np.int)
    v_lines = np.linspace(0, width, grid_size[1]+1, dtype=np.int)
    
    grid_parts = [image[h:h_lines[i+1], w:v_lines[j+1]] for i, h in enumerate(h_lines[:-1]) for j, w in enumerate(v_lines[:-1])]
    return grid_parts

grid_parts = split_image_with_grid('image.jpg', (3, 3))
for idx, part in enumerate(grid_parts):
    cv2.imwrite(f'grid_part_{idx}.jpg', part)

The resulting output will be nine parts if a 3×3 grid was specified, with each part saved as a separate image.

The provided code creates a function that reads an image and superimposes a grid defined by grid_size. For each cell in the grid, it slices and saves part of the image as a new file.

Method 4: Using Contour Detection for Irregular Division

This method is suited for scenarios where an image must be divided based on specific features or contours within it, rather than into a uniform grid. OpenCV’s contour detection is used to find these features and then to slice the image according to them. This method is less about equal division and more about feature-based partitioning.

Here’s an example:

import cv2
import numpy as np

def split_image_by_contours(image_path):
    image = cv2.imread(image_path)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, threshold = cv2.threshold(gray_image, 150, 255, cv2.THRESH_BINARY_INV)
    contours, _ = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    for idx, contour in enumerate(contours):
        x, y, w, h = cv2.boundingRect(contour)
        part = image[y:y+h, x:x+w]
        cv2.imwrite(f'contour_part_{idx}.jpg', part)
        
return None

split_image_by_contours('image.jpg')

The output will be images that correspond to the detected contours within the original image.

This code snippet converts an image to grayscale, applies thresholding, and then uses contour detection. Each detected contour is used to crop parts of the image, which are then saved accordingly.

Bonus One-Liner Method 5: Equal Horizontal Splits

If all you need is evenly spaced horizontal slices of the image, a one-liner using numpy slicing can do the trick. The following will slice an image into N horizontal parts in a concise and efficient manner.

Here’s an example:

import cv2

image = cv2.imread('image.jpg')
slices = numpy.array_split(image, 4, axis=0)
for idx, slice in enumerate(slices): 
    cv2.imwrite(f'horizontal_part_{idx}.jpg', slice)

Output will consist of four horizontal slices of the original image saved as separate files.

This succinct approach uses numpy’s array_split function to split the loaded image array on axis 0, which corresponds to height.

Summary/Discussion

  • Method 1: Horizontal and Vertical Slicing. Strengths: Flexible, can create any grid of images. Weaknesses: May leave unused parts of the image if it’s not perfectly divisible by the number of parts.
  • Method 2: Dividing into Equal Squares. Strengths: Simple and ideal for creating a checkerboard effect. Weaknesses: Cannot be used if the image cannot be divided into squares without cropping or stretching.
  • Method 3: Using Grid Lines. Strengths: Produces a consistent grid pattern, easy to adjust grid size. Weaknesses: Like Method 1, may leave out parts of the image.
  • Method 4: Using Contour Detection for Irregular Division. Strengths: Can split images based on content rather than arbitrary grid. Weaknesses: It’s not for equal parts division and requires clear contrast between features and background.
  • Bonus Method 5: Equal Horizontal Splits One-Liner. Strengths: Extremely easy and quick to divide an image horizontally. Weaknesses: Limited to only horizontal slicing and may not work well with all image dimensions.