B02 - 图像处理 - 颜色空间

总结图片的基本可调特征,如色调、饱和度、亮度,同时讲到颜色空间以及颜色空间的应用

什么是图像色相 / 色调 (Hue)?

  • 色相(hue)指的是色彩的外相,是在不同波长的光照射下,人眼所感觉不同的颜色,有红色、橙色、黄色、绿色、蓝色、紫色、粉红色、黑色、褐色、灰色、白色、金色和银色

什么是图像饱和度 (Saturation)?

  • 指的是色彩的纯度,也叫饱和度彩度,是 “色彩三属性” 之一。如大红就比玫红更红,这就是说大红的色度要高
  • 饱和度 S 表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为 0,饱和度达到最高。通常取值范围为 0%~100%,值越大,颜色越饱和

什么是图像明度 (Value/Brightness)?

  • 明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为 0%(黑)到 100%(白)

什么是图像亮度 (Luminance/Lightness)?

  • 亮度(luminance)又称辉度,是表示人眼对发光体或被照射物体表面的发光或反射光强度实际感受的物理量;可理解为:单位面积内看上去有多亮

%% 颜色空间 %%

什么是颜色空间?

  • 色彩空间:指通过多个(通常为 3 个或 4 个)颜色分量构成坐标系来表示各种颜色的模型系统。色彩空间中的每个像素点均代表一种颜色,各像素点的颜色是多个颜色分量的合成或描述
  • RGB 色彩空间:最常用的是 24 - 位实现方法,也就是红绿蓝每个通道有 8 位或者 256 色级。基于这样的 24 - 位 RGB 模型的色彩空间可以表现 256³ 亦即 16,777,216 种不同颜色
  • 常见的色彩空间包括:GRAY 色彩空间(灰度图像)、XYZ 色彩空间、YCrCb 色彩空间、HSV 色彩空间、HLS 色彩空间、CIELab 色彩空间、CIELuv 色彩空间、Bayer 色彩空间等。计算机显示器采用 RGB 色彩空间,数字艺术创作经常采用 HSV/HSB 色彩空间,机器视觉和图像处理系统大量使用 HSl、HSL 色彩空间
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    imgBGR = cv.imread("../images/imgLena.tif", flags=1)  # 读取为BGR彩色图像
    imgRGB = cv.cvtColor(imgBGR, cv.COLOR_BGR2RGB) # BGR 转换为 RGB, 用于 PyQt5, matplotlib
    imgGRAY = cv.cvtColor(imgBGR, cv.COLOR_BGR2GRAY) # BGR 转换为灰度图像
    imgHSV = cv.cvtColor(imgBGR, cv.COLOR_BGR2HSV) # BGR 转换为 HSV 图像
    imgYCrCb = cv.cvtColor(imgBGR, cv.COLOR_BGR2YCrCb) # BGR转YCrCb
    imgHLS = cv.cvtColor(imgBGR, cv.COLOR_BGR2HLS) # BGR 转 HLS 图像
    imgXYZ = cv.cvtColor(imgBGR, cv.COLOR_BGR2XYZ) # BGR 转 XYZ 图像
    imgLAB = cv.cvtColor(imgBGR, cv.COLOR_BGR2LAB) # BGR 转 LAB 图像
    imgYUV = cv.cvtColor(imgBGR, cv.COLOR_BGR2YUV) # BGR 转 YUV 图像
    show_images([imgBGR,imgRGB,imgGRAY,imgHSV,imgYCrCb,imgHLS,imgXYZ,imgLAB,imgYUV])

