混合图像而不改变颜色?
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(底部)中展示了原理。
- 将
img
和 scr2
从 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)
我不确定它是否有意义...
在学习 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(底部)中展示了原理。
- 将
img
和scr2
从 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)
我不确定它是否有意义...