Python 从两个图像的差异创建Alpha覆盖图像

Python 从两个图像的差异创建Alpha覆盖图像,python,numpy,opencv,python-imaging-library,Python,Numpy,Opencv,Python Imaging Library,我在这个问题上遇到了一个奇怪的困难时期。我有两张照片,我们称之为基地 和光 (实际图像的分辨率要高得多,但问题应该是一样的)。我的目标是生成一个新的图像,我们将其称为alpha,当覆盖在base上时,它会产生光。我试过做各种各样的差分/减法、缩放、亮度调整等,但我所做的一切似乎都不能真正产生结果(通常看起来很接近,但从来都不精确) 例如,在没有使用图像编辑软件进行猜测和检查的情况下,这段代码与我得到的代码差不多 from PIL import Image, ImageEnhance impo

我在这个问题上遇到了一个奇怪的困难时期。我有两张照片,我们称之为基地

和光

(实际图像的分辨率要高得多,但问题应该是一样的)。我的目标是生成一个新的图像,我们将其称为
alpha
,当覆盖在
base
上时,它会产生
光。我试过做各种各样的差分/减法、缩放、亮度调整等,但我所做的一切似乎都不能真正产生结果(通常看起来很接近,但从来都不精确)

例如,在没有使用图像编辑软件进行猜测和检查的情况下,这段代码与我得到的代码差不多

from PIL import Image, ImageEnhance
import numpy
import blend_modes
import sys
import scipy


background_img_raw = Image.open(sys.argv[1])
background_img_raw.putalpha(255)
background_img = numpy.array(background_img_raw)
background_img_float = background_img.astype(float)
foreground_img_raw = Image.open(sys.argv[2])
foreground_img_raw.putalpha(255)
foreground_img = numpy.array(foreground_img_raw)
foreground_img_float = foreground_img.astype(float)
opacity = 1.0
blended_img_float = blend_modes.difference(background_img_float, foreground_img_float, opacity)
r, g, b, a = np.transpose(blended_img_float)
alpha = np.clip((r+g+b)*2, 0, 255)
r = np.clip(r + 100, 0, 255)
g = np.clip(g + 100, 0, 255)
b = np.clip(b + 100, 0, 255)
blended_img_float = np.transpose([r, g, b, alpha])
blended_img = numpy.uint8(blended_img_float)
blended_img_raw = Image.fromarray(blended_img)
blended_img_raw.save(sys.argv[3])
结果是这样的

有什么建议吗

