core组件之操作图像中的像素

来源:互联网 发布:台式机显卡80度 知乎 编辑:程序博客网 时间:2024/05/16 05:04

图像处理的基础是遍历图像的每一个像素点,即图像扫描。首先,要对图像矩阵在内存中的存储方式稍有了解。

对于单通道灰度图像,它的存储方式是这样的:
单通道灰度图

对于多通道图像,是这样的:
三通道灰度图
(注:图片来自Google。如键入“单通道图像”,然后google图片就有啦)

在本节中,我们要对图像进行颜色空间缩减。借由颜色空间缩减来展示三种遍历图像的方式。那么,什么是”颜色空间缩减“呢?

我们知道,对于元素类型为 uchar 的单通道图像矩阵,每个像素点有 256 个灰度值;对于三通道图像,每个像素点的颜色种类达 16777216 种(256*256*256)。如此多的颜色可能会对算法性能造成严重影响,我们往往只需要颜色的一部分,也能满足要求,因此引入了颜色空间缩减。

颜色缩减可以由一个简单的函数实现

pixelNew = pixelOld / div * div;

比如int div=32,那么0~31范围的像素值转换为0,32~63范围的像素值转换为1,…,224~255范围的像素值转换为7。最终,经过颜色缩减后,256*256*256种情况则会降低为8*8*8种情况。

三种遍历方式

(注:在下面的遍历中,颜色缩减函数采用pixelNew = pixelOld / div * div + div/2)

1、下标遍历
void colorReduce(Mat& inputImg, Mat& outImg, int div){       outImg = inputImg.clone();       int rowNum = outImg.rows;       int colNum = outImg.cols*outImg.channels();//每一行的元素个数=列数*通道数       for (int i = 0; i < rowNum; ++i) {              uchar* pdata = outImg.ptr<uchar>(i);//取出第i行的首地址              for (int j = 0; j < colNum; ++j) {                     pdata[j] = pdata[j] / div*div + div / 2;//颜色缩减              }       }}
2、迭代器遍历
void colorReduce(Mat& inputImg, Mat& outImg, int div){       outImg = inputImg.clone();       Mat_<Vec3b>::iterator it = outImg.begin<Vec3b>();       while (it != outImg.end<Vec3b>()) {              //因为是三通道的              (*it)[0] = (*it)[0] / div*div + div / 2;              (*it)[1] = (*it)[1] / div*div + div / 2;              (*it)[2] = (*it)[2] / div*div + div / 2;              ++it;       }}
3、结合at()函数遍历
void colorReduce(Mat& inputImg, Mat& outImg, int div){       outImg = inputImg.clone();       int rowNum = outImg.rows;       int colNum = outImg.cols;       for (int i = 0; i < rowNum; ++i) {              for (int j = 0; j < colNum; ++j) {                     outImg.at<Vec3b>(i, j)[0] = outImg.at<Vec3b>(i, j)[0] / div*div + div / 2;                     outImg.at<Vec3b>(i, j)[1] = outImg.at<Vec3b>(i, j)[1] / div*div + div / 2;                     outImg.at<Vec3b>(i, j)[2] = outImg.at<Vec3b>(i, j)[2] / div*div + div / 2;              }       }}

测试:

#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>using namespace std;using namespace cv;void colorReduce(Mat& inputImg, Mat& outImg, int div);int main(){       Mat srcImg = imread("E:/image/dog.jpg");       imshow("srcImg", srcImg);       Mat dstImg;       double start = static_cast<double>(getTickCount());       colorReduce(srcImg, dstImg, 32);         start = (static_cast<double>(getTickCount()) - start) / getTickFrequency();       cout << "duration = " << start << endl;       imshow("dstImg", dstImg);       waitKey(0);       return 0;}

【注意】
以上每一个循环中都要计算一遍颜色缩减的函数,这是很费时的。正确的做法是,把颜色缩减的映射值先存在hashTable中,循环的时候直接去取就好了。

int div = 32;int hashTable[256] = { 0 };for (int i = 0; i < 256; ++i) {     hashTable[i] = i / div*div;//先把像素值存到hashTable中,到时候就不用每一个循环都计算一遍了,直接从hashTable中取就好了}

循环内的做法就是(以下标遍历为例子):

pdata[j] = hashTable[pdata[j]];

【LUT函数】
不过,以上只是为了说明图像遍历而写的例子,如果是实现批量图像元素查找和更改,则推荐使用OpenCV内置的LUT函数。LUT函数的使用需要首先初始化上面的hashTable。然后,

Mat lookupTable(1, 256, CV_8U);uchar *p = lookupTable.data;for (int i = 0; i < 256; ++i) {    p[i] = hashTable[i];}

LUT函数的定义

void LUT(InputArray src, InputArray lut, OutputArray dst);

测试:

#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>using namespace std;using namespace cv;int main(){       Mat srcImg = imread("E:/image/dog.jpg");       imshow("srcImg", srcImg);       Mat dstImg;       int div = 32;       int hashTable[256] = { 0 };       for (int i = 0; i < 256; ++i) {              hashTable[i] = i / div*div;       }       Mat lookupTable(1, 256, CV_8U);       uchar *p = lookupTable.data;       for (int i = 0; i < 256; ++i) {              p[i] = hashTable[i];       }       double start = static_cast<double>(getTickCount());       LUT(srcImg, lookupTable, dstImg);       start = (static_cast<double>(getTickCount()) - start) / getTickFrequency();       cout << "duration = " << start << endl;       imshow("dstImg", dstImg);       waitKey(0);       return 0;}

测试结果:
这里写图片描述