图像处理之偏色检查

209 阅读6分钟

1.偏色检查

在图像处理中,RGB 色彩空间虽然直观,但由于其三个通道(红、绿、蓝)之间的相关性较强,不利于颜色分析和分割。因此,HSV 色彩空间(色调 Hue、饱和度 Saturation、明度 Value)被广泛使用,尤其是在偏色检查和图像分割任务中。


HSV 色彩空间的优势

  1. 颜色分离更清晰:

    • HSV 将颜色信息(色调 H)、颜色纯度(饱和度 S)和亮度(明度 V)分离,便于单独处理。
    • 相比于 RGB 空间,HSV 更符合人类对颜色的感知。
  2. 适合图像分割:

    • 在 HSV 空间中,可以通过调整色调(H)和饱和度(S)来提取特定颜色的区域。
    • 明度(V)通道可以单独处理,例如通过直方图均衡化提升亮度。
  3. 偏色检查更有效:

    • 在 RGB 空间中,偏色检查容易受到亮度变化的影响。
    • 在 HSV 空间中,色调(H)和饱和度(S)能够更准确地反映颜色偏差。

HSV 色彩空间的三个通道

  1. 色调(Hue, H):

    • 表示颜色的基本属性,用角度度量,范围为 0°~360°。
    • 红色为 0°,绿色为 120°,蓝色为 240°。
    • 补色关系:黄色(60°)、青色(180°)、品红(300°)。
  2. 饱和度(Saturation, S):

    • 表示颜色的纯度,范围为 0%~100%。
    • 饱和度越高,颜色越鲜艳;饱和度越低,颜色越接近灰色。
    • 光谱色的饱和度为 100%,白色的饱和度为 0%。
  3. 明度(Value, V):

    • 表示颜色的亮度,范围为 0%(黑色)~100%(白色)。
    • 对于光源色,明度与发光体的亮度相关;对于物体色,明度与透射比或反射比相关。

HSV 在图像处理中的应用

  1. 偏色检查:

    • 在 HSV 空间中,通过分析色调(H)和饱和度(S)的分布,可以检测图像是否存在偏色。
    • 例如,如果图像的色调集中在某个狭窄范围内,可能存在偏色问题。
  2. 图像分割:

    • 通过设定色调(H)和饱和度(S)的阈值,可以提取特定颜色的区域。
    • 例如,提取图像中的绿色植物或红色标志。
  3. 亮度增强:

    • 对明度(V)通道进行直方图均衡化,可以提升图像的亮度,同时不影响颜色信息。

image.png

"""
Time    : 
Author  : 
"""
import cv2
import numpy as np

from typing import Union

from xxx.utils.logger import loger


class ColorClassify:
    def __init__(self):
        self.logger = loger
        self.hsv_color_dict = {
            "black": [np.array([0, 0, 0]), np.array([180, 255, 46])],
            "gray": [np.array([0, 0, 46]), np.array([180, 43, 220])],
            "white": [np.array([0, 0, 221]), np.array([180, 30, 255])],
            "red": [np.array([156, 43, 46]), np.array([180, 255, 255]), np.array([0, 43, 46]), np.array([10, 255, 225])],
            "green": [np.array([35, 43, 46]), np.array([77, 255, 255])],
            "blue": [np.array([100, 43, 46]), np.array([124, 255, 255])],
            "yellow": [np.array([26, 43, 46]), np.array([34, 255, 255])],
            "orange": [np.array([11, 43, 46]), np.array([25, 255, 255])],
            "purple": [np.array([125, 43, 46]), np.array([155, 255, 255])],
            "pink": [np.array([0, 43, 46]), np.array([10, 255, 255])],
            "brown": [np.array([0, 0, 0]), np.array([180, 30, 46])]
        }

        self.detect_result = {}

    def analysis_picture(self, image_path: str, threshold=0.9) -> None:
        for color in self.hsv_color_dict.keys():
            self.detect_result[color] = self.detect_color(image_path, color, threshold)

    def is_this_color(self, color: str) -> bool:
        if color.lower() not in self.detect_result.keys():
            return False
        elif self.detect_result[color.lower()] >= 0.9:
            return True
        else:
            return False
            
    @staticmethod
    def is_contain_chinese(img_url) -> bool:
        """
        判断img_url路径是否含有中文字符
        :param img_url: 图片路径
        :return: True or False
        """
        # 中文字符的正则表达式
        import re
        chinese_pattern = '[\u4e00-\u9fa5]'
        result = re.search(chinese_pattern, img_url)
        if result:
            return True
        else:
            return False

    def read_image_file(self, image_obj: Union[str, numpy.ndarray]) -> numpy.ndarray:
        """统一读取图片"""
        if isinstance(image_obj, numpy.ndarray):
            return image_obj
        if self.is_contain_chinese(image_obj):
            img_obj = cv2.imdecode(np.fromfile(image_obj, dtype=np.uint8), flags=cv2.IMREAD_COLOR)
            return img_obj
        else:
            return cv2.imread(image_obj)        

    def detect_color(self, image_obj: Union[str, numpy.ndarray],
                     color: str,
                     threshold: float = 0.9,
                     get_ratio: bool = False) -> Union[str, bool]:
        """
        判断图片是什么偏什么颜色的
        :param image_obj: 图片路径或者数组
        :param color: 什么颜色
        :param threshold: 阈值
        :param get_ratio: 是否获取概率值
        :return:
        """
        try:
            # 读取图片
            # self.logger.info(f"即将判断图片:{image_obj}是否是:{color}颜色")
            img = self.read_image_file(image_obj)
            # 转换颜色空间为HSV
            hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

            # 创建掩膜
            # Note:红色需要创建两个,因为hmin和hmax有两种可能的值
            if color.lower() == "red":
                mask1 = cv2.inRange(hsv, self.hsv_color_dict[color.lower()][0], self.hsv_color_dict[color.lower()][1])
                mask2 = cv2.inRange(hsv, self.hsv_color_dict[color.lower()][2], self.hsv_color_dict[color.lower()][3])
                mask = cv2.bitwise_or(mask1, mask2)
            else:
                mask = cv2.inRange(hsv, self.hsv_color_dict[color.lower()][0], self.hsv_color_dict[color.lower()][1])
            # 进行图像处理
            # res = cv2.bitwise_and(img, img, mask=mask1)
            purple_pixel_count = cv2.countNonZero(mask)
            total_pixels = img.shape[0] * img.shape[1]
            detect_ratio = purple_pixel_count / total_pixels
            # 判断是否为color色
            if get_ratio:
                self.logger.info("这张图片是{}的概率为:{}".format(color, detect_ratio))
                return detect_ratio
            if detect_ratio > threshold:
                self.logger.info("这张图片是{}的概率为:{}".format(color, detect_ratio))
                self.logger.info("检测到图片偏色")
                return True
            else:
                return False
        except:
            self.logger.error("判断图片偏色失败")
            if get_ratio:
                return '1000.0'
            return True

