opencv2及之后采用面向对象编程,Mat类存储图片数据。
python&opencv中一幅彩色图片的数据结构可以看作3维的,它是庞大的 二维元组,其中每个元素又为像素元组,像素元组中元素为3个数字。我们知道一个像素代表一个点,这个点的颜色是由三原色混合而成,那个 元组的3个数字就代表3原色的数值以不同的数量混合就成了,各种颜色。
opencv&c++中图片数据用Mat存储,Mat数据主要是信息头和图片数据矩阵组成。
信息头内容包括:
1–矩阵的尺寸----比如—class Mat这个类中的----数据成员rows,cols—指定图像的尺寸
2–存储方法------对应—各种Mat的构造函数
3–存储地址
4–和一个指向----存储所有像素值的矩阵的----指针
一个Mat对象占内存最大的就是图片数据矩阵了,一幅300400图片就有300400=120000个像素,每个像素3个原色(3通道)的数值我们用uchar类型存储,3600008bit=360000Byte=360KB。当然并不是所有图片都是彩色图片,灰度图片就是只有一个颜色通道的黑白图片,灰度图片是以后经常使用的图片。
c++开发opencv力求实时运算,算法速度上要尽量快。所以opencv&c++用2维数组表示方便访问快速遍历,每三个元素代表一个像素,矩阵大小为rows(cols*3),一定要注意rows和cols是图像像素的行列数目,而非c++中mat的图片数据矩阵的行列数。
可以cout打印该二维数组,一定要选用小图片,否则打印时间太长。
#include<opencv2\opencv.hpp>
#include<iostream>
using namespace cv;//一定要有
using namespace std;
int main(){
Mat src = imread("D:/s.jpg");
cout << src << endl;
waitKey(0);
}
由于图片没有特别小的,没法数清他的行列。那我们呢自己定义Mat更容易看清他的结构。
#include<opencv2\opencv.hpp>
#include<iostream>
#include<Windows.h>
using namespace cv;//一定要有
using namespace std;
int main(){
Mat mat(4, 3, CV_8UC3, Scalar(0, 230, 45));//4*3大小的图片,像素为3通道uchar类型,每个像素都为(0,230,45)
cout << mat << endl;
Sleep(30000);//没有图像显示时waitkey无效,我们调用休眠来暂停
}
结果:
Mat的构造函数有很多:
Mat mat1;
mat1.create(200, 200, CV_8UC3);//像其他基本数据类型,如果未指定像素,像素为3原色默认数字的像素
Mat mat2=Mat::ones(200, 200, CV_8UC3);//全为(1,1,1)
Mat mat3=Mat::zeros(200, 200, CV_8UC3);//全为(0,0,0)
Mat mat4(200,200,CV_8UC3);
Mat mat5 = (Mat_<double>(3,3)<<3,3,3,2,2,2,1,1,1);//Mat_本身是一个模板类,其中的一个模板专用于图片存储重命名为Mat
那模我们怎末才能访问Mat中的像素呢?大概有三种推荐第一种。
我们如果打算将每个像素的每个通道值(每个原色)改为:new = old / (n*n) + n / 2;可以这样做:
定义函数参数src为待访问的图片Mat,dst为盛放像素值映射后图片的Mat,n是关于映射的系数。
【1】
void ColorReduce(Mat& src, Mat& dst, int n){
dst = src.clone();
int rownum = src.rows;
int colnum = src.cols*src.channels();
int i, j;
for (i = 0; i < rownum; i++){
uchar* fred = dst.ptr<uchar>(i);
for (j = 0; j < colnum; j++)
fred[j] = fred[j] / (n*n) + n / 2;
}
}
【2】
void ColorReduce1(Mat& src, Mat& dst, int n){
dst = src.clone();
Mat_<Vec3b>::iterator it = dst.begin<Vec3b>();
Mat_<Vec3b>::iterator itend = dst.end<Vec3b>();
for (; it!=itend; it++){
(*it)[0] = (*it)[0] / (n*n) + n / 2;
(*it)[1] = (*it)[1] / (n*n) + n / 2;
(*it)[2] = (*it)[2] / (n*n) + n / 2;
}
}
【3】
void ColorReduce2(Mat& src, Mat& dst, int n){
dst = src.clone();
int i, j;
for (i = 0; i < dst.rows; i++)
for (j = 0; j < dst.cols; j++){
dst.at<Vec3b>(i, j)[0] = ((dst.at<Vec3b>(i, j)[0]) / (n*n))+n/ 2;
dst.at<Vec3b>(i, j)[1] = ((dst.at<Vec3b>(i, j)[1]) / (n*n)) + n / 2;
dst.at<Vec3b>(i, j)[2] = ((dst.at<Vec3b>(i, j)[2]) / (n*n)) + n / 2;
}
}
我随便用了一张图片,其中第一种运行时间约为0.006秒后面两种比第一种多近40倍的时间。我们详细介绍第一种,后面两种了解:
关键是ptr(i),这是一个Mat成员函数,是一个返回安全指针的模板函数,使用时必须指明Mat数组中元素类型,返回第i行的首地址。
获得某一行的首地址就可以访问此行数据,要注意前面说的Mat结构:一行有src.chanels()*src.cols列。