前言
在大多数图像处理任务中,我们需要扫描图像的所有像素才能执行计算,由于需要访问大量像素,我们必须以高效的方法进行扫描。本节我们将介绍如何使用指针实现高效扫描图像的方法。我们通过完成减少图像中的颜色数量这一任务来说明图像扫描过程。
用指针扫描图像
彩色图像由三通道像素组成,这些通道中的每一个都对应于红色、绿色和蓝色三种基色之一的强度值。由于这些像素值都是 8
位无符号字符,因此颜色总数为 256 x 256 x 256
,超过 1600
万种颜色。因此,为了降低分析的复杂性,减少图像中颜色的数量通常是有效的。实现此目标的一种方法是将 RGB
空间细分为大小相等的立方体。例如,如果我们将每个维度中的颜色数量减少为原来的 1/8
,那么可以得到共 32 x 32 x 32
种颜色。此时,原始图像中的每种颜色都会在新的颜色空间中分配一个新的颜色值,该值等于原始颜色值所属的立方体中心的值。
因此,基本的色彩量化(色彩量化即为减少图像中颜色数量的过程)算法很简单。如果 N
是缩减因子,则对于图像中的每个像素和该像素的每个通道,将值除以 N
(使用整数除法,舍弃余数);然后,将结果乘以 N
,此时获得的值与输入像素值之间的差值为 N
的倍数,然后,只需添加 N/2
即可获得 N
的两个相邻倍数间的中心位置。如果对每个 8
位通道值重复此过程,将获得共 256/N x 256/N x 256/N
个可能的颜色值。
1. 减色函数的签名如下,函数需要提供图像和每个通道的缩减因子 div
作为参数:
void colorReduce(cv::Mat image, int div=64);
复制代码
此函数使用原地处理,即输入图像的像素值被函数修改。
2. 只需创建一个遍历所有像素值的双循环即可完成处理。第一个循环扫描每一行,获取行图像数据的指针:
代码语言:javascript复制for (int j=0; j<image.rows; j ){
// 获取行的地址
uchar* data=image.ptr<uchar>(j);
复制代码
3. 第二个循环遍历行指针的每一列,并使用上述方法减少颜色:
代码语言:javascript复制 for (int i=0; i<nc; i ){
// 处理每个像素
data[i] = data[i]/div*div div/2
}
}
复制代码
通过加载图像并调用 colorReduce
函数来测试该函数:
// 读取图像
image= cv::imread("1.png");
// 处理图像
colorReduce(image,64);
// 展示图像
cv::namedWindow("Image");
cv::imshow("Image",image);
复制代码
编译并执行程序,可以得到以下结果:
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
前言
在大多数图像处理任务中,我们需要扫描图像的所有像素才能执行计算,由于需要访问大量像素,我们必须以高效的方法进行扫描。本节我们将介绍如何使用指针实现高效扫描图像的方法。我们通过完成减少图像中的颜色数量这一任务来说明图像扫描过程。
用指针扫描图像
彩色图像由三通道像素组成,这些通道中的每一个都对应于红色、绿色和蓝色三种基色之一的强度值。由于这些像素值都是 8
位无符号字符,因此颜色总数为 256 x 256 x 256
,超过 1600
万种颜色。因此,为了降低分析的复杂性,减少图像中颜色的数量通常是有效的。实现此目标的一种方法是将 RGB
空间细分为大小相等的立方体。例如,如果我们将每个维度中的颜色数量减少为原来的 1/8
,那么可以得到共 32 x 32 x 32
种颜色。此时,原始图像中的每种颜色都会在新的颜色空间中分配一个新的颜色值,该值等于原始颜色值所属的立方体中心的值。
因此,基本的色彩量化(色彩量化即为减少图像中颜色数量的过程)算法很简单。如果 N
是缩减因子,则对于图像中的每个像素和该像素的每个通道,将值除以 N
(使用整数除法,舍弃余数);然后,将结果乘以 N
,此时获得的值与输入像素值之间的差值为 N
的倍数,然后,只需添加 N/2
即可获得 N
的两个相邻倍数间的中心位置。如果对每个 8
位通道值重复此过程,将获得共 256/N x 256/N x 256/N
个可能的颜色值。
1. 减色函数的签名如下,函数需要提供图像和每个通道的缩减因子 div
作为参数:
void colorReduce(cv::Mat image, int div=64);
复制代码
此函数使用原地处理,即输入图像的像素值被函数修改。
2. 只需创建一个遍历所有像素值的双循环即可完成处理。第一个循环扫描每一行,获取行图像数据的指针:
代码语言:javascript复制for (int j=0; j<image.rows; j ){
// 获取行的地址
uchar* data=image.ptr<uchar>(j);
复制代码
3. 第二个循环遍历行指针的每一列,并使用上述方法减少颜色:
代码语言:javascript复制 for (int i=0; i<nc; i ){
// 处理每个像素
data[i] = data[i]/div*div div/2
}
}
复制代码
通过加载图像并调用 colorReduce
函数来测试该函数:
// 读取图像
image= cv::imread("1.png");
// 处理图像
colorReduce(image,64);
// 展示图像
cv::namedWindow("Image");
cv::imshow("Image",image);
复制代码
编译并执行程序,可以得到以下结果:
在彩色图像中,图像数据缓冲区的前三个字节分别用于表示左上角像素的三色通道 (BGR
通道);接下来的三个字节是第一行的第二个像素的三色通道值,依此类推。因此,宽度为 W
、高度为 H
的图像需要 WxHx3uchars
的内存块。但是,出于效率原因,一行图像元素可以填充一些额外的像素,这是因为某些多媒体处理器芯片(例如 Intel MMX
架构)在图像行像素数为 4
或 8
的倍数时可以更有效地处理图像,这些额外的像素并不会被显示或保存;它们的确切值会被忽略。