二、颜色提取、图像分割

同样,可以将图片从RGB转换成HSV提取图片中对应颜色的区域,例如我们提取下面这张图片绿色的区域。

image.png 整体思路如下

  1. 将图像从 BGR 色彩空间转换到 HSV 色彩空间。
  2. 定义绿色的 HSV 阈值范围。
  3. 使用阈值对图像进行分割,提取绿色区域。
  4. 对分割结果进行后处理(如形态学操作)以去除噪声。
  5. 展示原始图像和分割结果。
import cv2  
# 读取图像  
image = cv2.imread('test.jpg')  
  
# 将图像从 BGR 转换到 HSV 色彩空间  
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)  
  
# 定义绿色的 HSV 阈值范围  
lower_green = np.array([35, 43, 46]) # 下限  
upper_green = np.array([77, 255, 255]) # 上限  
  
# 根据阈值创建掩码  
mask = cv2.inRange(hsv_image, lower_green, upper_green)  
  
# 对掩码进行形态学操作(可选)  
kernel = np.ones((5, 5), np.uint8) # 定义kernel  
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 开运算去除噪声  
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 闭运算填充空洞  
  
# 将掩码应用到原始图像,提取绿色区域  
result = cv2.bitwise_and(image, image, mask=mask)  
  
cv2.imshow('Original Image', image)  
cv2.imshow('Green Mask', mask)  
cv2.imshow('Extracted Green', result)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

结果如下图(图片压缩了,长宽看着不是很协调)

测试.png 通过 HSV 色彩空间和阈值分割,可以轻松提取图像中的特定颜色区域。这种方法适用于许多应用场景,例如:

  • 提取自然图像中的指定颜色区域。
  • 检测交通标志中的红色或蓝色区域。
  • 分割医学图像中的特定组织。

三、亮度增强

亮度增强是对HSV中的V(明度通道)进行处理,处理完成后再转回RGB。完成在同时保留色调和饱和度信息的前提下,增强图像的亮度。

整体思路如下:

  1. 将图像从 BGR 色彩空间转换到 HSV 色彩空间。
  2. 分离 HSV 通道,提取明度(V)通道。
  3. 对明度通道进行直方图均衡化,增强亮度。
  4. 将增强后的明度通道与原始色调(H)和饱和度(S)通道合并。
  5. 将图像从 HSV 转换回 BGR 色彩空间。
  6. 展示原始图像和亮度增强后的图像。
import cv2  
  
# 读取图像  
image = cv2.imread('test.jpg')  
  
# 将图像从 BGR 转换到 HSV 色彩空间  
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)  
  
# 分离 HSV 通道  
h, s, v = cv2.split(hsv_image)  
  
# 对明度(V)通道进行直方图均衡化  
v_enhanced = cv2.equalizeHist(v)  
  
# 合并增强后的明度通道与原始色调和饱和度通道  
hsv_equalized = cv2.merge([h, s, v_enhanced])  
  
# 将图像从 HSV 转换回 BGR 色彩空间  
result = cv2.cvtColor(hsv_equalized, cv2.COLOR_HSV2BGR)  
  
# 显示结果  
cv2.imshow('Original Image', image)  
cv2.imshow('Brightness Enhanced Image', result)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

通过调整 HSV 空间中的明度(V)通道,可以有效增强图像的亮度,同时保留色调和饱和度信息。这种方法适用于以下场景:

  • 提升低光照条件下拍摄的图像亮度。
  • 增强图像中的细节信息。
  • 改善图像的视觉效果。