在 Opencv 中保持原始颜色强度的同时更改颜色

Change colors while maintining intensity of the original color in Opencv

提问人:diegobc11 提问时间:11/16/2023 最后编辑:diegobc11 更新时间:11/18/2023 访问量:75

问:

我想将推土机的颜色更改为特定的十六进制颜色(在本例中为红色),Original image同时保留强度(光线对推土机的影响)。 我只想改变它的黄色部分,到目前为止,我已经创建了一个具有浅黄色和深黄色调下限和上限的蒙版,并提取了该蒙版,但是我不知道如何将红色应用于蒙版并保持颜色的相对强度,而不仅仅是所有相同的红色色调。

像下图这样的东西会很棒(如果可能的话,质量会更好,因为似乎有一些颜色设置不正确)expected result

我已经看到了一些使用 HSV 的解决方法,但没有什么能真正完成这项工作。

到目前为止,我的代码涉及创建一个蒙版来提取我想要更改的颜色并将该蒙版转换为 HSV 颜色以保持颜色的强度,之后我尝试修改 HSV 以获得所需的颜色,但是它并不精确,没有确切的方法将 HSV 更改为我想要的十六进制或 rgb 颜色?

import cv2
import numpy as np
import os
import math
from numpy import inf

# set the bounds for the light and dark shade of color you want to mask
lower_yellow = np.array([20, 100, 100])
upper_yellow = np.array([30, 255, 255])  

all_images = sorted(os.listdir('data/audi_a6'))
for i, image in enumerate(all_images):
    print(image, end='\r')
    img = cv2.imread(f'data/lego/{image}')
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    yellow_mask = cv2.inRange(hsv, lower_yellow, upper_yellow)
    inv_mask = cv2.bitwise_not(yellow_mask)

    h, s, v = cv2.split(hsv)
    h = np.mod(h - h + 182, 180)
    s = np.clip(s + 120, 0, 255)
    v = np.clip(v, 0, 255)
    hsv = cv2.merge([h, s, v])

    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

    result = cv2.bitwise_or(cv2.bitwise_and(img, img, mask=inv_mask), cv2.bitwise_and(bgr, bgr, mask=yellow_mask))

    # Save the result
    cv2.imwrite(f'output_change_color/lego/frame_{i:03}.jpg', result)
OpenCV 图像处理 色彩空间 HSV

评论

0赞 Mark Setchell 11/17/2023
请添加您的代码以制作一个适当的最小可重现示例
0赞 diegobc11 11/17/2023
到目前为止,我已经添加了我的代码,它可以工作,但实际上设置我想要的颜色是不可能的。@Mark 塞切尔
0赞 Mark Setchell 11/23/2023
你已经很安静了。您对我们的回答有什么成功吗?还是仍然存在问题?

答:

1赞 Mark Setchell 11/17/2023 #1

你似乎很难区分黄色的机器和它下面的“泥泞的轨道”。解决这些问题的一种方法是查看不同的颜色空间,看看哪一个是很好的鉴别器。因此,我将您的图像转换为许多不同的色彩空间,并为您并排拆分各个通道:

enter image description here

因此,您可以查看每个色彩空间的每个通道,看看哪一个最适合您。例如,的第三个通道,或 的第二个通道 ...CIELabHCL

评论

0赞 diegobc11 11/17/2023
这真是太棒了!可视化效果也不错,但我的问题更多的是着色部分,而不是蒙版本身的分割。如果有意义的话,我希望能够将黄色更改为我想要的任何颜色,给定 rgb 或十六进制颜色代码。提供的蓝色图片实际上相当不错,但我不确定如何在不手动尝试数百种组合直到接近所需颜色的情况下为红色 hsv[0, 90, 90] 实现相同的效果。我尝试将 h、s、v 值分别更改为 0、90、90,但输出不好。
0赞 Mark Setchell 11/17/2023
保留原始版本中的“值”和“饱和度”,但只替换“色相”怎么样?如果您收到 RGB 的新颜色,只需将其更改为 HSV 即可获得色相。此外,没有什么可以阻止您在一个色彩空间中分割(并获得蒙版),然后使用蒙版替换另一个色彩空间。
0赞 diegobc11 11/17/2023
我也尝试过只操纵色调值。h = np.mod(h - h + 182, 180), s = np.clip(s, 0, 255), v = np.clip(v, 0, 255),但颜色仍然不接近 HSV 的 HSV 表示(0, 90%, 90%)
0赞 Mark Setchell 11/17/2023
我很着急,但请记住,OpenCV 存储的色调是您在其他地方可能看到的预期值的一半,因此 360 度色调可以容纳只能支持 0..255 的色调。所以,如果你想让色相出现,比如说 80,你可以在 OpenCV 中使用 40。或者,您可以将图像类型更改为可以使用常规的 0...360 范围。uint8float
0赞 diegobc11 11/18/2023
难道不会像我在示例中那样使用 mod (% 180) 将其修复为 8 位变量作为将色调值减半的解决方法吗?
1赞 Cris Luengo 11/18/2023 #2

我只需要做一件事就可以让你的代码产生预期的结果。黄色像素的 H 通道约为 25。我们希望它改为 0。所以我们需要减去 25: .H - H_yellow + H_red

但是由于是一个数组,减去 25 将导致一些像素环绕到 255,这不是 H 的有效值(OpenCV 将其定义为 8 位图像的 0-180 范围内)。因此,如果添加 0,我们添加 180,则操作可以执行正确的操作:huint8np.mod()

h = np.mod(h + 180 - 25, 180)

这显然不是通用的。为了更通用地做到这一点,我们最好使用浮点运算进行算术运算,因为无需担心溢出或换行。这还有另一个优点:OpenCV 不会将 HSV 空间量化为整数值,从而可以进行更精确的重建。但请记住,H 现在在 0-360 范围内,S 和 V 在 0-1 范围内。

img = cv2.imread(image)

imgf = img.astype(np.float32) / 255
hsv = cv2.cvtColor(imgf, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)

h = np.mod(h + h_target - h_source, 360)
s = np.clip(s + s_taget - s_source, 0, 1)

hsv = cv2.merge([h, s, v])
imgf = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
bgr = (imgf * 255).astype(np.uint8)

[注意,我们不碰V。

评论

0赞 diegobc11 11/18/2023
我假设 h_target 和 s_target 代表我想要的红色,h_source和s_source到底是什么值?我相信它们对应于原始的黄色(在本例中),但是当我唯一的数据是下限和上限时,我将如何获得 h 和 s 的值?
0赞 Cris Luengo 11/18/2023
@diegobc11我假设您的用户会指定“将黄色转换为红色”,因此您知道这两种颜色的 HS 值。如果您所拥有的只是颜色的边界,则取边界的平均值,或取这些边界内图像的平均值。有很多方法可以解决这个问题。
0赞 diegobc11 11/18/2023
我们只有这张黄色图像作为输入,并希望将黄色部分更改为代码为 hsv(0, 90%, 90%)的红色,因此基本上我们只有第二种颜色的 H 和 S 值
0赞 Cris Luengo 11/18/2023
@diegobc11 尝试使用 .或者只是设置为一些典型的黄色。h_source = np.mean(h[yellow_mask])h_sources_source