什么是 RGB 颜色空间?

  • RGB 采用加法混色法,因为它是描述各种 “光” 通过何种比例来产生颜色。光线从暗黑开始不断叠加产生颜色。RGB 描述的是红绿蓝三色光的数值。RGBA 是在 RGB 上增加阿尔法通道实现透明效果
  • 根据不同的数据类型,RGB 颜色空间有不同的范围,如 CV_8U 表示时,RGB 各个通道的数据范围为 0-255;如果 CV_16U 表示时,RGB 各个通道的数据范围为 0-65535;如果 CV_32F 表示时,RGB 各个通道的数据范围为 0-1;
  • Opencv 使用 BGR 的顺序进行存储,详细查看 OpenCV 中 Mat 数据内存组织方式
  • rgb 颜色空间存在颜色值分布不均匀以及色度和亮度混合在一起的问题: 在室内光照条件下第二张图像中的魔方蓝色和白色部分看起来相似,但第一张图像有明显差异

什么是 Lab 颜色空间?

  • lab 也有三个图像通道,分别是 (1) l:亮度通道,表亮度;(2) a:颜色通道 a,表示从绿色到洋红色的颜色;(3) b:颜色通道 b,表示从蓝色到黄色的颜色
  • lab 颜色空间与 rgb 颜色空间完全不同。在 rgb 颜色空间中,颜色信息被分成三个通道,但是相同的三个通道也包含亮度信息。另一方面,在 lab 颜色空间中,l 通道独立于颜色信息并仅只含亮度信息。另外两个通道编码颜色
  • 光照的变化主要影响 l 分量。1. 包含颜色信息的 a 和 b 分量,在光照的变化下没有经历大的变化。1. 绿色,橙色和红色(它们是 a 通道的主要颜色)的相应值在 b 通道中没有变化,同样地,蓝色和黄色(它们是 b 通道的主要颜色)在 a 通道中没有变化

什么是 HSV 颜色空间?

  • 是艺术家们常用的,因为与加法减法混色的术语相比,使用 h 色调;(2) s 饱和度;(3) v 明度 等概念描述色彩更自然直观。HSV 是 RGB 色彩空间的一种变形
  • hsv 最大的特点是它只使用一个通道来描述颜色(h),这使得指定颜色变得非常直观
  • h 分量在两个图像中非常相似,这表明即使在光照变化下颜色信息也是完整的; 两个图像中的 s 分量也非常相似; v 分量表示亮度,因此它会因照明变化而发生变化

什么是 HSL 颜色空间?

  • 也称 HLS 或 HSI(I 指 Intensity)与 HSV 颜色空间非常相似,仅用亮度(Lightness)替代了明度(Brightness)。二者区别在于,一种纯色的明度等于白色的明度,而纯色的亮度等于中度灰的亮度

什么是 YCrCb 颜色空间?

  • ycrcb 颜色空间源自 rgb 颜色空间,并具有以下三个成分 (1) 通道 y: 伽马校正后从 rgb 获得的亮度或亮度分量;(2) 通道 cr: cr=r-y(红色成分与亮度成分 y 的距离);(3) 通道 cb: cb=b-y(蓝色成分与亮度成分 y 的距离)
  • 对于照度变化,可以针对强度和颜色分量对 lab 进行类似的观察。与 lab 相比,室外图像中红色和橙色之间的感知差异较小。白色在所有 3 个组件中发生了变化

什么是 CMYK 颜色空间?

  • CMYK 印刷过程中使用减法混色法,因为它描述的是需要使用何种油墨,通过光的反射显示出颜色。它是在一种白色介质(画板,页面等)上使用油墨来体现图像。CMYK 描述的是青、品红、黄和黑四种油墨的数值

RGB⇔GRAY 的原理?

  • RGB[A]⇒GARY:$$\text{RGB[A] to Gray:} \quad Y \leftarrow 0.299 \cdot R + 0.587 \cdot G + 0.114 \cdot B$$
  • GARY⇒RGB[A]:$$\text{Gray to RGB[A]:} \quad R \leftarrow Y, G \leftarrow Y, B \leftarrow Y, A \leftarrow \max (ChannelRange)$$
  • opencv 使用 cv: : cvtColor 进行进行快速转换
    1
    2
    cv::cvtColor(src, bwsrc, cv::COLOR_RGB2GRAY);
    cv::cvtColor(src, bwsrc, cv::COLOR_GRAY2RGB);

