无法去除透明度,PIL getbbox() 和 Numpy 都不起作用
Unable to crop away transparency, Neither PIL getbbox() nor Numpy are working
我在图像处理方面非常菜鸟:(
我有一堆 PNG 文件(其中 300 个),它们具有我希望裁剪的大面积透明度。我显然想使该过程自动化,因此我尝试使用 python 和 PIL。
下面我来看看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,两者都没有成功 :( 输出文件与输入文件相同!没有裁剪透明度,大小相同。getbbox 返回相同的宽度和高度。
这是其中一张图片的 link; 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)
谢谢大家的帮助:)
您遇到的问题是您的图像包含透明的白色像素,而您的代码只会裁剪透明 和 黑色的像素。示例图像中大部分像素的 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
方法returns非零像素的边界,透明的白色像素不为零。要修复它,请尝试在找到边界框之前将图像从 "RGBA"
模式转换为预乘 alpha "RGBa"
模式:
bbox = image.convert("RGBa").getbbox()
这是裁剪透明边框的一种解决方案。
只需将此脚本与您的批处理 .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)
这是一个新的解决方案;我刚 运行 遇到这个问题:
您有一张 RGBA 图像:
- 当像素的A为0时,单元格应该完全t运行sparent,
- 但是您的某些像素的 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())
我在图像处理方面非常菜鸟:( 我有一堆 PNG 文件(其中 300 个),它们具有我希望裁剪的大面积透明度。我显然想使该过程自动化,因此我尝试使用 python 和 PIL。
下面我来看看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,两者都没有成功 :( 输出文件与输入文件相同!没有裁剪透明度,大小相同。getbbox 返回相同的宽度和高度。
这是其中一张图片的 link; 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)
谢谢大家的帮助:)
您遇到的问题是您的图像包含透明的白色像素,而您的代码只会裁剪透明 和 黑色的像素。示例图像中大部分像素的 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
方法returns非零像素的边界,透明的白色像素不为零。要修复它,请尝试在找到边界框之前将图像从 "RGBA"
模式转换为预乘 alpha "RGBa"
模式:
bbox = image.convert("RGBa").getbbox()
这是裁剪透明边框的一种解决方案。
只需将此脚本与您的批处理 .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)
这是一个新的解决方案;我刚 运行 遇到这个问题:
您有一张 RGBA 图像:
- 当像素的A为0时,单元格应该完全t运行sparent,
- 但是您的某些像素的 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())