编辑:在Paint.net中四处摆弄,如果执行以下操作,您可以非常接近:

  • 混合->差异层
  • 在黑色上使用
  • 最大亮度和最小对比度
  • 色相稍微偏移,最大化饱和度,增加亮度
  • 这是大量的转换,但结果比我通过编程得出的结果更接近。无论死神在做什么,看起来都比我在做的更接近正确。结果如下:

    不幸的是,这并不能解决问题,因为我需要反复这样做,但这给了我希望,它是可以解决的

    编辑2:请看这篇文章,了解我试图实现的一个例子:

    编辑3:我现在需要离开这个问题,但我认为我没有努力制作正确的图像,而是在CSS方面找到了一个解决方法,让混合更接近合适的效果。现在,我只使用如下图像之间的差异:

    import numpy as np
    from PIL import Image
    import sys
    
    def create_mask(foreground_filename, background_filename, output_filename):
        foreground = cv2.imread(foreground_filename)
        background = cv2.imread(background_filename)
        diff = foreground - background
        diff = cv2.cvtColor(diff, cv2.COLOR_BGR2RGB)
        out_pil_image = Image.fromarray(diff)
        out_pil_image.save(output_filename)
    
    if __name__ == "__main__":
        create_mask(sys.argv[1], sys.argv[2], sys.argv[3])
    
    结果如下所示:


    现在对我来说已经足够好了。我很想知道这个问题的正确答案,如果有人知道的话

    我不太了解Opecv,但在ImageMagick中,我会做以下工作:

    基数:

    灯光:



    补充:

    这里有一种替代方法,可以在原始图像上合成彩色透明图像。但是你必须稍微调整一下颜色。我在基础图像中测量了一个亮黄色的点

    1) turn the minus image into gray by desaturating it. The stretch the dynamic range, then apply a gamma adjustment.
    
    2) create a look-up table between black and that yellow color and apply it to the gray image with -clut
    
    3) put the gray image into the alpha channel of the colorized image
    
    4) composite the previous image over the base image
    
    
    convert minus.png -modulate 100,0,100 -auto-level -gamma 1.5 minus_gray.png
    convert minus_gray.png \( xc:black xc:"rgb(251,220,120)" +append \) -clut minus_color.png
    convert minus_color.png minus_gray.png -alpha off -compose copy_opacity -composite alpha.png
    convert base.png alpha.png -compose over -composite result3.png
    
    以下是这些步骤的结果:

    灰色:

    彩色灰色:

    透明的:

    结果:

    补充2:

    convert light.png -alpha copy -channel a -evaluate multiply 2 +channel alpha.png
    convert base.png alpha.png -compose over -composite result3.png
    

    阿尔法:

    结果:


    这是对您的问题的纯PIL回答

    要获得两个图像之间的差异,请执行以下操作:-

    from PIL import Image, ImageChops, ImageEnhance
    
    img1 = Image.open(r"base.png")
    img2 = Image.open(r"light.png")
    
    diff = ImageChops.difference(img2, img1)
    
    ImageChops.difference()
    接受两个图像对象作为参数,并返回两个图像对象之间逐像素差值的绝对值。简而言之,它计算两幅图像之间的差异

    差异显示的输出()
    :-

    现在,由于我们已经获得了这两幅图像的差异点,我们现在可以从diff图像和原始base图像中创建一幅合成图像

    将此代码行添加到以前的代码:-

    overlaid_img1 = Image.blend(img1, diff, 0.5)
    
    Image.blend()
    将两个图像对象和一个阈值作为参数,并通过使用常量alpha(阈值参数)在两个输入图像之间插值来创建新图像。输出由以下公式指导:-

    图像1*(1.0-α)+图像2*α

    其中,image1和image2是图像对象,alpha是阈值。由于我们使用
    0.5
    作为阈值,公式更改为:-

    (图像1*0.5)+(图像2*0.5)

    这意味着我们要取image1的一半颜色值(基本)和image2的一半颜色值(差异)。因为,差分图像(diff)大部分包含黑色像素,因此我们的基础图像的颜色像素亮度值减半(或者比以前更暗)

    叠加的
    输出\u img1.show()
    :-

    现在,为了使基础图像的像素获得原始亮度值,我们需要将通过
    image.blend()
    获得的输出图像的亮度增加一倍

    将此行添加到原始代码:-

    bright_overlay_img1 = ImageEnhance.Brightness(overlaid_img1).enhance(2.0)
    
    ImageEnhance.Brightness()
    是一个用于调整图像亮度的类
    ImageEnhance.Brightness.enhance()
    是用于执行此操作的函数

    enhanced(2.0)
    所做的是,在
    overlaid\u img1
    图像对象中,它使每个像素的亮度值加倍

    输出
    bright\u overlay\u img1.show()
    :-

    p.S.:-原始代码是通过在答案中添加所有提供的代码生成的。我对原始代码进行了分段,以便解释每个函数的工作原理及其背后的推理。因此,对于完整的工作,只需附加答案中提供的每个代码

    编辑:-

    刚才看到您对@fmw42答案的评论,结果发现您的要求与您提出的问题不同

    据我所知,您希望
    diff
    (存储两幅图像差异的图像)的格式支持alpha(透明度),而不是完全黑色的图像

    为此,请使用此代码:-

    # This is the full code
    
    from PIL import Image, ImageChops, ImageEnhance
    
    img1 = Image.open(r"base.png")
    img2 = Image.open(r"light.png")
    
    diff = ImageChops.difference(img2, img1).convert("RGBA")
    
    pixels = diff.load()
    
    for x in range(diff.size[0]):
        for y in range(diff.size[1]):
            if pixels[x, y] == (0, 0, 0, 255):
                pixels[x, y] = (0, 0, 0, 0)
    
    diff.save("alpha_difference.png")   # saving the difference image (having alpha channel)
    
    overlaid_img1 = Image.blend(img1.convert("RGBA"), diff, 0.5)
    
    bright_overlay_img1 = ImageEnhance.Brightness(overlaid_img1).enhance(2.0).convert("RGB")
    
    上面的代码与我前面提到的代码不同之处在于,它将差异图像
    diff
    转换为alpha通道图像,如果像素具有完全黑色(与原始图像没有差异),它将使像素透明

    差分图像(具有alpha通道):-

    两段代码的输出仍然相同

    附言:-我建议您更改问题的标题/描述,并明确说明您的问题
    bright_overlay_img1 = ImageEnhance.Brightness(overlaid_img1).enhance(2.0)
    
    # This is the full code
    
    from PIL import Image, ImageChops, ImageEnhance
    
    img1 = Image.open(r"base.png")
    img2 = Image.open(r"light.png")
    
    diff = ImageChops.difference(img2, img1).convert("RGBA")
    
    pixels = diff.load()
    
    for x in range(diff.size[0]):
        for y in range(diff.size[1]):
            if pixels[x, y] == (0, 0, 0, 255):
                pixels[x, y] = (0, 0, 0, 0)
    
    diff.save("alpha_difference.png")   # saving the difference image (having alpha channel)
    
    overlaid_img1 = Image.blend(img1.convert("RGBA"), diff, 0.5)
    
    bright_overlay_img1 = ImageEnhance.Brightness(overlaid_img1).enhance(2.0).convert("RGB")