Basj
Basj

Reputation: 46313

Background color when cropping image with PIL

The good thing with PIL.crop is that if we want to crop outside of the image dimensions, it simply works with:

from PIL import Image
img = Image.open("test.jpg")
img.crop((-10, -20, 1000, 500)).save("output.jpg")

Question: how to change the background color of the added region to white (default: black)?

enter image description here

Note:

Upvotes: 11

Views: 13204

Answers (2)

sardok
sardok

Reputation: 1116

I think it is not possible with one function call due to the relevant C function which seems to zero-out the destination image memory region (see it here: https://github.com/python-pillow/Pillow/blob/master/src/libImaging/Crop.c#L47)

You mentioned as not interested to create new Image and copy over it but i am pasting that kind of solution anyway for reference:

from PIL import Image
img = Image.open("test.jpg")
x1, y1, x2, y2 = -10, -20, 1000, 500  # cropping coordinates
bg = Image.new('RGB', (x2 - x1, y2 - y1), (255, 255, 255))
bg.paste(img, (-x1, -y1))
bg.save("output.jpg")

Output:

enter image description here

Upvotes: 13

Jeru Luke
Jeru Luke

Reputation: 21233

You can do what you intend to after using the expand() function available in ImageOps module of PIL.

from PIL import Image
from PIL import ImageOps
filename = 'C:/Users/Desktop/Maine_Coon_263.jpg'
img = Image.open(filename)

val = 10    #--- pixels to be cropped

#--- a new image with a border of 10 pixels on all sides
#--- also notice fill takes in the color of white as (255, 255, 255)
new_img = ImageOps.expand(img, border = val, fill = (255, 255, 255))

#--- cropping the image above will not result in any black portion
cropped = new_img.crop((val, val, 150, 150))

The crop() function only takes one parameter of how much portion has to be cropped. There is no functionality to handle the situation when a negative value is passed in. Hence upon passing a negative value the image gets padded in black pixels.

Using the expand() function you can set the color of your choice and then go ahead and crop as you wish.

EDIT

In response to your edit, I have something rather naïve in mind but it works.

  • Get the absolute values of all the values to be cropped. You can use numpy.abs().
  • Next the maximum among these values using numpy.max().
  • Finally expand the image using this value and crop accordingly.

This code will help you:

#--- Consider these values in a tuple that are to crop your image 
crop_vals = (-10, -20, 1000, 500)

#--- get maximum value after obtaining the absolute of each
max_val = np.max(np.abs(crop_vals))

#--- add border to the image using this maximum value and crop
new_img = ImageOps.expand(img, border = max_val, fill = (255, 255, 255))
cropped = new_img.crop((max_val - 10, max_val - 20, new_img.size[0], new_img.size[1]))

Upvotes: 5

Related Questions