OPENCV第三章-图像颜色处理
图像颜色空间介绍
RGB空间模型

相当于把0-255(8U)压缩到0-1之间
三种存储数据的区间
8U 0-255
32F 0-1
64F 0-1
OPENCV把0-1区间强行映射到0-255,大于1映射为白,小于0映射为黑
图像数据类型间的互相转换

相当于
是一个类方法
```Cpp
Mat a;
a.convertTo(b,CV_32F,1/255.0,0);//把0-255转换到0-1
```HSV颜色空间
色度(H),饱和度(S),亮度(V)

GRAY颜色模型

RGB模型转灰度图关系
不同颜色空间转换


示例
#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;
}
```图像像素运算

将输出值作为参数输入
会输出一个每一个像素都为二者最小(最大)的矩阵
图像最大值与最小值查找

其中掩码矩阵表示查找的范围
只能寻找全局第一个最小/大值的位置
像素逻辑运算
两个像素的像素逻辑规则运算
![[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;
}
```图像取反类似于反色
图像二值化
固定阈值二值化

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

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


**在第五个参数中
所以当使用这两个方法的时候阈值可以随便写
当使用大津法或者三角形法效果不是很好的情况可以把图像分割,再分别求阈值
自适应阈值二值化

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;
}