OpenCV图像
我们在前面讲了配置,讲了OpenCV一个很重要的数据结构Mat。那么Mat就是为了图像操作而生的。
图像的遍历
试想对于一个二维数组我们是如何遍历的?
我们写嵌套循环,第一个循环是二维数组的行数控制,第二个就是列数控制。
那么这一套也完全可以用到图像遍历中来!
只是我们需要考虑的一个问题就是图像的存储是否是连续的呢?即第一行的最后一列和第二行的第一个之间的地址差值是相邻的么?
好吧,实际上我们不需要通过大串代码去判断,因为有内置API!
那么我们写一个遍历的形式:
#include<iostream>
#include<string>
#include<opencv2/opencv.hpp>
#include<opencv2/imgcodecs.hpp>
#include<opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
const string file_path = "D://C++//kingjames//Debug//favorite.jpg";
int main() {
Mat img = imread(file_path);
// 判断一张图像的存储是否是连续的
cout << boolalpha << img.isContinuous() << endl;
//Mat copy_img = Mat::zeros(img.size(), CV_8UC3);
int row = img.rows;
int col = img.cols;
int channel = img.channels();
// 试想如果是三通道,那么我们的列数就应该乘上3能对每个值进行遍历到
col *= channel;
// 如果是连续的话我们可以用一维的形式来遍历即可,遍历总数就是col * row * channel
if (img.isContinuous()) {
row = 1;
col *= row;
}
uchar* p;
for (int i = 0; i < row; i++) {
p = img.ptr<uchar>(i);
for (int j = 0; j < col; j++) {
cout << p[j] << endl;
}
}
return 0;
}
我们读入一张图片后获得其行数与列数。在列数上值得我们注意,因为不要忘了图片的通道数!上述代码中我们直接将列数与其相乘得到一个新值后嵌套了两层循环,但是也未必一定如此。我们可以嵌套三层循环,令第三层循环被通道数控制即可。我们每遍历一行就先去获得当前行的首地址,即用ptr来获取。
ptr就代表了指针pointer,uchar就是数据类型代表unsigned char,最后的i就是行标。如此形式获得的就是第i行的首地址。
这里就不进行打印输出了,因为实在太长了。我们在进行项目Debug时可能对问题定位要输出,平时的话一张小图也就算了,我这张图太大!
在介绍迭代器遍历时,要先介绍另一种OpenCV数据结构,是Vec3b。
假设我们的一个像素点是三通道的,那么我们可以用一个Vec3b数据结构来装取那个像素点的值,同时用索引“[]”来获得某个通道的具体值。
(要注意Vec3b中的三个值都是uchar类型。)
迭代器相信大家在用STL标准库中很多容器的时候都已经体会到过了,我们只要获得一个头一个尾,然后类似于指针一样不断往后移得到的就完成了,那么步骤实际上一模一样。声明迭代器,获得头和尾,让迭代器从头到尾即可:
#include<iostream>
#include<string>
#include<opencv2/opencv.hpp>
#include<opencv2/imgcodecs.hpp>
#include<opencv2/imgproc.hpp>
using