1.前言
为了提高性能,可以在图像的每行末尾用额外的像素填充到某个数字的整数倍,例如8,图像处理的性能可能会提高,因此最好根据内存配置情况将数据对齐。若去掉填充后,图像仍可看作是W*H像素的长一维数组。
//检查矩阵是否连续
//检查行的长度(字节数)与“列的个数*单个像素的字节数”是否相等
image.step == image.cols * image.elemSize();
还有另一种方式也可以检查图像矩阵是否被填充:
cv::Mat的 isContinuous
利用这些,在出一些特殊的处理算法中,我们就可以充分利用图像的连续性,在单个循环内来处理图像。
2. 代码
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <random>
#include <iostream>
//减色函数
void reduceColor(cv::Mat image, int n);
int main(int argc, char** argv)
{
cv::Mat image = cv::imread("15011.jpg");
if (image.empty())
{
std::cout << "\n Durn, couldn't read image filename " << std::endl;
return 1;
}
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
auto startTime = std::chrono::system_clock::now();
reduceColor(image, 32);
auto endTime = std::chrono::system_clock::now();
std::cout << "time:" << std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count() << std::endl;
cv::namedWindow("ReducedColr Image");
cv::imshow("ReducedColr Image", image);
cv::waitKey(0);
return 0;
}
//image: 输入图像
//div : 减色因子
void reduceColor(cv::Mat image, int div)
{
if (image.isContinuous()) //判断图像是否被填充
{
//对矩阵的行和列进行重组
image = image.reshape(1, //通道数
1); //行数
}
int rows = image.rows; //行,高度
int cols = image.cols; //列,宽度
int allCols = cols * image.channels();//每行的元素总数
double nTemp = std::log(static_cast<double>(div)) / std::log(2.0); //求解给定div下的n值 div = pow(2, n);
int n = static_cast<int>(nTemp + 0.5); //0.5相当于四舍五入
//掩码,求div的倍数
uchar mask = 0xFF << n;
//对每个元素进行遍历
for (int i = 0; i < rows; i++) //行遍历
{
uchar* data = image.ptr<uchar>(i); //获取行的首地址
for (int j = 0; j < allCols; j++) //列遍历
{
*data &= mask; //掩码运算
*data++ += div >> 1;
}
}
}
注:opencv中的reshape之后,似乎可以不用再reshape回来,因为结果可以直接显示。
3.低层次指针算法
从图像的起点开始循环:
uchar* data = image.data;
从一行移动到下一行
data += image.step;//下一行
通过下面的方法得到第j行第i列的像素的地址:
data = image.daa + j * image.step + i * image.elemSize();
但是并不推荐使用这种方式。