【问题标题】:Blending images without color change?混合图像而不改变颜色?
【发布时间】:2021-08-31 11:29:39
【问题描述】:

在学习 OpenCV 时,我意识到每当我混合两个图像时,scr2 的颜色都会以某种方式发生变化(取决于 scr1 的颜色)。

但是,我知道这不是解释我的问题的信息丰富且清晰的方式;我不知道如何描述这个问题,因为我没有颜色方面的专业知识,所以我想向您展示我对图像和代码的含义。

输入图片:Input image

import cv2
import numpy as np

img=cv2.imread("inputImage.png")

scr2=255*np.ones((img.shape[0],img.shape[1],3),dtype=np.uint8)


#adding lines 

cv2.line(scr2,(100,0),(100,img.shape[1]),(0,255,0),3) 
cv2.line(scr2,(300,0),(300,img.shape[1]),(255,0,0),3)
cv2.line(scr2,(500,0),(500,img.shape[1]),(0,0,255),3)

#blending

blend=cv2.addWeighted(img,0.7,scr2,0.3,0)
crop=blend[60:100,460:530]

cv2.imwrite("crop.png",crop)

cv2.imwrite("line.png",blend)

output image

cropped in red line 如您所见,即使我为每条线添加了一种颜色,但线的颜色已经改变取决于背景颜色。裁剪后的图像中的红线似乎不是红色的。

您能否详细说明为什么会发生这种情况以及如何避免这个问题?我的意思是,我不希望线条上的颜色发生变化。

感谢您的关注。

【问题讨论】:

  • 两种颜色图像的混合是一种颜色的混合,因为它是两种颜色的加权平均值。当您叠加两种部分透明的颜色时,它们会混合或混合颜色,因为您可以看到一种在另一种之后。使用两种不透明颜色的权重就像将权重作为透明度。保留颜色的唯一方法是将线条的粗细设置为 1(并将背景设置为 1),以便在放置在图像上时它们实际上是完全不透明的。换句话说,只需直接在背景图像上画线,无需添加加权。
  • @fmw42 我明白了,感谢您提供的信息丰富的回复。但是如果我直接在目标图像中添加线条;很多信息也消失了(因为重叠)。那么,在保留实际颜色的同时混合图像是不可能的吗?

标签: python numpy opencv colors contrast


【解决方案1】:

如果您希望混合类似于感知颜色混合(更接近人类预期的混合颜色),您可以在LAB 颜色空间中应用混合。

优势:

与 RGB 和 CMYK 颜色模型不同,CIELAB 旨在接近人类视觉。

我已经在我的关注answer(在底部)中演示了主体。

  • imgscr2 从 BGR 转换为 LAB 色彩空间。
  • 在 LAB 色彩空间中应用 addWeighted
  • 将结果从 LAB 转换为 BGR 颜色空间。

由于scr2 的白色背景混合困扰我,我还将img 的原始像素复制到blend,其中scr2 中的像素是白色的。


这是一个完整的代码示例:

import cv2
import numpy as np

img = cv2.imread("inputImage.png")

scr2 = np.full_like(img, 255)

img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)  # Convert color space from BGR to LAB color space

# adding lines
cv2.line(scr2, (100, 0), (100, img.shape[1]), (0, 255, 0), 3)
cv2.line(scr2, (300, 0), (300, img.shape[1]), (255, 0, 0), 3)
cv2.line(scr2, (500, 0), (500, img.shape[1]), (0, 0, 255), 3)

scr2_lab = cv2.cvtColor(scr2, cv2.COLOR_BGR2LAB)  # Convert color space from BGR to LAB color space

# blending
#blend = cv2.addWeighted(img, 0.7, scr2, 0.3, 0)
blend_lab = cv2.addWeighted(img_lab, 0.7, scr2_lab, 0.3, 0)  # Blend images in LAB color space
blend = cv2.cvtColor(blend_lab, cv2.COLOR_LAB2BGR)  # Convert color space from LAB to BGR color space

# Replace blended "background" white pixels of scr2 with original pixel values from img:
mask = np.all(scr2 == 255, 2).astype(np.uint8)  # Mask value is 1 where 3 color channels are 255
cv2.copyTo(img, mask, blend)  # Copy pixels from img to blend where mask != 0.


crop = blend[60:100, 460:530]

cv2.imwrite("crop2.png", crop)
cv2.imwrite("line2.png", blend)

# Show the results
cv2.imshow('mask', mask*255)
cv2.imshow('img', img)
cv2.imshow('blend', blend)
cv2.imshow('crop', crop)
cv2.waitKey()
cv2.destroyAllWindows()

