如何在Python OpenCV中为灰度图像着色?

2w2cym1i  于 8个月前  发布在  Python
关注(0)|答案(1)|浏览(126)

所以我有一个灰度图像,它可能有一个alpha通道(透明度),它包含强度信息,但没有颜色信息。
现在我想给它着色,这意味着我有一个单一的RGB颜色,任何RGB颜色,我想合并的颜色和图像,以产生一个新的单调的图像。没有灰度图像,结果将是一个与灰度图像相同大小的图像充满给定的颜色。灰度图像使某些区域变亮或变暗,使新的图像具有可辨别的特征。
我试过了,但结果是错的:

import cv2
import numpy as np

def colorize(img, rgb):
    grey = img[..., 0]
    channels = [grey * (channel / 256) for channel in rgb[::-1]]
    
    if img.shape[2] == 4:
        channels.append(img[..., 3])
    
    return np.dstack(channels).astype(np.uint8)

原始图像:

我的原始图像是透明的,我用cv2.IMREAD_UNCHANGED标志加载它。
我想用(128,192,255)来着色它,GIMP 2的预期结果是:

但我得到了这个,这张照片太暗了:

我现在意识到,要获得与GIMP 2中相同的效果,我需要创建一个所需颜色的泛色填充图像,并将其与源图像混合,并使用“硬光”作为混合模式。
但从语义上讲,我原本想实现的是用HSV颜色模型中新颜色的HS替换原始图像每个像素的色调分量和饱和度分量,并使用原始图像的亮度作为新图像的亮度来添加阴影。
我刚才描述的是HSL颜色混合模式。
谷歌搜索任何超过几个字提供任何相关的,但搜索关键字"blend mode"给我Blend modes WikipediaBlend modes CSS specification,我也读过GIMP 2 docs
这里没有列出Python实现,但这不会阻止我。我非常聪明,公式看起来并不复杂,我可以在几个小时内实现所有38个公式,我会花一天的时间来实现它们。我也在学习C++,所以我也会尝试用C++实现它们。
然而,我不认为我的自定义实现将是最有效的,可能有库方法,至少完成了部分工作,使用它们将提高效率,但我不知道这些方法。
cv2库提供了635个函数,如果我逐一检查所有函数,将花费大量时间。
我发现他们使用以下内容:

import cv2
from pathlib import Path
from typing import Callable

cv2_functions = [f'cv2.{k}' for k, v in cv2.__dict__.items() if isinstance(v, Callable)]
Path('D:/cv2_functions.txt').write_text('\n'.join(cv2_functions))

我没有使用inspect,因为:

In [74]: import inspect

In [75]: inspect.isfunction(cv2.multiply)
Out[75]: False

In [76]: inspect.ismethod(cv2.multiply)
Out[76]: False

In [77]: inspect.iscode(cv2.multiply)
Out[77]: False

现有的答案实现了乘法混合模式,与我的原始代码具有相同的效果,但未能给予期望的结果。
实现强光混合模式和HSL颜色混合模式需要新的答案,所以我可以知道如何更有效地实现混合模式。
我不是在这里寻求软件推荐。问题的范围仅限于numpycv2,我在numpy中实现混合模式,但cv2模块提供了423个方法,其中一些方法可能至少已经完成了这里的部分工作,因此使用这些方法可以使代码更高效,但我不知道该用什么方法
因此,我想要使用cv2提供的方法实现混合模式的示例。
至于我的目的,我目前正在用Python和PyQt 6编写一个带有AI和GUI的Tic-Tac-Toe游戏。
我已经完成了人工智能部分,但我不熟悉GUI编程,我正在努力创建GUI。
我希望用户能够完全自定义GUI来更改所有内容的颜色和样式,因此我需要找到在运行中对图片进行着色的方法。
当程序完成后,我会在代码审查上发布程序,然后我会根据反馈尝试用C++重新实现程序。
我已经实现了大部分的混合模式,我正在实现其余的混合模式。这里是它们的选择,我不会发布所有的混合模式,这样问题就不会被代码弄乱,我会为它们发布一个单独的问题,因为我不知道如何使用Alpha通道混合图像,公式中没有提到透明度:

import numpy as np
import cv2

def scale_down(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    return base / 255, top / 255

def scale_up(img: np.ndarray) -> np.ndarray:
    return (img * 255).astype(np.uint8)

def blend_overlay(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    mask = base >= 0.5
    result = np.zeros_like(base)
    result[~mask] = (mult_2 := (2 * base * top))[~mask]
    result[mask] = (2 * base + 2 * top - mult_2 - 1)[mask]
    return result

def blend_hardlight(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    return blend_overlay(top, base)

def blend_multiply(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    return base * top

def blend_screen(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    return base + top - base * top

def blend_softlight(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    return (1 - 2 * top) * base**2 + 2 * base * top

def blend_colorburn(base: np.ndarray, top: np.ndarray) -> np.ndarray:
    result = np.zeros_like(base)
    result[ones := base == 1.0] = 1
    result[zeros := top == 0.0] = 0
    mask = (~ones == ~zeros) == (ones == 0)
    result[mask] = 1 - np.minimum(1, (1 - base[mask]) / top[mask])
    return result
0g0grzrc

0g0grzrc1#

这在Python/OpenCV中很好用。

  • 将输入读取为未更改
  • 创建一个蓝色的图像作为float(1,0,0)
  • 将BGR通道和Alpha通道与输入分离
  • 将BGR图像转换为float
  • 将BGR和alpha图像相乘并转换回uint 8
  • 将alpha通道放回相乘后的图像中
  • 保存结果
  • 显示结果

请注意,显示的结果不会显示透明度。如果您只是查看而不是保存它,这可能是您的问题。
输入:

import cv2
import numpy as np

# read the image unchanged
img = cv2.imread('gray_circle.png', cv2.IMREAD_UNCHANGED)
hh, ww = img.shape[:2]

# define color image as blue
color_img = np.full((hh,ww,3), (1,0,0), dtype=np.float32)

# separate bgr channels and alpha channel from img
bgr = img[:,:,0:3]
alpha = img[:,:,3]

# multiply bgr with color_img
bgr = bgr.astype(np.float32)
mult = cv2.multiply(bgr, color_img)
mult = mult.astype(np.uint8)

# put alpha channel back
result = cv2.cvtColor(mult, cv2.COLOR_BGR2BGRA)
result[:,:,3] = alpha

# save result
cv2.imwrite('gray_circle_blued.png', result)

# show result
cv2.imshow('result', result)
cv2.waitKey(0)

测试结果:

相关问题