#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/highgui.hpp>
#include <iostream>
#include <sstream>
using namespace std;
using namespace cv;
static void help() //运行时的菜单
{
cout
<< "\n--------------------------------------------------------------------------" << endl
<< "This program shows how to scan image objects in OpenCV (cv::Mat). As use case"
<< " we take an input image and divide the native color palette (255) with the " << endl
<< "input. Shows C operator[] method, iterators and at function for on-the-fly item address calculation." << endl
<< "Usage:" << endl
<< "./how_to_scan_images <imageNameToUse> <divideWith> [G]" << endl
<< "if you add a G parameter the image is processed in gray scale" << endl
<< "--------------------------------------------------------------------------" << endl
<< endl;
}
Mat& ScanImageAndReduceC(Mat& I, const uchar* table);
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* table);
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar * table);
int main(int argc, char* argv[])
{
help();
if (argc < 3) //如果命令行输入时参数不对
{
cout << "Not enough parameters" << endl;
return -1;
}
Mat I, J;
if (argc == 4 && !strcmp(argv[3], "G")) //根据是否输入G来改变imread参数
I = imread(argv[1], IMREAD_GRAYSCALE);
else
I = imread(argv[1], IMREAD_COLOR);
if (I.empty()) //看看是否成功读入Mat I
{
cout << "The image" << argv[1] << " could not be loaded." << endl;
return -1;
}
//! [dividewith]
int divideWith = 0; // convert our input string to number - C++ style
stringstream s; //讲字符串读入int
s << argv[2];
s >> divideWith;
if (!s || !divideWith) //看看是否读入成功
{
cout << "Invalid number entered for dividing. " << endl;
return -1;
}
uchar table[256]; //定义一个表
for (int i = 0; i < 256; ++i) //为这个表初始化
table[i] = (uchar)(divideWith * (i / divideWith));
//! [dividewith]
const int times = 100; //每个程序运行100次
double t;
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone(); //复制Mat 对象作为形参
J = ScanImageAndReduceC(clone_i, table);
}
t = 1000 * ((double)getTickCount() - t) / getTickFrequency(); //这个计算不太明白 1s的1000次?
t /= times;
cout << "Time of reducing with the C operator [] (averaged for " //输出时间
<< times << " runs): " << t << " milliseconds." << endl;
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
J = ScanImageAndReduceIterator(clone_i, table);
}
t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
t /= times;
cout << "Time of reducing with the iterator (averaged for "
<< times << " runs): " << t << " milliseconds." << endl;
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
ScanImageAndReduceRandomAccess(clone_i, table);
}
t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
t /= times;
cout << "Time of reducing with the on-the-fly address generation - at function (averaged for "
<< times << " runs): " << t << " milliseconds." << endl;
//! [table-init]
Mat lookUpTable(1, 256, CV_8U); //Lut函数第二个参数需要Mat类
uchar* p = lookUpTable.ptr(); //因为lookUpTable就1行
for (int i = 0; i < 256; ++i)
p[i] = table[i]; //每个点与表相同
//! [table-init]
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
//! [table-use]
LUT(I, lookUpTable, J);//用Lut函数操作
//! [table-use]
t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
t /= times;
cout << "Time of reducing with the LUT function (averaged for " //四种操作看看操作时间
<< times << " runs): " << t << " milliseconds." << endl;
imshow("show", J); //展示图片 个人加的
waitKey(0);
return 0;
}
//! [scan-c]
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table) //高效的方法 Efficient Way
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U); //这个函数只能接受char类型矩阵
int channels = I.channels(); //查看通道数
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous()) //看看整个矩阵的内存是否连续 如果连续就类似于1维数组
{
nCols *= nRows;
nRows = 1;
}
int i, j;
uchar* p;
for (i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i); //.ptr<>()返回行指针
for (j = 0; j < nCols; ++j)
{
p[j] = table[p[j]]; //赋值来改变
}
}
return I;
}
//! [scan-c]
//! [scan-iterator]
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table) //迭代法 The iterator (safe) method
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels(); //根据通道数来不同操作
switch (channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for (it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = table[*it];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end; //可以理解为每个点都是一个Vec,里面有三个点,这样就变成单通道了,个人感觉吧
for (it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
}
}
return I;
}
//! [scan-iterator]
//! [scan-random]
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table) //通过相关返回值的On-the-fly地址计算
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch (channels)
{
case 1:
{
for (int i = 0; i < I.rows; ++i)
for (int j = 0; j < I.cols; ++j)
I.at<uchar>(i, j) = table[I.at<uchar>(i, j)];
break;
}
case 3:
{
Mat_<Vec3b> _I = I; //.at()函数需要类似于下标的东西,感觉这种操作类似于第二种
for (int i = 0; i < I.rows; ++i)
for (int j = 0; j < I.cols; ++j)
{
_I(i, j)[0] = table[_I(i, j)[0]];
_I(i, j)[1] = table[_I(i, j)[1]];
_I(i, j)[2] = table[_I(i, j)[2]];
}
I = _I;
break;
}
}
return I;
}
//! [scan-random]
里面的注释都是个人见解,opencv才开始看没多久。有些vec3b什么时候用,.at函数用法等理解不是很全面。主要内容就是这个程序的思路吧,细节不怎么清楚,希望大家发现错误可以提醒我一下。