OpenCV 图像色彩空间转换与抠图

发布于:2025-06-07 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、知识点:

1、色彩空间转换函数
  (1)、void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0, AlgorithmHint hint = cv::ALGO_HINT_DEFAULT );
  (2)、将图像从一种颜色空间转换为另一种。
  (3)、参数说明:
      src: 输入图像,即要进行颜色空间变换的原图像。
      dst: 输出图像,即进行颜色空间变换后的图像。
      code: ColorConversionCodes枚举值,确定从什么颜色空间转换到什么颜色空间,如: COLOR_BGR2RGB、 COLOR_BGRA2RGBA、 COLOR_BGR2GRAY、 COLOR_BGR2HSV。
      dstCn: 指定目标图像的通道数,若为0,则通道数由src和code决定。
      hint: 算法提示。
  (5)、注意:
      对于RGB色彩空间,OpenCV的通道顺序是BGR,即标准24位彩色图像,第一个字节是蓝色分量,第二个字节是绿色分量,第三个字节是红色分量,第四、第五、第六依次是第二个像素的蓝、绿、红分量,以此类推。
      cvtColor用COLOR_BGR2RGB和用COLOR_RGB2BGR来转换原RGB色彩空间的图像,效果是一样的。
      本质上内部调用mixChannels()函数,都是将原先的B通道拷贝给目标的R通道,将原先的G通道拷贝给目标的G通道,将原先的R通道拷贝给目标的B通道。
      
2、HSV颜色空间
  (1)、H: 色相,即什么颜色,opencv中取值范围[0, 180]。
  (2)、S: 饱和度,即颜色有多深,opencv中取值范围[0, 255]。
  (3)、V: 色调、纯度、亮度,即颜色有多亮,opencv中取值范围[0, 255]。
  (4)、HSV颜色范围:
      黑: H[0, 180],S[0, 255],V[0, 46]。
      灰: H[0, 180],S[0, 43],V[46, 220]。
      白: H[0, 180],S[0, 30],V[221, 255]。
      红: H[0, 10]、[156、 180], S[43, 255],V[46, 255]。 
      橙: H[11, 25],S[43, 255],V[46, 255]。
      黄: H[26, 34],S[43, 255],V[46, 255]。
      绿: H[35, 77],S[43, 255],V[46, 255]。
      青: H[78, 99],S[43, 255],V[46, 255]。
      蓝: H[100, 124],S[43, 255],V[46, 255]。
      紫: H[125, 155],S[43, 255],V[46, 255]。 
  (5)、HSV比RGB更容易区分出颜色。
  
3、void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);
  (1)、创建一个掩码dst,将原始图像src中满足给定范围[lowerb, upperb]条件的像素置255,其余置0。
  (2)、参数说明:
      src: 输入图像。
      lowerb: 下界,与输入图像类型和通道数相同的标量。
      upperb: 上界,与输入图像类型和通道数相同的标量。
      dst: 输出掩码,类型为CV_8UC1。
      
      
二、示例代码: 从原始绿色背景图像中抠出人物到红色背景图像中

#include <iostream>
#include <opencv2/opencv.hpp>


int main()
{
    cv::Mat src1 = cv::Mat::zeros(3, 3, CV_8UC3);
    src1 = cv::Scalar(100, 20, 200);
    std::cout << "src1:" << std::endl << src1 << std::endl;

    //先把原B通道给目标R通道,再把原G通道给目标G通道,再把原R通道给目标B通道。
    cv::Mat dst2;
    cv::cvtColor(src1, dst2, cv::COLOR_BGR2RGB);
    std::cout << "dst2:" << std::endl << dst2 << std::endl;

    //先把原R通道给目标B通道,再把原G通道给目标G通道,再把原B通道给目标R通道。
    //所以dst2和dst3的结果是一样的。
    cv::Mat dst3;
    cv::cvtColor(src1, dst3, cv::COLOR_RGB2BGR);
    std::cout << "dst3:" << std::endl << dst3 << std::endl;

    cv::Mat src2 = cv::imread("../images/9.png");
    if (src2.empty())
    {
        std::cout << "load src2 image error..." << std::endl;
        return -1;
    }
    else
    {
        std::cout << "load src2 image ok..." << std::endl;
        cv::imshow("原始图像", src2);
    }
    
    //转换成HSV色彩空间后,显示的HSV图像颜色变了,猜测是imshow把hsvImage还是按BGR的方式显示。
    cv::Mat hsvImage;
    cv::cvtColor(src2, hsvImage, cv::COLOR_RGB2HSV);
    cv::imshow("HSV图像", hsvImage);

    //绿色的HSV最小值cv::Scalar(35, 43, 46),最大值cv::Scalar(77, 255, 255)。
    //把图像的绿色背景在mask中对应位置置255,其余置0。
    cv::Mat mask;
    cv::inRange(hsvImage, cv::Scalar(35, 43, 46), cv::Scalar(77, 255, 255), mask);
    cv::imshow("mask", mask);

    //mask取反,绿色背景在mask中对应位置置0,其余置255。
    cv::bitwise_not(mask, mask);
    cv::imshow("mask not", mask);

    //创建一个红色背景图
    cv::Mat redbackImage = cv::Mat::zeros(src2.size(), src2.type());
    redbackImage = cv::Scalar(40, 40, 200);
    
    //抠图拷贝到红色背景图上
    src2.copyTo(redbackImage, mask);
    cv::imshow("ROI区域提取", redbackImage);

    cv::waitKey(0);

    return 0;
}

输出结果:
src1:
[100,  20, 200, 100,  20, 200, 100,  20, 200;
 100,  20, 200, 100,  20, 200, 100,  20, 200;
 100,  20, 200, 100,  20, 200, 100,  20, 200]
dst2:
[200,  20, 100, 200,  20, 100, 200,  20, 100;
 200,  20, 100, 200,  20, 100, 200,  20, 100;
 200,  20, 100, 200,  20, 100, 200,  20, 100]
dst3:
[200,  20, 100, 200,  20, 100, 200,  20, 100;
 200,  20, 100, 200,  20, 100, 200,  20, 100;
 200,  20, 100, 200,  20, 100, 200,  20, 100]
load src2 image ok...