混合图像而不改变颜色?

Blending images without color change?

在学习 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 如您所见,即使我为每条线添加了一种颜色,线条的颜色也会根据背景颜色发生变化。红线在裁剪后的图片里好像不是红色的

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

感谢您的关注。

我想我误解了你的问题。如果您的问题是没有线条的图像发生了变化,那是因为您为 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)

然后我们得到:

保留了背景颜色。

如果您希望混合类似于感知颜色混合(更接近人类预期的混合颜色),您可以在 LAB 颜色 space.

中应用混合

优点:

Unlike the RGB and CMYK color models, CIELAB is designed to approximate human vision.

我已经在下面answer(底部)中展示了原理。

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

由于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 space.
中 右:crop 混合在 BGR space.

注:

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

将亮度从src2复制到blend:

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

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

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

代码的相关部分如下:

blend_lab = cv2.addWeighted(img_lab, 0.7, scr2_lab, 0.3, 0) # 以 LAB 颜色混合图像 space

# 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:

注:

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

我们可以将饱和度通道从 scr2 复制到 blend 的 HSV 颜色 space:

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)

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