图像专家:优化我的Python PNG透明函数

5 投票
4 回答
804 浏览
提问于 2025-04-15 23:59

我需要把PNG图片中所有白色(或接近白色)的像素替换成透明。

我在AppEngine上使用Python,所以不能使用像PIL、imagemagick这样的库。AppEngine有一个图像库,但主要是用来调整图像大小的。

我找到一个很不错的pyPNG模块,并且写了一个小函数来实现我需要的功能:

make_transparent.py

主要循环的伪代码大概是这样的:

for each pixel:
    if pixel looks "quite white":
        set pixel values to transparent
    otherwise:
        keep existing pixel values

而(假设是8位值)“相当白”的定义是:

where each r,g,b value is greater than "240" 
AND each r,g,b value is within "20" of each other

这是我第一次以这种方式处理原始像素数据,虽然能工作,但性能非常差。看起来应该有更高效的方法来处理这些数据,而不是这样逐个像素地遍历?(矩阵?)

我希望有经验的人能指出我算法中一些明显的错误或改进之处。

谢谢!

4 个回答

0

这个问题似乎跟Python中的循环有关,而不是跟图片有关。

Python的循环速度非常慢,所以最好避免使用循环,改用内置的循环操作。

在这里,如果你愿意复制图片的话,可以使用列表推导式:

def make_transparent(pixel):
  if pixel looks "quite white": return transparent
  else: return pixel

newImage = [make_transparent(p) for p in oldImage]
1

老实说,我能想到的唯一方法就是在你的图片上随便选几个点,然后使用一种叫做洪水填充的技术。

这种方法效果不错,前提是你的图片有大块连续的白色区域(如果你的图片是一个前面没有或只有很小孔洞的物体,那你就很幸运了——你其实有了一个选择填充点的好方法)。

(声明:我不是图像方面的专家 =/ )

1

这个方法还是会检查每一个像素,但可能会更快:

new_pixels = []
for row in pixels:
    new_row = array('B', row)
    i = 0
    while i < len(new_row):
        r = new_row[i]
        g = new_row[i + 1]
        b = new_row[i + 2]
        if r>threshold and g>threshold and b>threshold:
            m = int((r+g+b)/3)
            if nearly_eq(r,m,tolerance) and nearly_eq(g,m,tolerance) and nearly_eq(b,m,tolerance):
                new_row[i + 3] = 0
        i += 4
    new_pixels.append(new_row)

它避免了使用切片生成器,因为切片生成器会为每一个像素复制整行的像素(每次少复制一个像素)。

这个方法还通过直接复制输入行来提前分配输出行,然后只写入那些发生变化的像素的透明度值。

如果能不分配新的像素集合,直接在源图像的像素上写入,那就会更快(前提是你不需要源图像做其他事情)。

撰写回答