sayo9394
sayo9394

Reputation: 485

Unable to crop away transparency, Neither PIL getbbox() nor Numpy are working

I've very much a noob when it comes to image processing :( I have a bunch of PNG files (300 of them) that have large areas of transparency that I wish to crop. I want to automate the process obviously, hence why i tried using python and PIL.

Now I have a look at the following link, Crop a PNG image to its minimum size, and also using Numpy as suggested by this link, Automatically cropping an image with python/PIL, both to no success :( The output files are identical to the input files! no cropping of the transparency, same size. The getbbox is returning same width and height.

Here's a link to one of those images; 98x50button The image is of a button icon in the shape of a bell. it's drawn in white so it's hard to see which transparent background. The expected outcome would a 20x17 Button (with the transparency inside that 20x17 box remaining in tact)

Here's the code i'm using;

#!/usr/bin/env python

import sys
import os
import Image
import numpy as np


def autocrop_image2(image):
    image.load()

    image_data = np.asarray(image)
    image_data_bw = image_data.max(axis=2)
    non_empty_columns = np.where(image_data_bw.max(axis=0) > 0)[0]
    non_empty_rows = np.where(image_data_bw.max(axis=1) > 0)[0]
    cropBox = (min(non_empty_rows), max(non_empty_rows),
               min(non_empty_columns), max(non_empty_columns))

    image_data_new = image_data[cropBox[0]:cropBox[
        1] + 1, cropBox[2]:cropBox[3] + 1, :]

    new_image = Image.fromarray(image_data_new)
    return new_image


def autocrop_image(image, border=0):
    # Get the bounding box
    bbox = image.getbbox()

    # Crop the image to the contents of the bounding box
    image = image.crop(bbox)

    # Determine the width and height of the cropped image
    (width, height) = image.size

    # Add border
    width += border * 2
    height += border * 2

    # Create a new image object for the output image
    cropped_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))

    # Paste the cropped image onto the new image
    cropped_image.paste(image, (border, border))

    # Done!
    return cropped_image

walk_dir = sys.argv[1]

print('walk_dir = ' + walk_dir)

# If your current working directory may change during script execution, it's recommended to
# immediately convert program arguments to an absolute path. Then the variable root below will
# be an absolute path as well. Example:
# walk_dir = os.path.abspath(walk_dir)
print('walk_dir (absolute) = ' + os.path.abspath(walk_dir))

for root, subdirs, files in os.walk(walk_dir):
    print('--\nroot = ' + root)
    list_file_path = os.path.join(root, 'my-directory-list.txt')
    print('list_file_path = ' + list_file_path)

    with open(list_file_path, 'wb') as list_file:
        for subdir in subdirs:
            print('\t- subdirectory ' + subdir)

        for filename in files:
            file_path = os.path.join(root, filename)

            print('\t- file %s (full path: %s)' % (filename, file_path))
            filename, file_extension = os.path.splitext(filename)
            if file_extension.lower().endswith('.png'):
                # Open the input image
                image = Image.open(file_path)
                # Do the cropping
                # image = autocrop_image(image, 0)
                new_image = autocrop_image2(image)
                # Save the output image
                output = os.path.join("output", filename + ".png")
                print output
                new_image.save(output)

Thank you all for the help :)

Upvotes: 2

Views: 3523

Answers (3)

Charles Merriam
Charles Merriam

Reputation: 20520

Here's a new solution; I just ran into this problem:

You have an RGBA image:

  • When the pixel's A is 0, the cell should be fully transparent,
  • but some of your pixels have A as 0, and RGB values not zero.
  • Pillow's getbbox() and other functions now fail.
  • You want to force your RGB to 0 whenever alpha is 0

So:

  • Make a pure black RGBA image, each pixel being (0, 0, 0, 0)
  • Make a composite with your image and an RGBA black image, using your image as a mask.
  • Wherever your A was 0, your RGB will now be zero
  • This is a solution; there is probably a lower-memory solution.

Here is the code:

black = Image.new('RGBA', myImage.size)
myImage = Image.composite(myImage, black, myImage)
myCroppedImage = myImage.crop(myImage.getbbox())

Upvotes: 1

Here is one solution to crop the transparent borders.
Just throw this script in your folder with your batch .png files:

from PIL import Image
import numpy as np
from os import listdir

def crop(image_name):
    pil_image = Image.open(image_name)
    np_array = np.array(pil_image)
    blank_px = [255, 255, 255, 0]
    mask = np_array != blank_px
    coords = np.argwhere(mask)
    x0, y0, z0 = coords.min(axis=0)
    x1, y1, z1 = coords.max(axis=0) + 1
    cropped_box = np_array[x0:x1, y0:y1, z0:z1]
    pil_image = Image.fromarray(cropped_box, 'RGBA')
    print(pil_image.width, pil_image.height)
    pil_image.save(png_image_name)
    print(png_image_name)

for f in listdir('.'):
    if f.endswith('.png'):
        crop(f)

Upvotes: 0

Blckknght
Blckknght

Reputation: 104792

The issue you're having is that your images contain transparent white pixels, and your code is only going to crop pixels that are both transparent and black. The RGBA values for most of the pixels in your example image are (255, 255, 255, 0).

In autocrop_image2, you're taking the max of the channel values. You probably just want the alpha channel's value directly, so change:

image_data_bw = image_data.max(axis=2)

To:

image_data_bw = image_data[:,:,3]

The rest of the function should then work as intended.

The autocrop_image function has the same problem. The getbbox method returns the bounds of the non-zero pixels, and transparent white pixels are not zero. To fix it, try converting the image from "RGBA" mode to premultiplied alpha "RGBa" mode before finding the bounding box:

bbox = image.convert("RGBa").getbbox()

Upvotes: 7

Related Questions