Bootstrap

24/11/24 视觉笔记 滤镜

减淡和加深技术

减淡可以减少我们希望在图像A中变得比以前更亮的图像区域的曝光。在图像处理中,我们通常选择或指定使用蒙版更改的图像区域。蒙版B是与图像尺寸相同的数组,蒙版将应用于图像,可以将蒙版视为一张纸,这张纸上有若干个小孔,蒙版将覆盖在图像上。纸的“孔”以255表示,不透明区域则以 0 表示。

以下代码接受两个矩阵(image和mask)并返回混合图像

def dodge_native(image,mask):

    #确定输入图像形状
    width,height = image.shape[:2]
    
    #准备和图像一样大小的输出参数
    blend = np.zeros((width,height),np.       uint8)

    for c in range(width):
        
        for r in range(height):
        
            #按8位移位图像的像素值
            #除以蒙版的负片
            result = (image[c,r]<<8)/(255 - mask[c,r])

            #确保结果值在范围内
            blend[c,r] = min(255,result)
    return blend

像素级的除法还可以用cv2.divide函数实现。因此可以改进dodge函数

def dodge(image,mask):

    return cv2.divide(image,255 - mask,scale = 256)

应用铅笔素描变换效果

我们先将图像转化成灰度图,

rimg_gray = cv2.cvtColor(img_rgb,cv2.COLOR_RGB2GRAY)

输入图像无论是RGB还是BGR都没关系,最终都可以获得一幅很好的灰度图像

将图像反转并使用大小为(21,21)的高斯内核对其进行模糊处理

inv_gray = 255 - gray_image
blurd_img = cv2.GaussianBlur(inv_gray,(21,21),0,0)

高斯模糊的数学基础是高斯函数,它是一个关于实数的钟形曲线,其公式为:

使用dodge模式将原始灰度图像与模糊之后的负片混合在一起。

gray_sketch = cv2.divide(gray_image,255 - Fuzzy_image,scale = 256)

优化思路,将变换之后的图像与背景混合在一起,使其看起来像是在画布上绘制图像一样。因此,在返回结果之前,我们希望将其与canvas混合

def convert_to_pencil_sketch(rgb_image):
    gray_image = cv2.cvtColor(rgb_image,cv2.COLOR_RGB2GRAY)
    blurred_image = cv2.GaussianBlur(gray_image,(21,21),0,0)#使用高斯去模糊
    gray_sketch = cv2.divide(gray_image,blurred_image,scale = 256)# 将原始灰度图像除以模糊后的图像,得到素描效果,并将结果缩放到0-255的范围。

if canvas is not None:
    gray_sketch = cv2.multiply(gray_sketch,canvas,scale = 1/256)
    return cv2.cvtColor(gray_sketch,cv2.COLOR_GRAY2RGB)

这就是优化后的convert_to_pencil_sketch函数,它带有可选的canvas参数,可以为铅笔素描增加艺术感

生成冷调和暖调

为了操纵图像的感知色温,我们将实现曲线滤镜,这些滤镜在图像的不同区域之间控制颜色过渡,从而使我们可以巧妙地移动色谱,而不会给图像增加看起来不自然的整体色调

通过曲线平移使用颜色操作

{ (x0,y0),(x1,y1),....(xn,yn) }

每个锚点都是一对数字,分别代表输入和输出像素值。列入数字对(30,90)表示将输入像素值30增加到

如果想要使灰度图像稍微亮一些,则可以使用带有以下控制点集的曲线滤镜:

{(0,0) (128,192),(255,255)}

这意味着除0和255之外的所有输入像素值都会略微增加,从而对图像产生整体的增亮效果。

使用查找表实现曲线滤镜

def spline_to_lookup_table(spline_breaks:list,break_values:list):
    spl = UnivariateSpline(spline_breaks,break_values)
    return spl(range(256))

函数return参数是256个元素的列表,其中包含x的每个可能值的内插 f (x) 值。

UnivariateSpline 是 SciPy 库中的一个类,它用于创建一维数据的平滑样条。这个类通过最小二乘方法和罚项来控制平滑度,从而在拟合数据的准确度和模型的平滑度之间找到最佳平衡点。

