将PNG图像裁剪到最小尺寸

20

如何使用Python裁剪PNG图像的空白边框并将其缩小到最小尺寸?

NB:边框大小不是固定值,可能因图片而异。

7个回答

44

PILgetbbox方法对我有效。

im.getbbox() => 4元组或None

计算图像中非零区域的边界框。边界框返回为一个4元组,定义了左上、右下两个角的像素坐标。如果图像完全为空,则此方法返回None。

下面是我尝试的代码示例,我已经测试过bmp格式,但它也应该适用于png格式。

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")

似乎它不适用于PNG文件(例如使用MSPaint创建的文件)。也许是因为透明度或其他原因? - Basj
@YOU 为什么im.getbbox()在新打开的图像中返回这些数字,而不是(0, 0, 364, 471)?为什么前两个数字不是零? - Andrew Anderson

7

这里有一个现成的解决方案:

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')

示例输入(下载链接,如果您想尝试):

在此输入图像描述

输出:

在此输入图像描述


5

今天我也遇到了同样的问题。这是我裁剪透明边框的解决方案,只需将此脚本放入您带有批处理 .png 文件的文件夹中即可:

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)

这个脚本不起作用。 - steam3d

3

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)

2

我认为有必要补充@Frank Krueger的答案。他提出了一个很好的观点,但是没有包括如何正确地裁剪图像中多余的边框颜色。我在这里找到了相关信息。具体来说,我发现这个很有用:

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()

0

在编写Blender脚本时,其他答案对我没有用(无法使用PIL),因此也许其他人会发现这个有用。

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()

在这行代码image = bpy.data.images.load(crop_file, check_existing=False)中,bpy是什么意思? - Valeriy Van
在这行代码image = bpy.data.images.load(crop_file, check_existing=False)中,bpy是什么意思? - undefined
@ValeriyVan bpy是用于访问Blender数据的Blender API。 - aleng

0

你可以使用PIL来查找图像中完全由边框颜色组成的行和列。

利用这些信息,你可以轻松确定嵌入图像的范围。

然后,再次使用PIL可以裁剪图像以去除边框。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接