结果:
左:crop 混合在 LAB 空间中。
右:crop 混合在 BGR 空间中。

注意:

  • 它更准确,但在这种情况下看起来不是很有帮助...

将亮度从src2复制到blend

根据您的评论,您似乎希望保持src2 的彩色线条的亮度,而不是将亮度与背景混合。

在 LAB 颜色空间中,L 颜色通道是亮度。

您可以将亮度从src2 复制到blend,其中src2 不是白色。

以下是代码的相关部分:

blend_lab = cv2.addWeighted(img_lab, 0.7, scr2_lab, 0.3, 0) # 在 LAB 颜色空间中混合图像

# Get the luminance channel (get NumPy slice)
scr2_luminance = scr2_lab[:, :, 0]
blend_luminance = blend_lab[:, :, 0]
blend_luminance[np.any(scr2 != 255, 2)] = scr2_luminance[np.any(scr2 != 255, 2)]  # Copy the luminace from scr2_lab to blend_lab where scr2 is not white.

blend = cv2.cvtColor(blend_lab, cv2.COLOR_LAB2BGR)  # Convert color space from LAB to BGR color space

结果:
crop:

blend:

注意:

  • 亮度看起来是正确的,但我们正在失去色彩饱和度。

我们可以在 HSV 颜色空间中将饱和度通道从 scr2 复制到 blend

scr2_hsv = cv2.cvtColor(scr2, cv2.COLOR_BGR2HSV)
blend_hsv = cv2.cvtColor(blend, cv2.COLOR_BGR2HSV)
blend_hsv[:,:,1][np.any(scr2 != 255, 2)] = scr2_hsv[:,:,1][np.any(scr2 != 255, 2)]  # Copy the saturation channel.
blend = cv2.cvtColor(blend_hsv, cv2.COLOR_HSV2BGR)

我不确定这是否有意义......

【讨论】:

  • 非常丰富的答案。非常感谢您的关注。看来我可以用 LAB 或 HSV 颜色空间解决那些“人类视觉中感知到的颜色”问题。
【解决方案2】:

我想我误解了你的问题。如果您的问题是没有线条的图像发生了变化,那是因为您为 scr2 使用了白色背景。然后白色与输出中的图像混合。让它 scr2=img.copy() 代替你现在拥有的。然后试试你的代码。所以以 Python/OpenCV 作为演示,使用 Lena 图像作为背景,这里是你的代码:

import cv2
import numpy as np

img=cv2.imread("lena.png")

scr2=255*np.ones((img.shape[0],img.shape[1],3),dtype=np.uint8)

cv2.line(scr2,(50,0),(50,img.shape[1]),(0,255,0),13) 
cv2.line(scr2,(100,0),(100,img.shape[1]),(255,0,0),13)
cv2.line(scr2,(150,0),(150,img.shape[1]),(0,0,255),13)

blend=cv2.addWeighted(img,0.7,scr2,0.3,0)

cv2.imwrite("line.png",blend)

您现在可以看到背景混合了白色。

但是,如果我们使用 scr2 的输入图像,

import cv2
import numpy as np

img=cv2.imread("lena.png")

scr2=img.copy()

cv2.line(scr2,(50,0),(50,img.shape[1]),(0,255,0),13) 
cv2.line(scr2,(100,0),(100,img.shape[1]),(255,0,0),13)
cv2.line(scr2,(150,0),(150,img.shape[1]),(0,0,255),13)

blend=cv2.addWeighted(img,0.7,scr2,0.3,0)

cv2.imwrite("line.png",blend)

然后我们得到:

保留了背景颜色。

【讨论】:

  • 非常感谢!它给出了更好的结果!但是,不幸的是,我的原始输入图像仍然存在一些小问题。这是我原始输入中的结果,而不是 Lena(使用您的代码):ibb.co/9tbnv31(绿线裁剪)如您所见,“绿线的亮度(不确定该术语是否为真)”正在改变,具体取决于背景颜色。
  • 这就是我最初提到的问题。 addWeighted 两种颜色会改变外观,因为它混合了两种颜色。因此,黑色上的绿色将绿色与黑色混合以使绿色变暗。而粉红色上的绿色将两者混合,形成粉绿色。
  • 我明白了。非常感谢您的关注和时间!我会看得更深。
猜你喜欢
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-26
  • 2013-03-26
  • 2016-01-16
  • 1970-01-01
相关资源
最近更新 更多