【问题标题】:how to crop a colour 8 bit per pixel png image and save in colour in python如何裁剪每像素 8 位颜色的 png 图像并在 python 中保存颜色
【发布时间】:2022-11-26 06:26:10
【问题描述】:

我有一个要裁剪的 png 图像,删除顶部和底部的空白区域。

我使用以下代码: 从 PIL 导入图像 对于 pa_files 中的 f: 图片=f im = 图片.open(img) 宽度,高度 = im.size 像素 = 列表(im.getdata()) pixels = [pixels[i * width:(i + 1) * width] for i in range(height)]

    white_lines = 0
    for line in pixels:
        white_count = sum([sum(x) for x in line]) - im.width * 255*4
        if (white_count) == 0:
            white_lines += 1
        else:
            break

    crop_from_top = white_lines

    pixels.reverse()

    white_lines = 0
    for line in pixels:
        white_count = sum([sum(x) for x in line]) - im.width * 255*4
        if (white_count) == 0:
            white_lines += 1
            #print(white_count)
        else:
            break

    crop_from_bottom = white_lines

    crop_from_bottom, crop_from_top, im.size

    # Setting the points for cropped image
    left = 0
    top = crop_from_top - 5
    right = im.width
    bottom = im.height - (crop_from_bottom- 5)

    im1 = im.crop((left, top, right, bottom))

    im1.save(img)

这适用于 32 位 png

但现在我遇到了一个 8 位 png,并尝试运行相同的脚本,但遇到了这个错误:

TypeError: 'int' object is not iterable

进一步看,我看到每个像素由 0:255 表示

我们看到像素值 153 出现了 2m 次。

我使用以下内容进行裁剪:

im = Image.open(f).convert('L')
im = im.crop((x1, y1, x2, y2))
im.save('_0.png')

成功,但随后我的图像返回灰度。

前:

后:

它从蓝色变为灰度。

如何动态裁剪 8 位类型图像的边距,并再次以彩色保存?

【问题讨论】:

    标签: python image colors python-imaging-library


    【解决方案1】:

    问题是你必须考虑许多不同的情况。

    • 8 位 R、G、B、A 图像(这显然是你最初拥有的)
    • 8 位 R、G、B 图像
    • 8 位灰度级
    • 8 位索引图像

    对于 8 位灰度级,像素不是 4-uplets (R,G,B,A) 而是数字。因此,sum(x) 应替换为 x。然后你可以期望它值 255,而不是 255*4 白色(但这也不是确定的。也有一些 'MINISWHITE' 格式。因为我没有例子,而且对 PIL 不是很熟悉(你显然正在使用),不能确定 PIL 是否会使它透明(我的意思是,如果它会在加载时转换它)。

    例如在你的代码的第一部分

    
        white_lines = 0
        for line in pixels:
            white_count = sum([x for x in line]) - im.width * 255
            if (white_count) == 0:
                white_lines += 1
            else:
                break
    

    对于 R、G、B 图像,您的代码就可以了,但是当总和为 255*4 时,白色不是,而是 255*3

    您的第二个示例是索引图像。所以 8 位,但无论如何都是颜色。 通过将其转换为“L”,即灰度级,您得到了您所抱怨的。

    因此,这里的简单答案是建议您将所有内容转换为RGBRGBA,然后使用您的代码。

    for f in pa_files:
        img = f
        im = Image.open(img)
        width, height = im.size
        pixels = list(im.convert('RGBA').getdata())
        pixels = [pixels[i * width:(i + 1) * width] for i in range(height)]
    # Rest of code unchanged
        im1 = im.crop((left, top, right, bottom))
    
        im1.save(img)
    

    转换不会影响图像(它不会转换一个愚蠢的 RGBA 图像中的灰度图像,其所有 R=G=B,浪费空间),因为转换只是为了获得用于计算作物面积的像素阵列,并对未转换的原始图像进行裁剪。

    我无法抗拒建议您不惜一切代价避免遍历像素的冲动。

    您可以获取数据的 numpy 数组视图,而不是创建 python 列表(您必须自己重塑)。

    import numpy as np
    arr=np.asarray(im.convert('RGB'))
    

    然后,检查一行i是否为白色

    LineIisWhite=(arr[i]==255).all()
    

    (arr[i]==255) 是一个布尔值数组,与你的线的形状相同,也就是说,这里是 W×3,那里是 True,那里是 255,其他地方是 False。

    (arr[i]==255).all() 是一个布尔值,表示先前数组中的所有布尔值是否为真。所以如果线是白色的。

    那仍然无法避免对行的迭代。但我们可以做得更好。

    all 限制到最后 2 个轴(W 和 3),通过添加 axis=(1,2) 并应用于整个图像,我们得到一个 H 布尔值数组,如果每行中所有 W×3 布尔值都为真,则它们为真。

    whitelines=(arr==255).all(axis=(1,2))
    

    在我构建的示例图像中,结果是

    array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True])
    

    然后,不需要遍历这个布尔数组来计算开头或结尾的True的数量

    crop_from_top = np.argmin(whitelines)
    crop_from_bottom = np.argmin(whitelines[::-1])
    

    所以,一共

    import numpy as np
    for f in pa_files:
        img = f
        im = Image.open(img)
        width, height = im.size
        arr = np.asarray(im.convert('RGB'))
        whitelines=(arr==255).all(axis=(1,2))
        crop_from_top = np.argmin(whitelines)
        crop_from_bottom = np.argmin(whitelines[::-1])
    
        # Setting the points for cropped image
        left = 0
        top = crop_from_top - 5
        right = im.width
        bottom = im.height - (crop_from_bottom- 5)
    
        im1 = im.crop((left, top, right, bottom))
    
        im1.save(img)
    

    最后一点:由于索引必须近似颜色,甚至由于 JPEG 编码,白色像素可能不是纯白色。

    因此,您可能希望将 arr==255 更改为 arr>=250 或类似的内容。

    请注意,这里的 numpy 数组是只读的。我们只用它来计算要裁剪多少行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      • 2015-04-16
      • 1970-01-01
      • 2018-05-04
      • 1970-01-01
      相关资源
      最近更新 更多