在上一次中的最后,提到了修改图像数据的操作。但是,这种操作是比较慢的。下面说一下访问图像数据的几种方式。毕竟在实际的图像处理中,时刻少不了要自己写算法,这就必须访问图像数据,而快的访问方式可以大大提高效率。下面的例子中,还以修改图像数据为例。这些例子中,都通过用255减去原像素值来修改图像数据。其实这种操作就是反色操作。
例9-1 第一种修改方式,使用at访问修改像素值,这就是上次的例8,现在加上处理时间放在这里。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("E:/1.jpg", 1);
Mat modifyImg = Mat(src.size(), src.type());//新建一个与src大小、类型相同的空白图像
namedWindow("例9_1原图", 0);
imshow("例9_1原图", src);
double time0 = static_cast<double>(getTickCount());
int height = src.rows;//图片的高度
int width = src.cols;//图片的宽度
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
modifyImg.at<Vec3b>(i, j)[0] = 255 - src.at<Vec3b>(i, j)[0];
modifyImg.at<Vec3b>(i, j)[1] = 255 - src.at<Vec3b>(i, j)[1];
modifyImg.at<Vec3b>(i, j)[2] = 255 - src.at<Vec3b>(i, j)[2];
}
}
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "使用at的运行时间为:" << time0 << "秒" << endl;
namedWindow("例9_1修改图", 0);
imshow("例9_1修改图", modifyImg);
waitKey(0);
return 0;
}
例9-2第二种修改方式,使用指针访问修改像素值
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("E: /1.jpg", 1);
Mat modifyImg = Mat(src.size(), src.type());//新建一个与src大小、类型相同的空白图像
namedWindow("例9_2原图", 0);
imshow("例9_2原图", src);
double time0 = static_cast<double>(getTickCount());
///通过指针访问像素值
int height = src.rows;//图片的高度
int width = src.cols;//图片的宽度
int step = src.cols * src.channels();
for (int i = 0; i < height; i++)
{
uchar* data = src.ptr<uchar>(i);
uchar* mdData = modifyImg.ptr<uchar>(i);
for (int j = 0; j < step; j++)
{
mdData[j] = saturate_cast<uchar>(255 - data[j]);
}
}
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "使用指针运行时间为:" << time0 << "秒" << endl;
namedWindow("例9_2修改图", 0);
imshow("例9_2修改图", modifyImg);
waitKey(0);
return 0;
}
例9-3第三种修改方式,使用查找表访问修改像素值
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("E: /1.jpg", 1);
Mat modifyImg = Mat(src.size(), src.type());//新建一个与src大小、类型相同的空白图像
namedWindow("例9_原图", 0);
imshow("例9_原图", src);
double time0 = static_cast<double>(getTickCount());
///通过查找表访问像素值,三通道图像
// 建立LUT
uchar LutTable[256 * 3];
for (int i = 0; i < 256; ++i)
{
LutTable[i * 3] = 255 - i;
LutTable[i * 3 + 1] = 255 - i;
LutTable[i * 3 + 2] = 255 - i;
}
Mat lookUpTable(1, 256, CV_8UC3, LutTable);
// 应用索引表进行查找
LUT(src, lookUpTable, modifyImg);
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "使用查找表运行时间为:" << time0 << "秒" << endl;
namedWindow("例9_修改图", 0);
imshow("例9_修改图", modifyImg);
waitKey(0);
return 0;
}
从上面三种方式看,第一种方式速度是最慢的,指针访问比较快,而使用查找表速度是最快的。
上面都是对三通道图像的数据进行修改,同样的方式可以实现对单通道图像的访问。下面使用采用查找表和指针的方式对单通道图像进行像素值修改。
例9-4 利用查找表修改单通道图像数据
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("E: /1.jpg", 1);
Mat src_gray;
cvtColor(src, src_gray, COLOR_RGB2GRAY);
Mat modifyImg = Mat(src_gray.size(), src_gray.type());//新建一个与src大小、类型相同的空白图像
namedWindow("例9_4原图", 0);
imshow("例9_4原图", src_gray);
double time0 = static_cast<double>(getTickCount());
///通过查找表访问像素值,单通道图像
// 建立LUT
uchar LutTable[256];
for (int i = 0; i < 256; ++i)
{
LutTable[i] = 255 - i;
}
Mat lookUpTable(1, 256, CV_8UC1, LutTable);
// 应用索引表进行查找
LUT(src_gray, lookUpTable, modifyImg);
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "使用查找表处理单通道图像运行时间为:" << time0 << "秒" << endl;
namedWindow("例9_4修改图", 0);
imshow("例9_4修改图", modifyImg);
waitKey(0);
return 0;
}
例9-5 利用指针修改单通道图像数据
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("E: /1.jpg", 1);
Mat src_gray;
cvtColor(src, src_gray, COLOR_RGB2GRAY);
Mat modifyImg = Mat(src_gray.size(), src_gray.type());//新建一个与src_gray大小、类型相同的空白图像
namedWindow("例9_5原图", 0);
imshow("例9_5原图", src);
double time0 = static_cast<double>(getTickCount());
///通过指针访问像素值
int height = src_gray.rows;//图片的高度
int width = src_gray.cols;//图片的宽度
int step = src_gray.cols * src_gray.channels();
for (int i = 0; i < height; i++)
{
uchar* data = src_gray.ptr<uchar>(i);
uchar* mdData = modifyImg.ptr<uchar>(i);
for (int j = 0; j < step; j++)
{
mdData[j] = saturate_cast<uchar>(255 - data[j]);
}
}·
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "使用指针处理单通道图像运行时间为:" << time0 << "秒" << endl;
namedWindow("例9_5修改图", 0);
imshow("例9_5修改图", modifyImg);
waitKey(0);
return 0;
}
可以看出,采用指针和查找表的方式访问和修改像素值是比较快的方式。尤其是查找表,速度极快。对应上面例子的图像比较大,为3120X4160大小。由于担心初学者运行不出结果,所以每次小改动都放上了全部代码,毕竟十几年前我也是这么过来的。
除了上面几种方式之外,还有采用迭代器的方式以及操作连续图像块(isContinuous())的方式。其中迭代器的方式是最慢的,这里就不再举例,isContinuous()方式需要图像是连续存储的。在此也不再说明。有兴趣的可以自己试一试。