图像颜色空间介绍

RGB空间模型

相当于把0-255(8U)压缩到0-1之间

三种存储数据的区间

8U 0-255

32F 0-1

64F 0-1

OPENCV把0-1区间强行映射到0-255,大于1映射为白,小于0映射为黑

图像数据类型间的互相转换

相当于

N(x,y) = \alpha I(x,y)+\beta \tag{3,1}

是一个类方法

```Cpp

Mat a;

a.convertTo(b,CV_32F,1/255.0,0);//把0-255转换到0-1

```
HSV颜色空间

色度(H),饱和度(S),亮度(V)

GRAY颜色模型

RGB模型转灰度图关系

Gray = R*0.3+G*0.59+B*0.11 \tag{3,2}

不同颜色空间转换

示例


#include <opencv2\opencv.hpp>

#include <iostream>

using namespace cv;

using namespace std;

int main()

{

    Mat img;

    img = imread("lena.png");

    Mat img32;

    img.convertTo(img32, CV_32F, 1 / 255.0, 0);

    Mat HSV, HSV32;

    cvtColor(img, HSV, COLOR_BGR2HSV);

    cvtColor(img32, HSV32, COLOR_BGR2HSV);

    Mat gray0, gray1;

    cvtColor(img, gray1, COLOR_RGB2GRAY);

    cvtColor(img, gray0, COLOR_BGR2GRAY);

    return 0;

}

多通道的分离与合并

多通道的分离

输出的图像是vector<Mat>

多通道的合并

示例

```Cpp

#include <opencv2\opencv.hpp>

#include <iostream>

using namespace cv;

using namespace std;

int main()

{

    Mat img;

    img = imread("lena.png");

    Mat img32;

    img.convertTo(img32, CV_32F, 1 / 255.0, 0);

    Mat gray0, gray1;

    cvtColor(img, gray1, COLOR_RGB2GRAY);

    cvtColor(img, gray0, COLOR_BGR2GRAY);

    Mat imgs[3];

    split(img, imgs);

    Mat img0, img1, img2;

    img0 = imgs[0];

    img1 = imgs[1];

    img2 = imgs[2];

    Mat img_H;

    merge(imgs, 3, img_H);//要给出要合并的通道数,如果用vector就不用

    Mat zero = Mat::zeros(Size(img.cols, img.rows),CV_8U);//零矩阵

    vector<Mat>imgsv;

    imgsv.push_back(imgs[0]);

    imgsv.push_back(zero);

    imgsv.push_back(zero);

    Mat img_HV;//img_HV就是img第一个通道的颜色,opencv默认BGR模型

    merge(imgsv, img_HV);

    return 0;

}

```

以下的运算是单通道的,在多通道的时候会使用对逐个通道运算之后再进行合并

图像像素运算

将输出值作为参数输入

会输出一个每一个像素都为二者最小(最大)的矩阵

图像最大值与最小值查找

