#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
using namespace std;
using namespace cv;
// 访问Mat中每个像素的值
// https://blog.csdn.net/xiaowei_cqu/article/details/19839019
// https://blog.csdn.net/xiaowei_cqu/article/details/7771760
// 减小颜色空间 color space reduction
// 减小颜色空间的操作,这在一些识别之类的应用中会大大降低运算复杂度。
// 经典的Reduce Color的例子,即对图像中的像素表达进行量化。如常见的RGB24图像有
// 256×256×256中颜色,通过Reduce Color将每个通道的像素减少8倍至256/8=32种,则图像只有
// 32×32×32种颜色。假设量化减少的倍数是N,则代码实现时就是简单的value/N*N,
// 通常会再加上N/2以得到相邻的N的倍数的中间值,最后图像被量化为
// (256/N)×(256/N)×(256/N)种颜色。通过定义:
// middle = 64 / 2
// 0 ~ 63 范围的像素值为 0 + middle
// 64 ~ 127 范围的像素值为 64 + middle
// 128 ~ 191 范围的像素值为 128 + middle
// 192 ~ 255 范围的像素值为 192 + middle
// 这样的操作将颜色取值降低为 64 * 64 * 64 种情况。这个操作可以用到一个简单的公式:
// iamge_new = (image_old / div) * div + middle
class ColorReduce
{
typedef Mat_<Vec3b>::iterator iterator;
public:
static void colorReduce(Mat& image, int div = 64);
private:
static void Continuous(Mat& image, int& rows, int& cols); // 矩阵内存分布连续测试
static void TravelPixels(Mat& image, int rows, int cols, int div);
};
void ColorReduce::Continuous(Mat& image, int& rows, int& cols)
{
if (image.isContinuous())
{
cols *= rows;
rows = 1;
}
}
void ColorReduce::colorReduce(Mat& image, int div)
{
CV_Assert(image.depth() != sizeof(uchar));
if (!div) return;
int rows = image.rows, cols = image.cols * image.channels();
Continuous(image, rows, cols);
TravelPixels(image, rows, cols, div);
}
void ColorReduce::TravelPixels(Mat& image, int rows, int cols, int div)
{
if (image.data == nullptr) return;
uchar* data = nullptr;
iterator i = image.begin<Vec3b>();
iterator e = image.end<Vec3b>();
switch (image.channels())
{
case 1:
for (int j = 0; j < rows; ++j) // using .ptr和[]操作符
{ // Mat最直接的访问方法是通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值
data = image.ptr<uchar>(j);
if (data == nullptr) break;
for (int i = 0; i < cols; ++i)
data[i] = (data[i] / div) * div + (div >> 1);
}
break;
case 3:
for (; i != e; ++i) // using Mat_ iterator 遍历安全, 但速度较慢, 官方推荐
{
(*i)[0] = ((*i)[0] / div) * div + (div >> 1); // blue
(*i)[1] = ((*i)[1] / div) * div + (div >> 1); // green
(*i)[2] = ((*i)[2] / div) * div + (div >> 1); // red
}
break;
default:
break;
}
}
int main()
{
Mat image = cv::imread("test.jpg", IMREAD_COLOR);
if (image.empty())
{
cout << "failed to read image" << endl;
return -1;
}
int div = 4;
ColorReduce::colorReduce(image, div);
if (image.type() == CV_8UC3)
{
if (image.empty())
return -1;
imshow("color", image);
Mat gray;
cvtColor(image, gray, COLOR_RGB2GRAY);
if (gray.empty())
return -1;
if(gray.type() == CV_8UC1)
imshow("gray", gray);
waitKey(0);
}
return 0;
}