通过访问图像像素减少图像颜色数量

前言

由于你可以自己看下面的内容,所以没有前言。

该测试代码实现的功能是减少图像种颜色的数量,将256×256×256转换成26×26×26从而提高程序运行速度。实现该功能需要用到访问图像像素的函数和计时的函数。
效果图:


测试代码:

一、main函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <time.h>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

void colorReduce(Mat& inputImage,Mat& outputImage,int div);

int main(){
Mat srcImage = imread("1.jpg");
imshow("srcImage",srcImage);

Mat dstImage;
dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());

double time0 = static_cast<double>(getTickCount());

colorReduce(srcImage,dstImage,32);

time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "Time: " << time0 << "s" << endl;

imshow("dstImage",dstImage);
waitKey(0);
}

1.计时函数

getTickCount()函数返回CPU自某个事件以来走过的时钟周期数
getTickFrequency()函数返回CPU一秒钟走过的时钟周期数
两者组合如下:

1
2
3
double time0 = static_cast<double>(getTickCount());
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "Time: " << time0 << "s" << endl;

即可实现对colorReduce()函数的计时。

2.static_cast函数

强制类型转换,即在程序员知情的情况下进行转换,系统不会报错。

二、实现colorReduce()函数的三种方法

1.指针访问

函数代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void colorReduce(Mat& inputImage,Mat& outputImage,int div){
outputImage = inputImage.clone();
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols * outputImage.channels();

for(int i = 0; i < rowNumber; i++)
{
uchar* data = outputImage.ptr<uchar>(i);
for(int j = 0; j < colNumber; j++)
{
data[j] = data[j] / div * div + div / 2;
}
}
}

Mat类中的ptr函数
可以返回第i行的首地址,如:
1
uchar* data = outputImage.ptr<uchar>(i);

2.迭代器iterator

函数代码:

1
2
3
4
5
6
7
8
9
10
11
12
void colorReduce(Mat& inputImage,Mat& outputImage,int div){
outputImage = inputImage.clone();
Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();
Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();

for(;it != itend; ++it)
{
(*it)[0] = (*it)[0] / div * div + div / 2;
(*it)[1] = (*it)[1] / div * div + div / 2;
(*it)[2] = (*it)[2] / div * div + div / 2;
}
}

STL 迭代器嘻嘻嘻…自己找

3.动态地址计算

函数代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void colorReduce(Mat& inputImage,Mat& outputImage,int div){
outputImage = inputImage.clone();
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols;

for(int i = 0; i < rowNumber; i++)
{
for(int j = 0; j < colNumber; j++)
{
outputImage.at<Vec3b>(i,j)[0] = outputImage.at<Vec3b>(i,j)[0] / div * div + div / 2;
outputImage.at<Vec3b>(i,j)[1] = outputImage.at<Vec3b>(i,j)[1] / div * div + div / 2;
outputImage.at<Vec3b>(i,j)[2] = outputImage.at<Vec3b>(i,j)[2] / div * div + div / 2;
}
}
}

Mat类中的at函数可以读取和修改图像矩阵中对应坐标的元素,但是必须在编译时知道图像的数据类型,at函数本身不会进行任何数据类型的转换,因此需要确保我们指定的数据类型要和图像矩阵中的数据类型相同。
一般形式:
1
image.at<Vec3b>(i,j)[channel] = value;

在本程序给出的图像是三通道的,因此是Vec3b,表示三个八位数的向量。