![](https://name1110.oss-cn-beijing.aliyuncs.com/img/Pasted%20image%2020240214153108.png)

其中掩码矩阵表示查找的范围

只能寻找全局第一个最小/大值的位置

像素逻辑运算

两个像素的像素逻辑规则运算

![[Pasted image 20240214154941.png]]

对于8位二进制则逐位运算

00000000的非就11111111

0000010100000110进行与或异或运算分别为

000001000000011100000011

mask 还是掩码矩阵

示例

```Cpp

#include <opencv2\opencv.hpp>

#include <iostream>

#include <vector>

using namespace std;

using namespace cv;

int main()

{

    Mat img = imread("lena.png");

    if (img.empty())

    {

        cout << "请确认图像文件名称是否正确" << endl;

        return -1;

    }

    //创建两个黑白图像

    Mat img0 = Mat::zeros(200, 200, CV_8UC1);

    Mat img1 = Mat::zeros(200, 200, CV_8UC1);

    Rect rect0(50, 50, 100, 100);//定义区域0

    img0(rect0) = Scalar(255);//把区域0的所有数值设置为255

    Rect rect1(100, 100, 100, 100);

    img1(rect1) = Scalar(255);

    imshow("img0", img0);

    imshow("img1", img1);

    //进行逻辑运算

    Mat myAnd, myOr, myXor, myNot, imgNot;

    bitwise_not(img0, myNot);

    bitwise_and(img0, img1, myAnd);

    bitwise_or(img0, img1, myOr);

    bitwise_xor(img0, img1, myXor);

    bitwise_not(img, imgNot);

    imshow("myAnd", myAnd);

    imshow("myOr", myOr);

    imshow("myXor", myXor);

    imshow("myNot", myNot);

    imshow("img", img);

    imshow("imgNot", imgNot);

    waitKey(0);

    return 0;

}

```

图像取反类似于反色

图像二值化

固定阈值二值化

二值化的意思

> 把像素与某一个数值比较,把比较的结果作为最终结果输出

![](https://name1110.oss-cn-beijing.aliyuncs.com/img/Pasted%20image%2020240214174218.png)

注: 最大值是不一定出现的,是根据第五个参数使用的,如果第五个参数的二值化方法不需要最大值,则第四个参数可以随便写

**在第五个参数中

所以当使用这两个方法的时候阈值可以随便写

当使用大津法或者三角形法效果不是很好的情况可以把图像分割,再分别求阈值

当然你也可以用自适应二值化
自适应阈值二值化

C的目的是当目标图像太亮或者太暗的情况下,可以让平均值上下移动来处理

BTW blocksize必须是一个大于1的奇数

示例

```Cpp

#include <opencv2\opencv.hpp>

#include <iostream>

#include <vector>

using namespace std;

using namespace cv;

int main()

{

    Mat img = imread("lena.png");

    if (img.empty())

    {

        cout << "请确认图像文件名称是否正确" << endl;

        return -1;

    }

    Mat gray;

    cvtColor(img, gray, COLOR_BGR2GRAY);

    Mat img_B, img_B_V, gray_B, gray_B_V, gray_T, gray_T_V, gray_TRUNC;

    //彩色图像二值化

    threshold(img, img_B, 125, 255, THRESH_BINARY);

    threshold(img, img_B_V, 125, 255, THRESH_BINARY_INV);

    imshow("img_B", img_B);

    imshow("img_B_V", img_B_V);

    //灰度图BINARY二值化

    threshold(gray, gray_B, 125, 255, THRESH_BINARY);

    threshold(gray, gray_B_V, 125, 255, THRESH_BINARY_INV);

    imshow("gray_B", gray_B);

    imshow("gray_B_V", gray_B_V);

    //灰度图像TOZERO变换

    threshold(gray, gray_T, 125, 255, THRESH_TOZERO);

    threshold(gray, gray_T_V, 125, 255, THRESH_TOZERO_INV);

    imshow("gray_T", gray_T);

    imshow("gray_T_V", gray_T_V);

    //灰度图像TRUNC变换

    threshold(gray, gray_TRUNC, 125, 255, THRESH_TRUNC);

    imshow("gray_TRUNC", gray_TRUNC);

    //灰度图像大津法和三角形法二值化

    Mat img_Thr = imread("threshold.png", IMREAD_GRAYSCALE);

    Mat img_Thr_O, img_Thr_T;

    threshold(img_Thr, img_Thr_O, 100, 255, THRESH_BINARY | THRESH_OTSU);//自动获取阈值,使用THRESH_BINARY方法

    threshold(img_Thr, img_Thr_T, 125, 255, THRESH_BINARY | THRESH_TRIANGLE);

    imshow("img_Thr", img_Thr);

    imshow("img_Thr_O", img_Thr_O);

    imshow("img_Thr_T", img_Thr_T);

    //灰度图像自适应二值化

    Mat adaptive_mean, adaptive_gauss;

    adaptiveThreshold(img_Thr, adaptive_mean, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 55, 0);

    adaptiveThreshold(img_Thr, adaptive_gauss, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 55, 0);

    imshow("adaptive_mean", adaptive_mean);

    imshow("adaptive_gauss", adaptive_gauss);

    waitKey(0);

    return 0;

}

```

LUT查找表

LUT查找表原理

不仅实现二值化,还可以实现多值化

原图像数据只能是0-255,但是映射后不需要

通道数应该相同,如果多映射单则每个通道都按该查找表映射

查找表的结构类似于哈希,lut[100] = 1 的意思是把原灰度值100的像素映射为1

示例


#include <opencv2\opencv.hpp>

#include <iostream>

using namespace std;

using namespace cv;

int main(int argc, char** argv)

{

    //LUT查找表第一层

    uchar lutFirst[256];

    for (int i = 0; i < 256; i++)

    {

        if (i <= 100)

            lutFirst[i] = 0;

        if (i > 100 && i <= 200)

            lutFirst[i] = 100;

        if (i > 200)

            lutFirst[i] = 255;

    }

    Mat lutOne(1, 256, CV_8UC1, lutFirst);//写入到mat类中,定义为1行256列的矩阵

    //LUT查找表第二层

    uchar lutSecond[256];

    for (int i = 0; i < 256; i++)

    {

        if (i <= 100)

            lutSecond[i] = 0;

        if (i > 100 && i <= 150)

            lutSecond[i] = 100;

        if (i > 150 && i <= 200)

            lutSecond[i] = 150;

        if (i > 200)

            lutSecond[i] = 255;

    }

    Mat lutTwo(1, 256, CV_8UC1, lutSecond);

    //LUT查找表第三层

    uchar lutThird[256];

    for (int i = 0; i < 256; i++)

    {

        if (i <= 100)

            lutThird[i] = 100;

        if (i > 100 && i <= 200)

            lutThird[i] = 200;

        if (i > 200)

            lutThird[i] = 255;

    }

    Mat lutThree(1, 256, CV_8UC1, lutThird);

    //拥有三通道的LUT查找表矩阵

    vector<Mat> mergeMats;

    mergeMats.push_back(lutOne);

    mergeMats.push_back(lutTwo);

    mergeMats.push_back(lutThree);

    Mat LutTree;

    merge(mergeMats, LutTree);

    //计算图像的查找表

    Mat img = imread("lena.png");

    if (img.empty())

    {

        cout << "请确认图像文件名称是否正确" << endl;

        return -1;

    }

    Mat gray, out0, out1, out2;

    cvtColor(img, gray, COLOR_BGR2GRAY);//灰度图像只能用单通道查找表

    LUT(gray, lutOne, out0);

    LUT(img, lutOne, out1);

    LUT(img, LutTree, out2);

    imshow("out0", out0);

    imshow("out1", out1);

    imshow("out2", out2);

    waitKey(0);

    return 0;

}