RGB⇔HSV 的原理?

  • RGB⇒HS

Vmax(R,G,B)S{Vmin(R,G,B)V if V00 otherwise H{60(GB)/(Vmin(R,G,B)) if V=R120+60(BR)/(Vmin(R,G,B)) if V=G240+60(RG)/(Vmin(R,G,B)) if V=B0 if R=G=BifH<0,HH+360\begin{array}{c} V \leftarrow \max (R, G, B) \\ S \leftarrow\left\{\begin{array}{ll} \frac{V-\min (R, G, B)}{V} & \text { if } V \neq 0 \\ 0 & \text { otherwise } \end{array}\right. \\ H \leftarrow\left\{\begin{array}{ll} 60(G-B) /(V-\min (R, G, B)) & \text { if } V=R \\ 120+60(B-R) /(V-\min (R, G, B)) & \text { if } V=G \\ 240+60(R-G) /(V-\min (R, G, B)) & \text { if } V=B \\ 0 & \text { if } R=G=B \end{array}\right. \end{array} \\ if H<0,H \leftarrow H+360

RGB⇔HLS 的原理?

  • RGB⇒HL

Vmaxmax(R,G,B)Vminmin(R,G,B)LVmax+Vmin2S{VmaxVminVmaVminVmaxVmin2(VmaxVmid if L0.5H{60(GB)/(VmaxVmin) if Vmax=R120+60(BR)/(VmaxVmin) if Vmax=G240+60(RG)/(VmaxVmin) if Vmax=B0 if R=G=BifH<0,HH+360\begin{array}{c} V_{\max } \leftarrow \max (R, G, B) \\ V_{\min } \leftarrow \min (R, G, B) \\ L \leftarrow \frac{V_{\max }+V_{\min }}{2} \\ S \leftarrow\left\{\begin{array}{ll} \frac{V_{\max } V_{\min }}{V_{\operatorname{ma}} V_{\min }} \\ \frac{V_{\max } V_{\min }}{2-\left(V_{\max } V_{\operatorname{mid}}\right.} & \text { if } L \geq 0.5 \end{array}\right. \\ H \leftarrow\left\{\begin{array}{ll} 60(G-B) /\left(V_{\max }-V_{\min }\right) & \text { if } V_{\max }=R \\ 120+60(B-R) /\left(V_{\max }-V_{\min }\right) & \text { if } V_{\max }=G \\ 240+60(R-G) /\left(V_{\max }-V_{\min }\right) & \text { if } V_{\max }=B \\ 0 & \text { if } R=G=B \end{array}\right. \end{array} \\ if H<0,H \leftarrow H+360

RGB⇔YCrCb 的原理?

  • RGB⇒YCrC

Y0.299R+0.587G+0.114BCr(RY)0.713+deltaCb(BY)0.564+deltaY \leftarrow 0.299 \cdot R + 0.587 \cdot G + 0.114 \cdot B\\ Cr \leftarrow (R-Y) \cdot 0.713 + delta\\ Cb \leftarrow (B-Y) \cdot 0.564 + delta

  • YCrCb⇒RG

RY+1.403(Crdelta)GY0.714(Crdelta)0.344(Cbdelta)BY+1.773(Cbdelta)R \leftarrow Y + 1.403 \cdot (Cr - delta) \\ G \leftarrow Y - 0.714 \cdot (Cr - delta) - 0.344 \cdot (Cb - delta) \\ B \leftarrow Y + 1.773 \cdot (Cb - delta)

 delta ={128 for 8-bit images 32768 for 16-bit images 0.5 for floating-point images \text { delta }=\left\{\begin{array}{ll} 128 & \text { for 8-bit images } \\ 32768 & \text { for 16-bit images } \\ 0.5 & \text { for floating-point images } \end{array}\right.

RGB 与 BGR 的相互转换?

  • Python: 使用 numpy 进行转换
    1
    2
    3
    4
    5
    destRGB = cv2.cvtColor(srcBGR, cv2.COLOR_BGR2RGB)
    rgb = bgr[...,::-1]
    bgr = rgb[...,::-1]
    gbr = rgb[...,[2,0,1]]
    # 注意:ndarray..transpose((2, 0, 1)))常用于调整维度顺序,不是调整通道顺序
  • C++:使用 cv: : cvtColor 或遍历转换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    cv::cvtColor(cv_img, cv_img, cv::COLOR_RGB2BGR);
    // 遍历转换
    Mat BGRToRGB(Mat img)
    {
    Mat image(img.rows, img.cols, CV_8UC3);
    for (int i = 0; i < img.rows; ++i)
    {
    //获取第i行首像素指针
    Vec3b *p1 = img.ptr(i);
    Vec3b *p2 = image.ptr(i);
    }

OpneCV 使用颜色查找表 LUT ?

  • 函数 cv. LUT () 用来实现对图像中像素值的快速转换,称为查表函数(Look up table),使用 LUT 查找表的速度远远快于对图像中逐个像素的映射变换
  • 函数 cv. LUT () 本质上是查表替换,因此不仅可以用于灰度图像,也可以用于 RGB 彩色图像,还可以用于 HSV、YCrCb、LAB 等色彩空间的图像
  • 查找表只能用于不涉及位置相关、邻域相关的操作,如:替换、取反、赋值、阈值、二值化、灰度变换、颜色缩减和直方图均衡化;不能用于位置相关、邻域相关的操作,如旋转、模糊
  • 颜色空间缩减是将图像的像素值除以某个参数,以得到较少的颜色种类,这是 LUT 的典型应用。例如,8 位灰度图像对应着 255 个灰度级,在某些印刷条件下可以将其缩减到 8 个灰度级
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    img = cv.imread("../images/imgLena.tif")  # 读取彩色图像(BGR)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # BGR -> Gray
    h, w = img.shape[:2] # 图片的高度, 宽度
    timeBegin = cv.getTickCount()
    imgGray32 = np.empty((w,h), np.uint8) # 创建空白数组
    for i in range(h):
    for j in range(w):
    imgGray32[i][j] = (gray[i][j]//8) * 8
    timeEnd = cv.getTickCount()
    time = (timeEnd-timeBegin)/cv.getTickFrequency()
    print("灰度级缩减(for 循环实现): {} s".format(round(time, 4))) # 0.8667 s
    timeBegin = cv.getTickCount()
    table32 = np.array([(i//8)*8 for i in range(256)]).astype("uint8") # 32 levels
    gray32 = cv.LUT(gray, table32)
    timeEnd = cv.getTickCount()
    time = (timeEnd-timeBegin)/cv.getTickFrequency()
    print("灰度级缩减(LUT 查表实现): {} s".format(round(time, 4))) # 0.0002 s
    table8 = np.array([(i//32)*32 for i in range(256)]).astype("uint8") # 8 levels
    gray8 = cv.LUT(gray, table8)
    show_images([gray,gray32,gray8])

OpenCV 使用 applyColorMap 生成伪彩图?

  • 伪彩色图像是指对单色图像进行处理,结果转换得到颜色分量,构造为彩色效果的图像。伪彩色图像在形式和视觉表现为彩色图像,但其所呈现的颜色并非图像的真实色彩重现,仅仅是各颜色分量的像素值合成的结果
  • 伪彩色图像增强是指按照特定的准则对灰度图像进行处理,将不同的灰度级按照某种映射关系变换为不同的颜色分量。例如,天气预报中的气象云图,红外测温图像,一般都是伪彩色图像。人眼的视觉特性,只能分辨 20 级左右的灰度,但可以分辨几千种色调和亮度。通过伪彩色图像增强,可以让灰度图像看起来更清楚,更容易分辨
  • 函数 cv.applyColorMap () 根据色彩映射表,将灰度图像变换为伪彩色图像
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    gray = cv.imread("../images/Fig0525a.tif", flags=0)  # 读取灰度图像
    h, w = gray.shape[:2] # 图片的高度, 宽度
    # 伪彩色处理
    pseudo1 = cv.applyColorMap(gray, colormap=cv.COLORMAP_HOT)
    pseudo2 = cv.applyColorMap(gray, colormap=cv.COLORMAP_PINK)
    pseudo3 = cv.applyColorMap(gray, colormap=cv.COLORMAP_RAINBOW)
    pseudo4 = cv.applyColorMap(gray, colormap=cv.COLORMAP_HSV)
    pseudo5 = cv.applyColorMap(gray, colormap=cv.COLORMAP_TURBO)
    show_images([gray,pseudo1,pseudo2])
    show_images([pseudo3,pseudo4,pseudo5])

OpenCV 的色彩风格?

  • res = cv. bitwise_and (frame, frame, mask= mask),使用 cv: : applyColorMap 进行颜色空间的映射

OpenCV 使用 LUT 调整色彩平衡?

  • 调节色彩平衡,可以通过对不同颜色分量分别进行对比度拉伸来实现
  • 简单地,设置各通道的最大值 maxG(<=255),将某颜色通道的色阶从 0-255 映射到 0-maxG,就可以使该颜色通道的色彩衰减
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    img = cv.imread("../images/imgGaia.tif", flags=1)  # 读取彩色
    maxG = 128 # 修改颜色通道最大值,0<=maxG<=255
    lutHalf = np.array([int(i * maxG/255) for i in range(256)]).astype("uint8")
    lutEqual = np.array([i for i in range(256)]).astype("uint8")
    lut3HalfB = np.dstack((lutHalf, lutEqual, lutEqual)) # (1,256,3), B_half/BGR
    lut3HalfG = np.dstack((lutEqual, lutHalf, lutEqual)) # (1,256,3), G_half/BGR
    lut3HalfR = np.dstack((lutEqual, lutEqual, lutHalf)) # (1,256,3), R_half/BGR
    blendHalfB = cv.LUT(img, lut3HalfB) # B 通道衰减 50%
    blendHalfG = cv.LUT(img, lut3HalfG) # G 通道衰减 50%
    blendHalfR = cv.LUT(img, lut3HalfR) # R 通道衰减 50%
    show_images([blendHalfB,blendHalfG,blendHalfR])

OpenCV 使用 LUT 调整饱和度、明度?

  • HSV 模型是针对用户观感的一种颜色模型,可以直观的表达色彩的色调明暗、及鲜艳程度,HSV 的含义分别为:色调(Hue)、饱和度(Saturation)和明度(Value)
  • 将图像从 RGB 色彩空间转换到 HSV 色彩空间后,用 LUT 对特定通道进行对比度拉伸,就可以调节图像的饱和度、明度
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    img = cv.imread("../images/imgGaia.tif", flags=1)  # 读取彩色
    hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) # 色彩空间转换, BGR->HSV
    # 调节通道强度
    lutWeaken = np.array([int(0.6*i) for i in range(256)]).astype("uint8")
    lutEqual = np.array([i for i in range(256)]).astype("uint8")
    lutRaisen = np.array([int(102+0.6*i) for i in range(256)]).astype("uint8")
    # 调节饱和度
    lutSWeaken = np.dstack((lutEqual, lutWeaken, lutEqual)) # Saturation weaken
    lutSRaisen = np.dstack((lutEqual, lutRaisen, lutEqual)) # Saturation raisen
    # 调节明度
    lutVWeaken = np.dstack((lutEqual, lutEqual, lutWeaken)) # Value weaken
    lutVRaisen = np.dstack((lutEqual, lutEqual, lutRaisen)) # Value raisen
    blendSWeaken = cv.LUT(hsv, lutSWeaken) # 饱和度降低
    blendSRaisen = cv.LUT(hsv, lutSRaisen) # 饱和度增大
    blendVWeaken = cv.LUT(hsv, lutVWeaken) # 明度降低
    blendVRaisen = cv.LUT(hsv, lutVRaisen) # 明度升高
    show_images([blendSWeaken,img,blendSRaisen])
    show_images([blendVWeaken,img,blendVRaisen])

OpenCV 实现 Photoshop 色阶调整算法?

  • Photoshop 的色阶调整分为输入色阶调整和输出色阶调整
  • 输入色阶调整算法:先根据黑场阈值和白场阈值对 RGB 颜色通道的动态范围进行线性拉伸,再根据灰场调节值进行幂律变换(伽马变换),对发白(曝光过度)或过暗(曝光不足)的图片进行矫正; 输出色阶调整方法:基于动态范围进行线性拉伸
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def levelAdjust(img, Sin=0, Hin=255, Mt=1.0, Sout=0, Hout=255):
    Sin = min(max(Sin, 0), Hin-2) # Sin, 黑场阈值, 0<=Sin<Hin
    Hin = min(Hin, 255) # Hin, 白场阈值, Sin<Hin<=255
    Mt = min(max(Mt, 0.01), 9.99) # Mt, 灰场调节值, 0.01~9.99
    Sout = min(max(Sout, 0), Hout-2) # Sout, 输出黑场阈值, 0<=Sout<Hout
    Hout = min(Hout, 255) # Hout, 输出白场阈值, Sout<Hout<=255
    difIn = Hin - Sin
    difOut = Hout - Sout
    table = np.zeros(256, np.uint16)
    for i in range(256):
    V1 = min(max(255 * (i-Sin)/difIn,0), 255) # 输入动态线性拉伸
    V2 = 255 * np.power(V1/255, 1/Mt) # 灰场伽马调节
    table[i] = min(max(Sout+difOut*V2/255, 0), 255) # 输出线性拉伸
    imgTone = cv.LUT(img, table)
    return imgTone
    img = cv.imread("../images/buddha01.png", flags=1) # 读取彩色
    equ1 = levelAdjust(img, 10, 225, 1.0, 10, 245)
    equ2 = levelAdjust(img, 10, 225, 1.2, 10, 245)
    show_images([img,equ1,equ2])

OpenCV 实现 Photoshop 色阶自动调整算法?

  • photoshop 还提供了自动色阶(Auto Levels)功能,根据图像的曝光程度、明暗程度自动调节色彩平衡以达到最佳状态。自动色阶和自动对比度功能算法简单,对于一些图像的处理效果非常显著,具有很强的实用性
  • 自动色阶调整的实现方法是,系统基于灰度直方图统计结果,自动设置色阶调整所需的参数。输入色阶调整算法:先根据黑场阈值和白场阈值对 RGB 颜色通道的动态范围进行线性拉伸,再根据灰场调节值进行幂律变换(伽马变换),对发白(曝光过度)或过暗(曝光不足)的图片进行矫正;输出色阶调整方法:基于动态范围进行线性拉伸
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    def autoLevels(img, cutoff=0.1):
    channels = img.shape[2] # h,w,ch
    table = np.zeros((1,256,3), np.uint8)
    for ch in range(channels):
    # cutoff=0.1, 计算 0.1%, 99.9% 分位的灰度值
    low = np.percentile(img[:,:,ch], q=cutoff) # ch 通道, cutoff=0.1, 0.1 分位的灰度值
    high = np.percentile(img[:,:,ch], q=100 - cutoff) # 99.9 分位的灰度值, [0, high] 占比99.9%
    # 输入动态线性拉伸
    Sin = min(max(low, 0), high - 2) # Sin, 黑场阈值, 0<=Sin<Hin
    Hin = min(high, 255) # Hin, 白场阈值, Sin<Hin<=255
    difIn = Hin - Sin
    V1 = np.array([(min(max(255*(i-Sin)/difIn, 0), 255)) for i in range(256)])
    # 灰场伽马调节
    gradMed = np.median(img[:,:,ch]) # 拉伸前的中值
    Mt = V1[int(gradMed)] / 128. # 拉伸后的映射值
    V2 = 255 * np.power(V1/255, 1/Mt) # 伽马调节
    # 输出线性拉伸
    Sout, Hout = 5, 250 # Sout 输出黑场阈值, Hout 输出白场阈值
    difOut = Hout - Sout
    table[0, :, ch] = np.array([(min(max(Sout + difOut*V2[i]/255, 0), 255)) for i in range(256)])
    return cv.LUT(img, table)
    # Photoshop 自动色阶调整算法
    img = cv.imread("../images/Fig0310b.tif", flags=1) # 读取彩色图像
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 转换为灰度图像
    print("cutoff={}, minG={}, maxG={}".format(0.0, gray.min(), gray.min()))
    # 色阶手动调整
    equManual = levelsAdjust(img, 63, 205, 0.8, 10, 245) # 手动调节
    # 色阶自动调整
    cutoff = 0.1 # 截断比例, 建议范围 [0.0,1.0]
    equAuto = autoLevels(img, cutoff)
    show_images([img,equManual,equAuto])

OpenCV 实现 Photoshop 对比度自动调整算法?

  • 对比度是指图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,差异范围越大代表对比越大。当对比率达到 120:1 就可以容易地显示生动、丰富的色彩,对比率高达 300:1 时就可支持各阶的颜色
  • 对比度对视觉效果的影响非常关键。高对比度对于图像的清晰度、细节表现、灰度层次表现都有很大帮助。对比度调整的目的通常是增强对比度,形成清晰的图像效果和醒目的视觉冲击力
  • Photoshop 中的自动对比度调整算法,与自动色阶调整算法基本相同,区别在于对比度自动调整不是对三个通道分别调整,而是对各通道按统一的比例进行调整。首先获取图像的亮度信息,然后根据修剪比例对亮度进行动态范围的拉伸,同比例调整 R, G, B 三个通道
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    def autoLevels(img, cutoff=0.1):  # 自动色阶调整
    channels = img.shape[2] # h,w,ch
    table = np.zeros((1,256,3), np.uint8)
    for ch in range(channels):
    # cutoff=0.1, 计算 0.1%, 99.9% 分位的灰度值
    low = np.percentile(img[:,:,ch], q=cutoff) # ch 通道, cutoff=0.1, 0.1 分位的灰度值
    high = np.percentile(img[:,:,ch], q=100 - cutoff) # 99.9 分位的灰度值, [0, high] 占比99.9%
    # 输入动态线性拉伸
    Sin = min(max(low, 0), high - 2) # Sin, 黑场阈值, 0<=Sin<Hin
    Hin = min(high, 255) # Hin, 白场阈值, Sin<Hin<=255
    difIn = Hin - Sin
    V1 = np.array([(min(max(255*(i-Sin)/difIn, 0), 255)) for i in range(256)])
    # 灰场伽马调节
    gradMed = np.median(img[:,:,ch]) # 拉伸前的中值
    Mt = V1[int(gradMed)] / 128. # 拉伸后的映射值
    V2 = 255 * np.power(V1/255, 1/Mt) # 伽马调节
    # 输出线性拉伸
    Sout, Hout = 5, 250 # Sout 输出黑场阈值, Hout 输出白场阈值
    difOut = Hout - Sout
    table[0, :, ch] = np.array([(min(max(Sout + difOut*V2[i]/255, 0), 255)) for i in range(256)])
    return cv.LUT(img, table)
    def autoContrast(img, cutoff): # 自动对比度调整
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 转换为灰度图像
    # 计算 0.1%, 99.9% 分位的灰度值
    Sin = np.percentile(gray, q=cutoff) # cutoff=0.1, 0.1 分位的灰度值
    Hin = np.percentile(gray, q=100 - cutoff) # 99.9 分位的灰度值, [0, per999] 占比99.9%
    # 输入动态线性拉伸
    difIn = Hin - Sin
    V1 = np.array([(min(max(255 * (i-Sin)/difIn,0), 255)) for i in range(256)])
    # 灰场伽马调节, Mt: 0.01~9.99
    gradMed = np.median(gray) # 拉伸前的中值
    Mt = V1[int(gradMed)] / 160. # 拉伸后的映射值
    V2 = 255 * np.power(V1/255, 1/Mt) # 伽马调节
    # 输出线性拉伸
    Sout, Hout = 5, 250 # Sout 输出黑场阈值, Hout 输出白场阈值
    difOut = Hout - Sout
    table = np.array([(min(max(Sout + difOut*V2[i]/255, 0), 255)) for i in range(256)]).astype("uint8")
    imgTone = cv.LUT(img, table)
    return imgTone
    # Photoshop 自动对比度调整算法
    img = cv.imread("../images/Fig0310b.tif", flags=1) # 读取彩色图像
    # img = cv.imread("../images/demist02.png", flags=1) # 读取彩色图像
    # 色阶自动调整
    cutoff = 0.1 # 截断比例, 建议范围 [0.0,1.0]
    levelsAuto = autoLevels(img, cutoff)
    # 对比度自动调整
    cutoff = 0.1 # 截断比例, 建议范围 [0.0,1.0]
    contrastAuto = autoContrast(img, cutoff)
    show_images([img,levelsAuto,contrastAuto])

OpenCV 在 HSV 颜色空间查找目标?

  • HSV 颜色空间的各通道分别表示色调(Hue)、饱和度(Saturation)和明度(Value),可以直观地表达色彩的明暗、色调及鲜艳程度
  • HSV 颜色空间可以用一个圆锥空间模型来描述。圆锥的顶点处 V=0,H 和 S 无定义,代表黑色;圆锥的顶面中心处 V=max,S=0,H 无定义,代表白色。
  • HSV 模型在对指定颜色分割时非常有效。用 H 和 S 分量表示颜色距离,颜色距离指代表两种颜色之间的数值差异。对于不同的彩色区域,混合 H 与 S 变量,划定阈值,就可以进行简单的分割
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    img = cv.imread("../images/lady983Green.png", flags=1)  # 读取彩色图像
    hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) # 将图片转换到 HSV 色彩空间
    # 使用 cv.inrange 函数在 HSV 空间检查设定的颜色区域范围,转换为二值图像,生成遮罩
    lowerColor = np.array([35, 43, 46]) # (下限: 绿色33/43/46)
    upperColor = np.array([77, 255, 255]) # (上限: 绿色77/255/255)
    binary = cv.inRange(hsv, lowerColor, upperColor) # 生成二值遮罩,指定背景颜色区域白色
    binaryInv = cv.bitwise_not(binary) # 生成逆遮罩,前景区域白色开窗,背景区域黑色
    matting = cv.bitwise_and(img, img, mask=binaryInv) # 生成抠图图像 (前景保留,背景黑色)
    # 将背景颜色更换为红色: 修改逆遮罩 (抠图以外区域黑色)
    imgReplace = img.copy()
    imgReplace[binaryInv==0] = [0,0,255] # 黑色背景区域(0/0/0) 修改为红色 (BGR:0/0/255)
    show_images([img,binary,binaryInv,matting])

参考:

* 【OpenCV 例程 300 篇】201. 图像的颜色空间转换_gray2rgb_youcans_的博客 - CSDN 博客