文章目录
图像在计算机中是如何存储的,在计算机中保存图像的流行格式-灰度和RGB格式,图像的最小单位是像素,描述一张图我们通常所说的x*y,意味着图像的尺寸就是图像的高度(x)和宽度(y)上的像素数,所以计算机中每个图像都以 数字矩阵 的形式保存;
1、什么是 Mat类:
Mat类 结构:Mat类可以分为两个部分,矩阵头 和 数据;
Mat类
|—— 矩阵头
| |——尺寸
| |——行数
| |—— 列数
| |—— 数据类型
| |—— 通道数
| |—— 引用次数
|—— 数据
2、Mat 类中能存储什么类型的数据:
(1) cv::Mat_< _TP > 可以存储,自定义数据类型
(2) cv::Mat_< double > 可以存储,double 类型数据
(3) cv::Mat_< float > 可以存储,float 类型数据
(4) cv::Mat_< uchar > 可以存储,uchar 类型数据
(5) cv::Mat_< unsigned char > 可以存储,unsigned char 类型数据
opencv 中规定的数据类型:
数据类型 | 具体类型 | 取值范围 |
---|---|---|
CV_8U | 8位无符号整数 | 0 ~ 255 |
CV_8S | 8位符号整数 | -128 ~ 127 |
CV_16U | 16位无符号整数 | 0 ~ 65535 |
CV_16S | 16位符号整数 | -32768 ~ 32767 |
CV_32S | 32位符号整数 | -2147483648 ~ 2147483647 |
CV_32F | 32位浮点整数 | -FLT_MAX ~ FLT_MAX,INF,NAN |
CV_64F | 64位浮点整数 | -DBL_MAX ~ DBL_MAX,INF,NAN |
3、Mat类 的创建
(1)使用默认构造函数 Mat()
创建Mat 类:
- 构造函数原型:
Mat()
- 示例:实例化一个Mat矩阵
Mat img; // 声明一个保存图像的类 img = imread("C:\\cpp\\vs\\suzy.jpg"); // 读取图像
(2)利用矩阵 宽,高,和数据类型,创建Mat 类:
-
构造函数原型:
Mat(int rows, int cols, int type)
rows:构造矩阵的行数; cols:构造矩阵的列数; type:矩阵中存储的数据类型,CV_8U定义通道1~4,可以写成CV_8UC1 ~ CV_8UC4,通道数大于4,要这样写CV_8UC(n),n最大值512;
-
示例:实例化一个,3行3列,单通道的Mat矩阵
Mat a(3, 3, CV_8UC1);
(3)利用矩阵 Size()结构,和数据类型,创建Mat 类:
- 构造函数原型:
Mat(Size size, int type)
size:2D数组变量尺寸,通过Size(rows, cols)赋值,rows:构造矩阵的行数,cols:构造矩阵的列数; type:矩阵中存储的数据类型,CV_8U定义通道1~4,可以写成CV_8UC1 ~ CV_8UC4,通道数大于4,要这样写CV_8UC(n),n最大值512;
- 示例:实例化一个,4行4列,单通道的Mat矩阵
Mat b(Size(4, 4), CV_8UC1);
(4)利用已有Mat类,创建新的Mat 类:
-
构造函数原型:
Mat ( const Mat &m, const Range &rowRange, const Range &colRange=Range::all() )
m:已经构造完成的Mat类矩阵数据; rowRange:在已有矩阵 m 中,需要截取的行数范围,是一个Range变量,例如从第2行到第5行可以表示为Range(2, 5); colRange:在已有矩阵 m 中,需要截取的列数范围,是一个Range变量,例如从第2列到第5列可以表示为Range(2, 5),当不输入任何值时表示所有列都会被截取;
-
示例:创建一个2行4列的,单通道的Mat矩阵
// 创建一个6行6列,单通道的Mat矩阵 Mat a(6, 6, CV_8UC1); // 利用已有m矩阵,创建新的n矩阵 Mat n = Mat(m, Range(2, 4), Range(2, 4));
4、Mat类 的赋值
(1)使用默认构造函数 Mat()
创建Mat 类:
- 构造函数原型:
Mat()
- 示例:实例化一个Mat矩阵,并赋值
Mat img = imread("C:\\cpp\\vs\\suzy.jpg");
(2)利用矩阵 宽,高,和数据类型,创建Mat 类,并赋值:
-
构造函数原型:
Mat(int rows, int cols, int type, const Scalar &s)
rows:构造矩阵的行数; cols:构造矩阵的列数; type:矩阵中存储的数据类型; s:给矩阵中每个像素赋值的参数变量,例如Scalar(0, 0, 255)
-
示例:
Mat c0(5, 5, CV_8UC1, Scalar(4, 5, 6)); Mat c1(5, 5, CV_8UC2, Scalar(4, 5, 6)); Mat c2(5, 5, CV_8UC3, Scalar(4, 5, 6));
(3)Mat类在创建时可以直接调用Mat类自己的内置方法进行赋值:
- eye:单位矩阵;
- diag:对角矩阵;
- ones:相当于每个像素的第一个通道为1,其余两个通道为0,
Scalar(1,0,0)
; - zeros:相当于创建一张黑色的图,每个像素的每个通道都为0,
Scalar(0,0,0)
; - 示例:创建一个对角矩阵
// 下面的定义默认省略了Scalar(1, 0, 0),Mat::ones(2, 2, CV_8UC3, Scalar(1, 0, 0) ); Mat m5 = Mat::ones(2, 2, CV_8UC3); // 创建空白图像 // // 下面的定义默认省略了Scalar(0, 0, 0),Mat::zeros(Size(200, 200), CV_8UC3, Scalar(0, 0, 0) ); Mat m3 = Mat::zeros(Size(200, 200), CV_8UC3); // 赋值红色 m3 = Scalar(0, 0, 255); Mat m4 = Mat::zeros(Size(400, 400), CV_8UC3); // 赋值蓝色 m3 = Scalar(255, 0, 0); imshow("红色图像", m3); imshow("蓝色图像", m4);
(4)枚举法赋值:
枚举赋值方式:cv::Mat a = ( cv::Mat_
<c语言基本数据类型>
(rows, cols) << 枚举值 );
-
c语言基本数据类型:int,double 等;
-
rows:构造矩阵的行数;
-
cols:构造矩阵的列数;
-
枚举值:填充值;
-
示例:
Mat d = (cv::Mat_<int>(1,5) << 1,2,3,4,5); cv::Mat a = ( cv::Mat_<int>(3,3) << 1,2,3,4,5,6,7,8,9 ); cv::Mat b = ( cv::Mat_<double>(2,3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2 );
5、Mat类 元素的读取,图像像素的读写操作:
(1)Mat类矩阵的常用属性函数
属性 | 作用 |
---|---|
cols | 矩阵的列数 |
rows | 矩阵的行数 |
step | 以字节为单位的矩阵的有效宽度 |
elemSize() | 每个元素的字节数 |
total() | 矩阵中元素的个数 |
channels() | 矩阵的通道数 |
(2)读取图像像素常用函数
读取函数:at( int row, int col )
- 单通道
读取单通道第一个元素的值
int value = (int)a.at<uchar>(0, 0);
- 多通道
双通道元素值读取:
cv::Vec2b, vc2 = b.at<cv::Vec2b>(0, 0);
int first = (int)vc2.val[0];
三通道元素值读取:
cv::Vec3b, vc3 = b.at<cv::Vec3b>(0, 0);
int first = (int)vc3.val[0];
矩阵元素地址定位方式,访问元素值
- 即可以读取单通道元素值,也可以读取多通道元素值
(int)( *( b.data + b.step[0] * row + b.step[1] * col + channel ) )
row:行数
col:列数
channel:通道数
b.step[0]:x轴
b.step[1]:y轴
channel:z轴
(3)图像像素的读写操作方法
-
方法一:数组遍历方式,读写图像像素
// Mat类 元素的读取,图像像素的读写操作 void pixel_visit(Mat &image) { // 获取行数 int h = image.rows; // 获取列数 int w = image.cols; // 获取通道数 int dims = image.channels(); std::cout << h << std::endl; std::cout << w << std::endl; std::cout << dims << std::endl; // 数组遍历方式,读取图像像素 for (int row = 0; row < h; row++) { // 遍历行 for (int col = 0; col < w; col++) { //遍历列 // 通道数=1,灰度图像 if (dims==1) { int pv = image.at<uchar>(row, col); // 读取像素值 image.at<uchar>(row, col)=255-pv; // 给像素值重新赋值 } if (dims == 3) { // 通道数=3,RGB图像即彩色图像 Vec3b bgr = image.at<Vec3b>(row, col); // 读取像素值,读取出来的bgr是个数组,数组内有3个元素 image.at<Vec3b>(row, col)[0] = 255 - bgr[0]; image.at<Vec3b>(row, col)[1] = 255 - bgr[1]; image.at<Vec3b>(row, col)[2] = 255 - bgr[2]; } } } // 显示图像 imshow("像素读写演示", image); }
-
方法二:指针方式遍历,读写图像像素
opencv Mat数据类型,指针ptr()函数的使用:uchar a = a1.ptr
<uchar>
(row)[column],指向a1的第row+1行的第column+1个数据,数据类型为无符号整型;
(1)单通道,是一个二维数组,读取的像素值,是一个包含1个值的一维数组:
cv::Mat image = cv::Mat(3, 6, CV_8UC1); // 定义一个3行6列单通道的Mat变量,image
uchar *data00 = image.ptr<uchar>
(0); // 创建一个指针data00,指向images的第1行的第1个元素
uchar *data10 = image.ptr<uchar>
(1); // 创建一个指针data10,指向images的第2行的第1个元素
uchar *data01 = image.ptr<uchar>
(0)[1]; // 创建一个指针data01,指向images的第1行的第2个元素
(2)多通道,也是一个二维数组,读取的像素值,是一个包含3个值的一维数组:
cv::Mat image = cv::Mat(6, 9, CV_8UC3); // 定义一个6行9列3通道的Mat变量,image,3通道彩色图片
cv::Vec3b *data00 = image.ptrcv::Vec3b(0); // 创建一个指针data00,指向images的第1行的第1个元素
cv::Vec3b *data10 = image.ptrcv::Vec3b(1); // 创建一个指针data10,指向images的第2行的第1个元素
cv::Vec3b *data01 = image.ptrcv::Vec3b(0)[1]; // 创建一个指针data01,指向images的第1行的第2个元素// Mat类 元素的读取,图像像素的读写操作 void QuickDemo::pixel_visit(Mat &image) { // 获取行数 int h = image.rows; // 获取列数 int w = image.cols; // 获取通道数 int dims = image.channels(); std::cout << h << std::endl; std::cout << w << std::endl; std::cout << dims << std::endl; // 指针方式遍历,读写图像像素 for (int row = 0; row < h; row++) { // 遍历行 // 获取当前行的指针 uchar *currentRow = image.ptr<uchar>(row); for (int col = 0; col < w; col++) { //遍历列 // 单通道,是一个二维数组,读取的像素值,是一个包含1个值的一维数组,array1(): if (dims == 1) { int pv = *currentRow; // 通过指针获取像素值 *currentRow++ = 255 - pv; // 给像素值重新赋值 } if (dims == 3) { //3通道,也是一个二维数组,读取的像素值,是一个包含3个值的一维数组,array2(); *currentRow++ = 255 - *currentRow; // 指针currentRow指向数组元素array2[0]的地址 *currentRow++ = 255 - *currentRow; // 指针currentRow指向数组元素array2[1]的地址 *currentRow++ = 255 - *currentRow; // 指针currentRow指向数组元素array2[2]的地址 } } } // 显示图像 imshow("像素读写演示", image); }
6、Mat类的运算,图像像素的算术操作:
图像的算数运算是对图像进行加减乘除运算,过算术运算可以让图像来达到图像增强的效果;
(1)加减乘除运算
图像和标量:
Mat out;
//加
out = image1 + Scalar(50, 50, 50);
imshow("加法", out);
//减
out = image1 - Scalar(50, 50, 50);
imshow("减法", out);
//乘
//out = image1 * Scalar(2, 2, 2); 直接乘法会出错
//imshow("乘法", out);
//除
out = image1 / Scalar(0.5, 0.5, 0.5);
imshow("除法", out);
图像和图像:
opencv为 Mat类 提供的一些运算函数
函数名 | 作用 |
---|---|
add() | 矩阵求和 |
subtract() | 矩阵减法 |
multiply | 矩阵乘法 |
divide() | 矩阵除法 |
max()/min() | 两个矩阵计算最大值 / 最小值 |
示例:操作可使图像亮度变化
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat image1 = imread("C:\\Users\\Pictures\\suzy.jpg");
Mat image2 = Mat::zeros(image1.size(), image1.type());
image2 = Scalar(2, 2, 2);
Mat out;
//加
add(image1, image2, out);
imshow("加法", out);
//减
subtract(image1, image2, out);
imshow("减法", out);
//乘
multiply(image1, image2, out);
imshow("乘法", out);
//除
divide(image1, image2, out);
imshow("除法", out);
waitKey(0);
destroyAllWindows();
return 0;
}
(2)两个矩阵相乘
- 矩阵乘积:
*
cij = ai1b1i + ai2b2j + ai3b3j - 向量内积:
.dot()
f=d1e1 + d2e2 + d3e3 - 对应位元素乘积:
.mul()
cij = aij bij
7、Mat类的运算,图像像素的逻辑运算(与、或、非、异或):
图像的逻辑运算是对图像进行与、或、非、异或等逻辑运算,通过逻辑运算对图像进行分割、图像增强、图像识别、图像复原等操作(图像的逻辑运算严格意义上来说 叫 位运算bitwise)
(1)逻辑运算基础知识:
逻辑运算只有两个布尔值:
0
-> 表示假 False1
-> 表示真 True
运算 | 数学符号 | 运算规则 | OpenCV内置函数 |
---|---|---|---|
与 | AND | 两个操作数都为真时,结果才为真,其他情况均为假 | bitwise_and() |
或 | OR | 有一个操作数为真则结果为真,两个操作数都为假时结果才为假 | bitwise_or() |
非 | NOT | 进行非运算,真变假,假变真 | bitwise_not() |
异或 | XOR | 两个操作数相同为假,相异为真 | bitwise_xor() |
(2)opencv 的位操作内置函数:
- 与 bitwise_and()
- 或 bitwise_or()
- 非 bitwise_not()
- 异或 bitwise_xor()
示例代码:
// Mat类的运算,图像像素的逻辑运算
void QuickDemo::bitwise(Mat &image) {
// 声明两个图像
Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
// 填充两个实心矩形
rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);
rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
imshow("m1", m1);
imshow("m2", m2);
// 图像像素的位运算
// 与
Mat dst;
bitwise_and(m1, m2, dst);
imshow("and", dst);
bitwise_or(m1, m2, dst);
imshow("or", dst);
bitwise_not(m1, dst);
imshow("not", dst);
bitwise_xor(m1, m2, dst);
imshow("xor", dst);
};