1

I want to rotate a black and white image. I am trying to use the rotate function as follows:

image.rotate(angle, fillcolor=255)

I am required to older versions of Python and Pillow, and they do not support the 'fillcolor' argument. I cannot upgrade to the newer versions due to certain restrictions and cannot use any external libraries.

Is there another way to fill the area outside the rotated image with white color using Pillow?

Rotated image has black color in the area outside the rotated part. I want to fill it with white color.

Original : Original image

Rotated :Rotated image

Python_Student
  • 137
  • 1
  • 15
  • What color result do you get now for the outside area after rotation? Please post your input and output image, so we can see what is happening. If it is just extending the colors of the outside perimeter of your current image so you see stripes, then put, say, a red border around your image. Then rotate, then change all red to white. – fmw42 Jun 26 '19 at 05:35
  • @fmw42 : I have updated the question and added images. – Python_Student Jun 26 '19 at 05:58

3 Answers3

3

You can try Interpolating the Original Image, with the cropped one via Image.composite() to get rid of the black bars/borders.

from PIL import Image

img = Image.open(r"Image_Path").convert("RGBA")

angle = 30

img = img.rotate(angle)

new_img = Image.new('RGBA', img.size, 'white')

Alpha_Image = Image.composite(img, new_img, img)

Alpha_Image = Alpha_Image.convert(img.mode)

Alpha_Image.show()

The above code takes in an Image, converts it into mode RGBA (Alpha is required for this process), and then rotates the Image by 30 degrees. After that It creates a empty Image object of mode RGBA of the same dimensions as the original image, with each pixel having a default value of 255 each channel (i.e Pure white for RGB, and Full Opacity in the context of Alpha/Transparency). Then Interpolates the original image with this empty one using the mask of original Image (we are using the transparency mask of the first image). This results in the Desired images, where black bars/edges are replaced by white. In the end we convert the image color space to the original one.

ORIGINAL IMAGE:-

enter image description here

IMAGE AFTER ROTATING 30 DEGREES:-

enter image description here

Vasu Deo.S
  • 1,820
  • 1
  • 11
  • 23
0

Edit: As no external libraries are allowed, I would suggest cropping the rectangle you want and pasting it onto the original image, this could be done with magic numbers (of the rectangle's coordinates), this works for me (you might will need to tweek a little)

im = Image.open("mFul4.png")
rotated = im.rotate(105)
box = (55, 65,200,210)
d = rotated.crop(box=box)
im.paste(d, box=box)
im.save("ex.bmp" )

and the output

enter image description here

Edit2: This is the ugliest way, but it works, you might need to tweak the magic numbers a bit to have it more precise, I was working on your given image, so couldn't tell when i'm overdoing it. It produces the same output

from PIL import Image
im = Image.open("mFul4.png")
angle=105
cos = 0.240959049 # -cos(angle)
d = im.rotate(angle)
pix = d.load()

tri_x = 120
for i in range(4): # 4 triangles
    for j in range(tri_x, -1, -1):
        for k in range(int((tri_x-j)*cos)+1, -1, -1):
            x,y =( j, k )if i <1 else (d.size[0]-j-1, d.size[1]-k-1)
            if i in [2,3]:
                y, x = (d.size[0] - j-2 , k) if i <3 else (j, d.size[1] - k)
            pix[x,y] = (255, 255, 255, 255)
d.show()
Rotem Tal
  • 739
  • 5
  • 11
  • OP stated the use of any external library is prohibited – Vasu Deo.S Jun 26 '19 at 07:05
  • sorry, morning daze, I hope this solution would work for you – Rotem Tal Jun 26 '19 at 07:34
  • This method won't be helpful, if the image gets any complex then some vector shapes. As in your method, you cropped the original image after rotation, which in turn will result in loss of information (not in this image as the background is total white and for a large region) if the image is complex (complex img = a photograph, pixel art etc). Because in PIL, images are cropped in a square/rectangular based grids, therefore when a rotated image (of any angle) is cropped to a shorter dimension, in general this would result in loss of information from the original image – Vasu Deo.S Jun 26 '19 at 07:47
  • This final fix should work, you might need to tweak the magic numbers a bit – Rotem Tal Jun 26 '19 at 09:13
0

An awkward option that has always worked for me, seeing as with my tools I always get a light gray "border" around the rotated image that interferes with filling:

  • add a border on the non-rotated image and use the fill color with that border. The bordering operation is lossless and filling will be exact (and easy).

  • rotate the bordered image. The seam will now also be correct (but not exact unless you rotate by 45° or 90°).

  • calculate the size of the rotated border using trigonometry. The result will not be exact (i.e. "131.12 pixel"). Usually you can do this in reverse, starting with an exact border on the rotated image and calculating the border you need to add, and adjust the border width so that the nonrotated border is exact. Example: with a rotated border of 170 pixels you get a nonrotated border of 140.3394 pixels. So you use a 510 pixel rotated border, resulting in the need to add a 421.018 pixel nonrotated border. This is close enough to 421 pixels that it is acceptable.

  • remove the rotated border.

This also helps avoiding some artefacts near the cut parts of the image that fall off the rotated image.

It has the drawback that you end up with a more massive rotation, with higher memory expenditure and computation time, especially if you use larger borders to increase precision.

LSerni
  • 55,617
  • 10
  • 65
  • 107