【问题标题】:Unable to crop away transparency, Neither PIL getbbox() nor Numpy are working无法裁剪透明度,PIL getbbox() 和 Numpy 都不起作用
【发布时间】:2016-10-22 20:06:53
【问题描述】:

在图像处理方面我是个菜鸟 :( 我有一堆 PNG 文件(其中 300 个),它们具有我希望裁剪的大面积透明度。我显然想自动化这个过程,因此我尝试使用 python 和 PIL。

现在我看看下面的链接, Crop a PNG image to its minimum size,并且还使用此链接建议的 Numpy,Automatically cropping an image with python/PIL,均未成功:( 输出文件与输入文件相同!没有裁剪透明度,相同大小。getbbox 返回相同的宽度和高度。

这是其中一张图片的链接; 98x50button 该图像是一个钟形按钮图标。它是用白色绘制的,所以很难看出是哪个透明背景。预期的结果是一个 20x17 的按钮(20x17 框内的透明度保持完好)

这是我正在使用的代码;

#!/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)

谢谢大家的帮助:)

【问题讨论】:

  • 您能详细说明“没有成功”是什么意思吗?当您在文件上使用代码时会发生什么?
  • 输出文件在外观和大小上与输入文件相同。
  • 裁剪透明度?去除透明度?保存为jpg?
  • 裁剪透明度,98x50 按钮的预期结果是 20x17Button(该框内保留透明度)如果不清楚,请见谅

标签: python image numpy python-imaging-library


【解决方案1】:

您遇到的问题是您的图像包含透明的白色像素,而您的代码只会裁剪透明 黑色的像素。示例图像中大多数像素的 RGBA 值是(255, 255, 255, 0)

autocrop_image2 中,您将获取通道值的max。您可能只想要 alpha 通道的值,所以更改:

image_data_bw = image_data.max(axis=2)

收件人:

image_data_bw = image_data[:,:,3]

然后该功能的其余部分应按预期工作。

autocrop_image 函数也有同样的问题。 getbbox 方法返回非零像素的边界,透明的白色像素不为零。要修复它,请尝试在找到边界框之前将图像从 "RGBA" 模式转换为预乘 alpha "RGBa" 模式:

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

【讨论】:

  • 我会接受你的答案,因为它比我刚刚提出的答案更干净。在 autocrop_image 函数中,我将 image.getbbox() 替换为执行以下操作的函数; def getbbox(image): alphaData = image.tostring("raw", "A") alphaImage = Image.fromstring("L", image.size, alphaData) return alphaImage.getbbox()
【解决方案2】:

这是裁剪透明边框的一种解决方案。
只需将此脚本与您的批处理 .png 文件一起放入您的文件夹中即可:

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)

【讨论】:

    【解决方案3】:

    这是一个新的解决方案;我刚遇到这个问题:

    你有一个 RGBA 图像:

    • 当像素的A为0时,单元格应该是全透明的,
    • 但您的某些像素的 A 为 0,且 RGB 值不为零。
    • Pillow 的 getbbox() 和其他函数现在失败。
    • 当 alpha 为 0 时,您希望将 RGB 强制为 0

    所以:

    • 制作纯黑色 RGBA 图像,每个像素为 (0, 0, 0, 0)
    • 使用您的图像和 RGBA 黑色图像进行合成 图像作为蒙版。
    • 无论你的 A 是 0,你的 RGB 现在都是零
    • 这是一个解决方案;可能有一个内存较低的解决方案。

    代码如下:

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

    【讨论】:

      猜你喜欢
      • 2014-09-04
      • 2012-04-09
      • 1970-01-01
      • 1970-01-01
      • 2013-11-23
      • 2012-06-04
      • 2018-01-08
      • 2011-04-19
      • 1970-01-01
      相关资源
      最近更新 更多