0

How can I crop images that looks like this and save as 3 different images?

The issue is that images are different in size and non-proportional, so I want to make a code that dynamically cuts black borders but not the black part which is inside the picture.

Here is the desired outcome:

Desired outcome

Below is the sample code I've made which works only for one specific image.

from PIL import Image
im = Image.open(r"image.jpg")

# Setting the points for cropped image1
# im1 = im.crop((left, top, right, bottom))
im1 = im.crop((...))
im2 = im.crop((...))
im3 = im.crop((...))

im1 = im1.save(r"image1.jpg")
im2 = im2.save(r"image2.jpg")
im3 = im3.save(r"image3.jpg")
Orkhan Mammadov
  • 173
  • 1
  • 12
  • 1
    basic techniques. calculate mask for background color, invert, find connected components (or contours). your code doesn't help because there isn't even a hint of an approach. – Christoph Rackwitz Dec 22 '21 at 17:02
  • @ChristophRackwitz thanks for the response and ideas. I'll try to come up with the solution and share it here – Orkhan Mammadov Dec 23 '21 at 08:35

2 Answers2

0

Finally I've found the solution. Here is what I did:

from PIL import Image, ImageChops

def RemoveBlackBorders(img):
    bg = Image.new(img.mode, img.size, img.getpixel((0,0)))
    diff = ImageChops.difference(img, bg)
    diff = ImageChops.add(diff, diff, 2.0, -100)
    bbox = diff.getbbox()
    if bbox:
        return img.crop(bbox)

# Opens a image in RGB mode
im = Image.open(r"C:\Path\Image.jpg")

# removing borders

im = RemoveBlackBorders(im)

# getting midpoint from size

width, height = im.size
mwidth = width/2

# assign shape of figure from the midpoint

     #crop((x,y of top left, x, y of bottom right))
im1 = im.crop((0, 0, mwidth-135, height))
im2 = im.crop((mwidth-78, 0, mwidth+84, height))
im3 = im.crop((mwidth+135, 0, width, height))

The function to remove borders I've found from here.

Although the solution is not completely dynamic, it still solves my problem with ~90% accuracy. But I believe there should be a more universal approach for this problem.

Orkhan Mammadov
  • 173
  • 1
  • 12
0

If the areas have always the same size and the same top and bottom coordinates the following should work:

The coordinates for the crops can be retrieved by calculating the sums per rows and per columns, then analyzing them.

import cv2
import numpy as np
im = cv2.imread(image_path)
sum_of_rows = np.sum(im, axis=(1,2))
sum_of_cols = np.sum(im, axis=(0,2))

The top and bottom can be calculated by calculating the sum for each row (each sum value being calculated R+G+B, the value should be zero for black). Then looking for the first value being different form zero and the last value being different than zero. Both indicating the top and bottom.

top = np.argmax(sum_of_rows > 0)
bottom = top + np.argmax(sum_of_rows[top:]==0)

The same can be done for the sum for each column, but here checking for multiple left and right values.

ai2ys
  • 1,151
  • 10
  • 21