官方文档
若矩阵元素存储的是单通道像素,使用 C 或 C++ 无符号字符类型(uchar),那么像素可有 256 个不同的取值(0 ~255)。若图像采用三通道存储,颜色可有一千六百多万中取值(0 ~ -1),如此之多的颜色可能会对我们的算法性能造成影响。我们可以使用其中具有代表性的一部分来达到与原来近似的效果,即颜色空间缩减,做法是:将颜色值 0~9 映射为 0,10~19映射为10,等等。这样做可将颜色取值降低到 26 * 26 * 26 种。
由此,在处理较大的图像时,我们可以预先计算出所有可能的像素取值,将其存入一个 table 中,之后可以使用查表法来进行赋值(随机存取)。
//查找表设置
int divideWith = 10;
uchar table[256];
for(int i = 0; i < 256; ++i)
table[i] = divideWith * (i / divideWith);
//遍历图像对每个像素赋新值
p[j] = table[p[j]];
1. LUT 函数:Look up table 操作。OpenCV官方提供的函数,原型为:
void LUT(InputArray src, InputArray lut, OutputArray dst);
(1)第一个参数,InputArray 类型的 src,输入图像。
(2)第二个参数,InputArray 类型的 lut,查找表。
(3)第三个参数,OutputArray 类型的 dst,输出图像。
使用方法如下:
//创建一个 Mat 类型的查找表
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
//uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
p[i] = table[i];
//调用 LUT函数, times 遍历次数
for(int i = 0; i < tiems; i++)
LUT(I, lookUpTable, J);
2.自定义遍历像素的方法。
方法一 指针访问:C 操作符 [ ]
方法二 迭代器 iterator
方法三 动态地址计算
示例代码如下:
#include
#include
using namespace cv;
using namespace std;
Mat& scanImageAndReduceC(Mat srcImg, const uchar* table);
Mat& scanImageAndReduceIterator(Mat srcImg, const uchar* table);
Mat& scanImageAndReduceRandomAccess(Mat srcImg, const uchar* table);
int main()
{
Mat srcImage, dstImage;
srcImage = imread("cat.jpg");
imshow("原图", srcImage);
int divideWith = 32;
uchar table[256];
for (int i = 0; i < 256; i++)
table[i] = divideWith * (i / divideWith);
const int times = 100;
//1. 使用方法一遍历 100 次,取平均遍历时间
double t = (double)getTickCount();
for (int i = 0; i < times; i++)
{
Mat clone_src = srcImage.clone();
dstImage = scanImageAndReduceC(clone_src, table);
}
t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
t /= times;
cout << "Time of reducing with the C operator [] (averaged for "
<< times << " runs): " << t << " milliseconds." << endl;
//2. 使用方法二遍历 100 次,取平均遍历时间
t = (double)getTickCount();
for (int i = 0; i < times; i++)
{
Mat clone_src = srcImage.clone();
dstImage = scanImageAndReduceIterator(clone_src, table);
}
t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
t /= times;
cout << "Time of reducing with the Iterator (averaged for"
<< times << "runs): " << t << " milliseconds." << endl;
//3. 使用方法三遍历 100 次,取平均遍历时间
t = (double)getTickCount();
for (int i = 0; i < times; i++)
{
Mat clone_src = srcImage.clone();
dstImage = scanImageAndReduceRandomAccess(clone_src, 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;
//4. 使用官方 LUT 函数遍历100次,取平均时间
t = (double)getTickCount();
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for (int i = 0; i < 256; i++)
p[i] = table[i];
for (int i = 0; i < times; i++)
LUT(srcImage, lookUpTable, dstImage);
t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
t /= times;
cout << "Time of reducing with the LUT function (averaged for "
<< times << " runs): " << t << " milliseconds." << endl;
imshow("LUT 处理图像", dstImage);
waitKey(0);
return 0;
}
//C操作符[ ] 遍历
Mat& scanImageAndReduceC(Mat srcImg, const uchar* table)
{
//只对 uchar 类型的图像做处理
CV_Assert(srcImg.depth() == CV_8U);
int channels = srcImg.channels();
int rows = srcImg.rows; //通道数 * 每个通道包含的列数
int cols = channels * srcImg.cols;
//判断是否是连续存储
if (srcImg.isContinuous())
{
cols *= rows;
rows = 1;
}
uchar* p;
for(int i = 0; i < rows; i++)
{
p = srcImg.ptr(i);
for (int j = 0; j < cols; j++)
{
p[j] = table[p[j]];
}
}
return srcImg;
}
Mat& scanImageAndReduceIterator(Mat srcImg, const uchar* table)
{
CV_Assert(srcImg.depth() == CV_8U);
int channels = srcImg.channels();
switch (channels)
{
case 1:
{
MatIterator_ it, end;
for (it = srcImg.begin(), end = srcImg.end(); it != end; it++)
{
*it = table[*it];
}
break;
}
case 3:
{
MatIterator_ it, end;
for (it = srcImg.begin(), end = srcImg.end(); it != end; it++)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
break;
}
default:
break;
}
return srcImg;
}
Mat& scanImageAndReduceRandomAccess(Mat srcImg, const uchar* table)
{
CV_Assert(srcImg.depth() == CV_8U);
int channels = srcImg.channels();
int rows = srcImg.rows;
int cols = srcImg.cols;
switch (channels)
{
case 1:
{
for(int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
{
srcImg.at(i, j) = table[srcImg.at(i, j)];
}
break;
}
case 3:
{
Mat_ temp = srcImg;
for(int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
{
temp(i, j)[0] = table[temp(i, j)[0]];
temp(i, j)[1] = table[temp(i, j)[1]];
temp(i, j)[2] = table[temp(i, j)[2]];
}
srcImg = temp;
break;
}
default:
break;
}
return srcImg;
}
执行结果如下图: