开发者

Crop a PNG image to its minimum size

开发者 https://www.devze.com 2022-12-14 13:24 出处:网络
How to cut off the blank border area of a PNG image and shrink it to its m开发者_Go百科inimum size using Python?

How to cut off the blank border area of a PNG image and shrink it to its m开发者_Go百科inimum size using Python?

NB: The border size is not a fixed value, but may vary per image.


PIL's getbbox is working for me

im.getbbox() => 4-tuple or None

Calculates the bounding box of the non-zero regions in the image. The bounding box is returned as a 4-tuple defining the left, upper, right, and lower pixel coordinate. If the image is completely empty, this method returns None.

Code Sample that I tried, I have tested with bmp, but it should work for png too.

import Image
im = Image.open("test.bmp")
im.size  # (364, 471)
im.getbbox()  # (64, 89, 278, 267)
im2 = im.crop(im.getbbox())
im2.size  # (214, 178)
im2.save("test2.bmp")


I had the same problem today. Here is my 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(png_image_name):
    pil_image = Image.open(png_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)


Here is ready-to-use solution:

import numpy as np
from PIL import Image

def bbox(im):
    a = np.array(im)[:,:,:3]  # keep RGB only
    m = np.any(a != [255, 255, 255], axis=2)
    coords = np.argwhere(m)
    y0, x0, y1, x1 = *np.min(coords, axis=0), *np.max(coords, axis=0)
    return (x0, y0, x1+1, y1+1)

im = Image.open('test.png')
print(bbox(im))  # (33, 12, 223, 80)
im2 = im.crop(bbox(im))
im2.save('test_cropped.png')

Example input (download link if you want to try):

Crop a PNG image to its minimum size

Output:

Crop a PNG image to its minimum size


https://gist.github.com/3141140

import Image
import sys
import glob

# Trim all png images with alpha in a folder
# Usage "python PNGAlphaTrim.py ../someFolder"

try:
    folderName = sys.argv[1]
except :
    print "Usage: python PNGPNGAlphaTrim.py ../someFolder"
    sys.exit(1)

filePaths = glob.glob(folderName + "/*.png") #search for all png images in the folder

for filePath in filePaths:
    image=Image.open(filePath)
    image.load()

    imageSize = image.size
    imageBox = image.getbbox()

    imageComponents = image.split()

    if len(imageComponents) < 4: continue #don't process images without alpha

    rgbImage = Image.new("RGB", imageSize, (0,0,0))
    rgbImage.paste(image, mask=imageComponents[3])
    croppedBox = rgbImage.getbbox()

    if imageBox != croppedBox:
        cropped=image.crop(croppedBox)
        print filePath, "Size:", imageSize, "New Size:",croppedBox
        cropped.save(filePath)


You can use PIL to find rows and cols of your image that are made up purely of your border color.

Using this information, you can easily determine the extents of the inlaid image.

PIL again will then allow you to crop the image to remove the border.


I think it's necessary to supplement @Frank Krueger's answer. He makes a good point, but it doesn't include how to properly crop extra border color out of an image. I found that here. Specifically, I found this useful:

from PIL import Image, ImageChops

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

im = Image.open("bord3.jpg")
im = trim(im)
im.show()


The other answers did not work for me while writing a Blender script (cannot use PIL), so maybe someone else will find this useful.

import numpy as np

def crop(crop_file):
    """crop the image, removing invisible borders"""
    image = bpy.data.images.load(crop_file, check_existing=False)
    w, h = image.size

    print("Original size: " + str(w) + " x " + str(h))

    linear_pixels = image.pixels[:]
    pixels4d = np.reshape(linear_pixels, (h, w, 4))
    
    mask = pixels4d [:,:,3] != 0.
    coords = np.argwhere(mask)
    y0, x0 = coords.min(axis=0)
    y1, x1 = coords.max(axis=0) + 1
    cropped_box = pixels4d[y0:y1, x0:x1, :]
    
    w1, h1 = x1 - x0, y1 - y0
    print("Crop size: " + str(w1) + " x " + str(h1))
    
    temp_image = bpy.data.images.new(crop_file, alpha=True, width=w1, height=h1)
    temp_image.pixels[:] = cropped_box.ravel()
    temp_image.filepath_raw = crop_file
    temp_image.file_format = 'PNG'
    temp_image.alpha_mode = 'STRAIGHT'
    temp_image.save()
0

精彩评论

暂无评论...
验证码 换一张
取 消