OpenCV_tutorials资料学习笔记
颜色缩减方法:将现有颜色空间值除以某个输入值,获得较少的颜色数。例如,颜色值0到9可取为新值0,10到19可取为10等等。
颜色空间缩减算法为:
1)遍历图像矩阵的每一个像素
2)对像素应用上述公式
ps:乘法和除法运算特别费时,尽可能用低代价的加、减、赋值等运算替换。对于较大的图像,有效的方法是预先计算所有可能的值,然后需要这些值的时候,利用查找表直接赋值即可。查找表是一维或多维数组,存储了不同输入值所对应的输出值,其优势在于只需读取、无需计算。
颜色空间缩减中的查找表计算:
uchar table[256]; //存储查找表
for (int i = 0; i < 256; ++i)
table[i] = (uchar)(divideWith * (i/divideWith)); //设置查找表,颜色空间缩减公式计算得到
计时:
OpenCV提供了两个简便的可用于计时的函数 getTickCount() 和 getTickFrequency()。第一个函数返回你的CPU自某个事件(如启动电脑)以来走过的时钟周期数,第二个函数返回你的CPU一秒钟所走的时钟周期数。
以秒为单位对某运算计时:
double t = (double)getTickCount();
// 做点什么 ...
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;
1)经典的C风格运算符[](指针)访问,是推荐的效率最高的查找表赋值方法
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
int channels = I.channels();
int nRows = I.rows * channels;
int nCols = I.cols;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
另外一种方法来实现遍历功能,就是使用
data
, data会从
Mat
中返回指向矩阵第一行第一列的指针。注意如果该指针为NULL则表明对象里面无输入,所以这是一种简单的检查图像是否被成功读入的方法。当矩阵是连续存储时,我们就可以通过遍历
data
来扫描整个图像。一个灰度图像操作如下:
uchar* p = I.data;
for( unsigned int i =0; i < ncol*nrows; ++i)
*p++ = table[*p];
2)迭代法 The iterator method
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
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;
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;
}
3)通过相关返回值的On-the-fly地址计算
Mat.at读取
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
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;
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;
}
4)利用opencv自带的核心函数LUT计算
在图像处理中,对于一个给定的值,将其替换成其他的值是一个很常见的操作,OpenCV 提供里一个函数直接实现该操作,并不需要你自己扫描图像,就是:operationsOnArrays:LUT() <lut> ,一个包含于core module的函数. 首先我们建立一个mat型用于查表:
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
p[i] = table[i];
然后调用函数
LUT(I, lookUpTable, J);
LUT函数为:
void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst, int interpolation )
{
Mat src = _src.getMat(), lut = _lut.getMat();
CV_Assert( interpolation == 0 );
int cn = src.channels();
int lutcn = lut.channels();
CV_Assert( (lutcn == cn || lutcn == 1) &&
lut.total() == 256 && lut.isContinuous() &&
(src.depth() == CV_8U || src.depth() == CV_8S) );
_dst.create( src.dims, src.size, CV_MAKETYPE(lut.depth(), cn));
Mat dst = _dst.getMat();
LUTFunc func = lutTab[lut.depth()];
CV_Assert( func != 0 );
const Mat* arrays[] = {&src, &dst, 0};
uchar* ptrs[2];
NAryMatIterator it(arrays, ptrs);
int len = (int)it.size;
for( size_t i = 0; i < it.nplanes; i++, ++it )
func(ptrs[0], lut.data, ptrs[1], len, cn, lutcn);
}
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> //opencv头文件
#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
<< "./howToScanImages 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[]) //argc是参数个数,第一参数argv[0]是运行程序名,系统自己会给定!
{
help();
if (argc < 3) //判断输入的参数数目是否符合需要
{
cout << "Not enough parameters" << endl;
return -1;
}
Mat I, J;
if( argc == 4 && !strcmp(argv[3],"G") ) //第四个参数argv[3]是用来区分读取图像的类型,==G时,读取灰度图像
I = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);//第二个参数argv[1]是读取图像路径,包括图像后缀名
else
I = imread(argv[1], CV_LOAD_IMAGE_COLOR);
if (!I.data) //判断图像读取是否成功
{
cout << "The image" << argv[1] << " could not be loaded." << endl;
return -1;
}
int divideWith = 0; // convert our input string to number - C++ style //divideWith是颜色空间缩减的参数,公式中的除数和乘数
stringstream s; //字符流数据存储
s << argv[2]; //用<<读取符号,将第三个参数argv[2]即缩减参数输出到s变量中
s >> divideWith; //将字符型数据转为int型,存到divideWith中:stringstream 类,把第三个命令行参数由字符串转换为整数
if (!s || !divideWith) //参数为0时退出函数
{
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)); //设置查找表,颜色空间缩减公式计算得到
const int times = 100;
double t;
t = (double)getTickCount(); //getTickcount返回你的CPU自某个事件(如启动电脑)以来走过的时钟周期数
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone(); //将图像I复制拷贝到clone_i,具有自己的矩阵头,矩阵指针,还有自己的数据矩阵
J = ScanImageAndReduceC(clone_i, table);
}
t = 1000*((double)getTickCount() - t)/getTickFrequency(); //getTickFrequency返回你的CPU一秒钟所走的时钟周期数
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); //Mat.at访问像素
}
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;
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
p[i] = table[i];
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
LUT(I, lookUpTable, J); //opencv自带的查找表函数
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;
cout << "Time of reducing with the LUT function (averaged for "
<< times << " runs): " << t << " milliseconds."<< endl;
system("pause");
return 0;
}
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table) //const uchar* const table是指向const uchar(无符号字符常量)的const常量指针table
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar)); //若括号中的表达式值为false,则返回一个错误信息。
int channels = I.channels(); //读取图像I的通道数
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous()) //isContinuous判断矩阵元素存储是否连续
{
nCols *= nRows; //矩阵存储连续,修改成一维矩阵存储格式,row为1
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i); //p指针指向图像I的第i行首地址
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]]; //将(i,j)位置的图像像素值修改成原有像素在查找表中的对应值
}
}
return I;
}
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table) //用C++特有的迭代法遍历图像
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
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;
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;
}
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
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;
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;
}
运行结果