PIL 不检测相同的图像
PIL doesn't detect identical images
我需要确定图像层 (mainLayer) 是否在最终图像中可见,它位于图层 layer2 和 layer3 的下方。我需要在不知道其他层是什么的情况下执行此操作。所有这些图像层都是 RGBA;它们也具有相同的尺寸 (2048x2048) 和文件格式 (.png)。
这是我目前的代码:
# Create compImage1, which is all of the layers that go on top of mainLayer
compImage1 = layer.copy()
compImage1.paste(layer2, (0, 0), layer2)
compImage1.paste(layer3, (0, 0), layer3)
# Create compImage2, which is mainLayer with all of the other layers added
compImage2 = mainLayer.copy() # I want to determine this layer's visibility in the final image
compImage1Copy = compImage1.copy()
compImage2.paste(compImage1Copy, (0, 0), compImage1Copy)
# Compare compImage1 and compImage2; if layer, layer2, and/or layer 3 completely obscure
# mainLayer, then there should be no difference between the two images
difference = ImageChops.difference(
compImage1.convert("RGB"), compImage2.convert("RGB")
).getbbox()
if not difference:
print("mainLayer will not be visible")
else:
print("mainLayer will be visible")
当 mainLayer 未被层 layer2、and/or layer3 覆盖时,此代码打印“mainLayer will be visible”。但是,当 mainLayer 被其中一层覆盖时,它仍然会打印“mainLayer will be visible”。
为什么这段代码不能正常工作?有 better/faster 的方法吗?
感谢您提供的任何帮助。
编辑:
我被要求澄清我到底在问什么:
我有 4 个图像层:mainLayer、layer、layer2 和 layer3。我将它们组合成一个图像。我不知道这些层的任何可见部分的位置或大小;都是2048x2048的图层,每个图层的大部分都是透明的
我需要知道 mainLayer 是否会被 layer、layer2 和 layer3 的任意组合完全覆盖(情况 2),或者是否不会(情况 1)。我的代码适用于案例 1,但不适用于案例 2。Here is a diagram depicting cases 1 & 2.
这有点笨拙,但您可以创建一个函数,给定图像,returns 所有 pixel-coordinates(如 xy-tuples)不透明(non-transparent ) 作为一个集合。然后遍历所有层的组合(powerset?),将当前层组合合并为一张图片,取主层opaquepixel-coords集合和opaquepixel-coords集合的差值在当前组合图层图像中。如果任何层组合完全遮盖了主层,我们将提前结束循环。伪代码:
def get_opaque_coords(image):
# Return all pixel coords that are not completely transparent
return set((pixel.x, pixel.y) for pixel in image.get_pixels() if pixel.color.alpha != 0)
main_layer = Image.open(...)
layers = [
Image.open(...),
Image.open(...),
Image.open(...)
]
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
from itertools import chain, combinations
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
for selected_layers in powerset(layers):
merged_image = merge_into_single_image(*selected_layers)
if not(get_opaque_coords(main_layer) - get_opaque_coords(merged_image)):
print("The following selected layers entirely obscure the main layer!")
print(selected_layers)
break
else:
print("None of the layer combinations entirely obscure the main layer.")
我的第一个观察是解决方案完全取决于图像的 alpha 通道 - RGB 通道无关紧要。
我的第二个观察是,解决方案仅取决于基础图像上 alpha 通道的边界框 - 任何其他无关紧要的东西。
考虑到这一点,我将这样开始并丢弃 RGB 通道并裁剪到基本图像的边界框以节省时间和 RAM:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
# Load base image, extract alpha channel and bounding box and crop to it
base = Image.open('base.png').getchannel('A')
bbox = base.getbbox()
print(f'Base image bbox: {bbox}')
base = base.crop(bbox)
base.save('DEBUG-base.png')
# Load overlying layers, extracting alpha channel, cropping and making into Numpy arrays
layers = []
for layer in [0,1,2]:
filename = f'layer{layer}.png'
print(f'Loading {filename}')
im = Image.open(filename).getchannel('A').crop(bbox)
im.save(f'DEBUG-{filename}')
layers.append(np.array(im))
请注意,如果您想进行计时或理解更简单的代码,可以注释掉任何包含单词 DEBUG
的行。
您也可以在 运行 之后进行清理,方法非常简单:
rm DEBUG*.png
请注意,因为我通过将布尔 True/False 图像乘以 255 形成 DEBUG 图像,所以 True 区域将显示为白色,而 false 区域将显示为黑色。
下一步是制作各种条件的布尔掩码,这样我们就可以很容易地一次只考虑问题的一个方面,并在最后简单地组合掩码。在 Numpy 中掩码操作非常快。
# Make PIL Image of base layer into Numpy array
base = np.array(base)
# So we now have base and all layers, just alpha channels, cropped to bounding box as Numpy arrays
# We want to find areas where:
# a) base is non-transparent - we call this "maskA" and store as Numpy array
# b) any layer fully opaque - we call this "maskB" and store as Numpy array
maskA = base > 0
Image.fromarray((maskA*255).astype(np.uint8)).save('DEBUG-maskA.png')
maskB = (layers[0] == 255) | (layers[1] == 255) | (layers[2] == 255)
Image.fromarray((maskB*255).astype(np.uint8)).save('DEBUG-maskB.png')
# result is where there IS something in base layer and there is NO overlay that is fully opaque
res = maskA & ~maskB
Image.fromarray((res*255).astype(np.uint8)).save('result.png')
# Reduce result to simple Yes/No by seeing if any pixel is True
print(f'Result: {np.any(res)}')
所以,如果我将这些图像用作 base
,并且 layer0-2
:
它是这样运行的:
Base image bbox: (80, 800, 301, 951)
Loading layer0.png
Loading layer1.png
Loading layer2.png
Result: True
如果我使用这些图像,答案是 False
。
请注意,我用 ImageMagick 制作的图像是这样的:
案例一
#!/bin/bash
magick -size 2000x2000 xc:none -fill red -draw "rectangle 80,800 300,950" PNG32:base.png
magick -size 2000x2000 xc:none -fill blue -draw "rectangle 280,700 480,850" PNG32:layer0.png
magick -size 2000x2000 xc:none -fill lime -draw "rectangle 320,1000 460,1200" PNG32:layer1.png
magick -size 2000x2000 xc:none -fill orange -draw "rectangle 500,800 800,900" PNG32:layer2.png
magick base.png layer0.png -composite layer1.png -composite layer2.png -composite PNG32:case1.png
案例二
#!/bin/bash
magick -size 2000x2000 xc:none -fill red -draw "rectangle 80,800 300,950" PNG32:base.png
magick -size 2000x2000 xc:none -fill blue -draw "rectangle 70,700 350,900" PNG32:layer0.png
magick -size 2000x2000 xc:none -fill lime -draw "rectangle 50,900 250,1200" PNG32:layer1.png
magick -size 2000x2000 xc:none -fill orange -draw "rectangle 250,900 800,1200" PNG32:layer2.png
magick base.png layer0.png -composite layer1.png -composite layer2.png -composite PNG32:case2.png
请注意,如果您的基础 and/or 层包含部分透明度,and/or “孔”,and/or 不连续的透明区域,and/or 边缘参差不齐,您需要仔细检查我的代码生成的 DEBUG 图像,以确保我涵盖了所有情况。
我需要确定图像层 (mainLayer) 是否在最终图像中可见,它位于图层 layer2 和 layer3 的下方。我需要在不知道其他层是什么的情况下执行此操作。所有这些图像层都是 RGBA;它们也具有相同的尺寸 (2048x2048) 和文件格式 (.png)。
这是我目前的代码:
# Create compImage1, which is all of the layers that go on top of mainLayer
compImage1 = layer.copy()
compImage1.paste(layer2, (0, 0), layer2)
compImage1.paste(layer3, (0, 0), layer3)
# Create compImage2, which is mainLayer with all of the other layers added
compImage2 = mainLayer.copy() # I want to determine this layer's visibility in the final image
compImage1Copy = compImage1.copy()
compImage2.paste(compImage1Copy, (0, 0), compImage1Copy)
# Compare compImage1 and compImage2; if layer, layer2, and/or layer 3 completely obscure
# mainLayer, then there should be no difference between the two images
difference = ImageChops.difference(
compImage1.convert("RGB"), compImage2.convert("RGB")
).getbbox()
if not difference:
print("mainLayer will not be visible")
else:
print("mainLayer will be visible")
当 mainLayer 未被层 layer2、and/or layer3 覆盖时,此代码打印“mainLayer will be visible”。但是,当 mainLayer 被其中一层覆盖时,它仍然会打印“mainLayer will be visible”。
为什么这段代码不能正常工作?有 better/faster 的方法吗? 感谢您提供的任何帮助。
编辑: 我被要求澄清我到底在问什么:
我有 4 个图像层:mainLayer、layer、layer2 和 layer3。我将它们组合成一个图像。我不知道这些层的任何可见部分的位置或大小;都是2048x2048的图层,每个图层的大部分都是透明的
我需要知道 mainLayer 是否会被 layer、layer2 和 layer3 的任意组合完全覆盖(情况 2),或者是否不会(情况 1)。我的代码适用于案例 1,但不适用于案例 2。Here is a diagram depicting cases 1 & 2.
这有点笨拙,但您可以创建一个函数,给定图像,returns 所有 pixel-coordinates(如 xy-tuples)不透明(non-transparent ) 作为一个集合。然后遍历所有层的组合(powerset?),将当前层组合合并为一张图片,取主层opaquepixel-coords集合和opaquepixel-coords集合的差值在当前组合图层图像中。如果任何层组合完全遮盖了主层,我们将提前结束循环。伪代码:
def get_opaque_coords(image):
# Return all pixel coords that are not completely transparent
return set((pixel.x, pixel.y) for pixel in image.get_pixels() if pixel.color.alpha != 0)
main_layer = Image.open(...)
layers = [
Image.open(...),
Image.open(...),
Image.open(...)
]
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
from itertools import chain, combinations
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
for selected_layers in powerset(layers):
merged_image = merge_into_single_image(*selected_layers)
if not(get_opaque_coords(main_layer) - get_opaque_coords(merged_image)):
print("The following selected layers entirely obscure the main layer!")
print(selected_layers)
break
else:
print("None of the layer combinations entirely obscure the main layer.")
我的第一个观察是解决方案完全取决于图像的 alpha 通道 - RGB 通道无关紧要。
我的第二个观察是,解决方案仅取决于基础图像上 alpha 通道的边界框 - 任何其他无关紧要的东西。
考虑到这一点,我将这样开始并丢弃 RGB 通道并裁剪到基本图像的边界框以节省时间和 RAM:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
# Load base image, extract alpha channel and bounding box and crop to it
base = Image.open('base.png').getchannel('A')
bbox = base.getbbox()
print(f'Base image bbox: {bbox}')
base = base.crop(bbox)
base.save('DEBUG-base.png')
# Load overlying layers, extracting alpha channel, cropping and making into Numpy arrays
layers = []
for layer in [0,1,2]:
filename = f'layer{layer}.png'
print(f'Loading {filename}')
im = Image.open(filename).getchannel('A').crop(bbox)
im.save(f'DEBUG-{filename}')
layers.append(np.array(im))
请注意,如果您想进行计时或理解更简单的代码,可以注释掉任何包含单词 DEBUG
的行。
您也可以在 运行 之后进行清理,方法非常简单:
rm DEBUG*.png
请注意,因为我通过将布尔 True/False 图像乘以 255 形成 DEBUG 图像,所以 True 区域将显示为白色,而 false 区域将显示为黑色。
下一步是制作各种条件的布尔掩码,这样我们就可以很容易地一次只考虑问题的一个方面,并在最后简单地组合掩码。在 Numpy 中掩码操作非常快。
# Make PIL Image of base layer into Numpy array
base = np.array(base)
# So we now have base and all layers, just alpha channels, cropped to bounding box as Numpy arrays
# We want to find areas where:
# a) base is non-transparent - we call this "maskA" and store as Numpy array
# b) any layer fully opaque - we call this "maskB" and store as Numpy array
maskA = base > 0
Image.fromarray((maskA*255).astype(np.uint8)).save('DEBUG-maskA.png')
maskB = (layers[0] == 255) | (layers[1] == 255) | (layers[2] == 255)
Image.fromarray((maskB*255).astype(np.uint8)).save('DEBUG-maskB.png')
# result is where there IS something in base layer and there is NO overlay that is fully opaque
res = maskA & ~maskB
Image.fromarray((res*255).astype(np.uint8)).save('result.png')
# Reduce result to simple Yes/No by seeing if any pixel is True
print(f'Result: {np.any(res)}')
所以,如果我将这些图像用作 base
,并且 layer0-2
:
它是这样运行的:
Base image bbox: (80, 800, 301, 951)
Loading layer0.png
Loading layer1.png
Loading layer2.png
Result: True
如果我使用这些图像,答案是 False
。
请注意,我用 ImageMagick 制作的图像是这样的:
案例一
#!/bin/bash
magick -size 2000x2000 xc:none -fill red -draw "rectangle 80,800 300,950" PNG32:base.png
magick -size 2000x2000 xc:none -fill blue -draw "rectangle 280,700 480,850" PNG32:layer0.png
magick -size 2000x2000 xc:none -fill lime -draw "rectangle 320,1000 460,1200" PNG32:layer1.png
magick -size 2000x2000 xc:none -fill orange -draw "rectangle 500,800 800,900" PNG32:layer2.png
magick base.png layer0.png -composite layer1.png -composite layer2.png -composite PNG32:case1.png
案例二
#!/bin/bash
magick -size 2000x2000 xc:none -fill red -draw "rectangle 80,800 300,950" PNG32:base.png
magick -size 2000x2000 xc:none -fill blue -draw "rectangle 70,700 350,900" PNG32:layer0.png
magick -size 2000x2000 xc:none -fill lime -draw "rectangle 50,900 250,1200" PNG32:layer1.png
magick -size 2000x2000 xc:none -fill orange -draw "rectangle 250,900 800,1200" PNG32:layer2.png
magick base.png layer0.png -composite layer1.png -composite layer2.png -composite PNG32:case2.png
请注意,如果您的基础 and/or 层包含部分透明度,and/or “孔”,and/or 不连续的透明区域,and/or 边缘参差不齐,您需要仔细检查我的代码生成的 DEBUG 图像,以确保我涵盖了所有情况。