opencv 学习: 06 使用指针遍历像素,以均匀颜色量化为例
在大多数图像处理任务中,你需要遍历所有像素,来完成计算。
由于像素数量巨大,通常需要高效的方法。
指针和迭代器是两种高效的遍历方式,这里先介绍下基于指针的方式。
下面以减少彩色图片中的颜色数量为例,来演示图片像素遍历。
彩色图片的像素由RGB三个通道组成。每个通道如果以 8 bit 来表示的话,总共可以合成 2^8 * 2^8 * 2^8 = 256 * 256 * 256 = 16,777,216 种颜色。
一般情况下,图像处理不需要区分处理如此丰富的色彩。因此,为了降低图像分析的复杂度,可以减少色彩数量。甚至也可以减少纳入计算的像素数。
一个简单的处理方式是 均匀颜色量化(Uniform Color Quantization)。
1.均匀颜色量化
1.1 方法解释:
- RGB空间划分:
将RGB颜色空间划分为大小相等的立方体(cubes)。RGB颜色空间是一个三维空间,每个维度代表红、绿、蓝三种颜色分量,范围都是0-255。 - 减少颜色数量:
原始图像中的每种颜色都会被映射到它所属立方体的中心颜色值。这意味着所有落在同一立方体内的颜色都会变成同一种颜色(该立方体中心的颜色)。
假设每个维度的颜色数量减少8倍,也就是说:
- 原本每个维度有256个可能的值(0-255)
- 减少8倍后,每个维度只有32个值(256/8 = 32)
- 总共的颜色数变为:32 × 32 × 32 = 32,768种颜色
1.2 方法特点:
优点:
- 实现简单直观
- 计算效率高
- 颜色分布均匀
缺点:
- 可能导致某些重要颜色丢失(特别是人眼敏感的颜色区域)
- 不考虑图像实际的颜色分布情况
- 在颜色过渡平滑的区域可能会出现明显的色带现象
这是一种基础但常用的颜色量化技术,在图像处理和计算机视觉中经常用来减少图像的色彩复杂度,便于后续处理或压缩图像文件大小。
2.实现方法
1.将每个像素,每个通道的颜色值 除以 缩小倍数 ,取商;再乘以 缩小倍数;最后加上 1/2 的倍数值。
2.代码中,拿到每行的数据起始地址,对行上的每个像素的每个通道分量进行遍历,计算。
3.完整代码:
#include <iostream>
#include <opencv2/opencv.hpp>// 颜色
void colorReduce(cv::Mat/*&*/ image, int div)
{int row, col;// 每行图像的数据元素个数size_t colsdataCnt = image.cols * image.channels();for (size_t i = 0; i < image.rows; i++){// 得到第 i 行的数据指针uchar* data = image.ptr<uchar>(i);for(size_t j = 0; j < colsdataCnt; j++){// 处理第 i 行第 j 列 像素数据 // 不用区分灰度图和彩色图片,每次移动一个通道的数据字段data[j] = data[j] / div * div + div / 2;}}
}int main(int argc, char *argv[])
{// 检查命令行参数if (argc != 4){std::cerr << "Usage: " << argv[0] << " <input_image> <colorReduce> <output_image>" << std::endl;return -1;}// 读取输入图像和logo图像cv::Mat input_image = cv::imread(argv[1]);// 检查输入图像和logo图像是否成功读取if (input_image.empty()){std::cerr << "Error: Could not open or find input image" << std::endl;}cv::namedWindow("input_image", cv::WINDOW_NORMAL);cv::imshow("input_image", input_image);cv::waitKey(0);int reduce = std::stof(argv[2]);if (reduce < 0 || reduce > 255){std::cerr << "Error: colorReduce must be between 0 and 255" << std::endl;return -1;}colorReduce(input_image, reduce);cv::imwrite(argv[3], input_image);cv::namedWindow("output_image", cv::WINDOW_NORMAL);cv::imshow("output_image", input_image);cv::waitKey(0);return 0;
}
减少8倍时,基本上看不出区别:

减少 64 倍:

减少128倍:也就是每个通道 2^8 / 2^7 = 2 , 三个通道 2 * 2 * 2 = 8 种颜色