现在我们需要做的是提出一组锚点(xi,yi),并且已经做好了准备,可以将滤镜应用于灰度输入图像

import cv2
import numpy as np

x = [0, 128, 255]
y = [0, 192, 255]
myLUT = spline_to_lookup_table(x,y)
img_curved = cv2.LUT(img_gray,myLUT).astype(np.uint8)


设计暖调和冷调效果

先定义两个通用曲线滤镜

一个可以按照趋势增加通道的所有像素值,另一个则是减少它们:

INCREASE_LOOKUP_TABLE = spline_to_lookup_table([0,64,128,192,256]
                                               [0,70,140,210,256])
DECREASE_LOOKUP_TABLE = spline_to_lookup_table([0,64,128,192,256]
                                               [0,30,80, 120,192])

cv2.LUT 是 OpenCV 中的一个函数,它用于执行查找表(LUT,Look-Up Table)转换。这个函数根据提供的查找表对输入图像的每个像素值进行转换

v2.LUT(src, lut[, dst]) -> dst,其中 src 是输入的源图像,lut 是查找表,dst 是输出图像,如果未指定,则会创建一个新的图像

对于 src 中的每个元素(像素点),cv2.LUT 会通过查找 lut 表来找到对应的值,并填充到输出数组 dst 中对应的位置。具体来说,如果 src 的深度是 CV_8U(8位无符号整数),则 d=0;如果 src 的深度是 CV_8S(8位有符号整数),则 d=128。公式为:dst(I) = lut(src(I) + d)

如何将LUT查找表应用于RGB图像

首先将图片分解为不同的通道

c_r,c_g,c_b = cv2.split(rgb_image)

然后将滤镜应用于每个通道

if green_filter is not None:
    c_g = cv2.LUT(c_g,green_filter).astype(np.uint8)

对RGB图像中的3个通道都执行次操作,可获得以下辅助函数

def apply_rgb_filters(rgb_image,*,red_filter=None,green_filter=None,blue_filter = None):
    
    c_r,c_g,c_b = cv2.split(rgb_image)
    if red_filter is not None:
        c_r = cv2.LUT(c_r,red_filter).astype(np.uint8)
    if green_filter is not None:
        c_g = cv2.LUT(c_g,green_filter).astype(np.uint8)
    if blue_filter is not None:
        c_b = cv2.LUT(c_b,blue_filter).astype(np.uint8)
    return cv2.merge((c_r,c_g,c_b))

要使图像看起来是在炎热的晴天拍摄的,最简单的方法是增加图像中的红色,并通过增加色彩饱和度使颜色显得更加鲜艳。这可以分以下两个步骤实现。

(1)分别使用INCREASE_LOOKUP_TABLE和DECREASE_LOOKUP_TABLE增加R通道,并减少RGB彩色的B通道中的像素值

interim_img = apply_rgb_filters(rgb_image,
red_filter = INCREASE_LOOKUP_TABLE,
blue_filter = DECREASE_LOOKUP_TABEL)

(2)将图像转换为 HSV 色彩空间——H表示色相,S表示饱和度,V表示值,并使用INCREASE_LOOKUP_TABLE增加S通道的值,这可以通过apply_hue_filters函数来实现,该函数需要采用一幅RGB彩色图像和一个查找表作为输入的参数

def apply_hue_filter(rgb_image,hue_filter):
    c_h,c_s,c_v = cv2.split(cv2.cvtColor(rgb_image,cv2.COLOR_RGB2HSV))
    c_s = cv2.LUT(c_s,hue_filter).astype(np.uint8)
    return cv2.cvtColor(cv2.merge((c_h,c_s,c_v)),cv2.COLOR_HSV2RGB)

类似的也可以定义一个冷调滤镜,减少RGB图像的R通道中的像素值,增加RGB图像的B通道的像素值,然后再将图像转化为HSV色彩空间,并通过S通道降低色彩饱和度。

def _render_cool(rgb_image:np.ndarray)->np.ndarray:
    interim_img  = apply_rgb_filters(rgb_image,
                                     red_filter = DECREASE_LOOKUP_TABLE,
                                     blue_filter = INCREASE_LOOKUP_TABLE)
    return apply_hue_filter(interim_img,DECREASE_LOOKUP_TABLE)

;