PotatoPie 4.0 实验教程(25) —— FPGA实现摄像头图像直方图均衡变换-Anlogic-安路论坛-FPGA CPLD-ChipDebug

PotatoPie 4.0 实验教程(25) —— FPGA实现摄像头图像直方图均衡变换

手机扫码

20240416075513933-1713225291635

链接直达

https://item.taobao.com/item.htm?ft=t&id=776516984361

图像的直方图均衡是什么?

图像的直方图均衡是一种用于增强图像对比度的图像处理技术。在直方图均衡中,图像的像素值被重新分配,以使得图像的直方图变得更均匀,即各个像素值的分布更加平衡。这意味着直方图中每个像素值的频率大致相同,从而使得图像的对比度增强。

直方图均衡可以应用于灰度图像和彩色图像,并且通常用于图像增强、图像预处理以及计算机视觉应用中。它有以下几个主要作用和优势:

  1. 增强对比度:直方图均衡可以增强图像的对比度,使得图像中的细节更加清晰、突出。通过重新分配像素值,可以拉伸直方图,使得图像中的像素值范围更广,从而增加了图像的动态范围。

  2. 消除背景噪声:直方图均衡可以帮助消除图像中的背景噪声,提高图像的质量。通过增强图像的对比度,可以更好地区分目标与背景,减少背景噪声的影响。

  3. 提高图像质量:直方图均衡可以改善图像的视觉质量,使图像更加清晰、生动,提高了图像的观赏性和识别性。

  4. 改善图像分割和特征提取:直方图均衡可以使得图像中不同目标的灰度级别更加明显,有利于图像分割和特征提取。这对于后续的图像分析、目标检测和识别等任务非常重要。

  5. 预处理步骤:直方图均衡通常作为图像预处理的一部分,用于提高后续图像处理算法的性能和准确性。例如,在图像识别、目标跟踪和计算机视觉任务中,对图像进行直方图均衡可以改善算法的鲁棒性和准确率。

总的来说,直方图均衡是一种简单而有效的图像增强技术,可以提高图像的质量和可用性,使得图像在各种应用领域中都能取得更好的效果。

直方图均衡算法的步骤

直方图均衡的步骤通常包括以下几个阶段:

  1. 计算图像的灰度直方图:统计图像中每个灰度级别的像素数量。
  2. 计算累积分布函数(CDF):对灰度直方图进行归一化处理,得到像素值的累积分布函数,该函数描述了每个灰度级别在图像中出现的累积概率。
  3. 根据CDF进行像素值映射:使用累积分布函数对图像的像素值进行重新映射,以使得图像的直方图更加均匀。通常情况下,这涉及到将原始像素值映射到新的像素值,以便在直方图中实现更均匀的分布。
  4. 应用像素值映射:根据映射关系,将图像中的每个像素值替换为对应的新值,从而完成直方图均衡化。

python实现图像的直方图均衡变换源码

这段代码实现了图像的直方图均衡化,并使用 Matplotlib 库在 Python 中进行可视化展示。以下是对代码功能的详细说明:

  1. 导入必要的库

    • os:用于处理文件路径。
    • numpy:用于数组操作和数学计算。
    • cv2:OpenCV 库,用于图像处理。
    • matplotlib.pyplot:用于绘制图像和直方图。
  2. 获取图像路径

    • 使用 os.path.dirnameos.path.abspath 函数获取当前 Python 脚本所在目录的路径。
    • 使用 os.path.join 函数构造图像文件的完整路径。
  3. 读取图像

    • 使用 OpenCV 的 cv2.imread 函数读取图像。
  4. 将图像转换为灰度图像

    • 使用 OpenCV 的 cv2.cvtColor 函数将彩色图像转换为灰度图像。
  5. 计算直方图

    • 创建一个长度为 256 的数组 histogram,用于存储灰度级别的像素数量。
    • 使用双重循环遍历图像的每个像素,并在 histogram 中累计每个灰度级别的像素数量。
  6. 计算累积分布函数

    • 创建一个长度为 256 的数组 cumulative_distribution,用于存储每个灰度级别的累积分布函数值。
    • 使用双重循环遍历直方图,计算每个灰度级别的累积像素数量,并将其除以总像素数得到累积分布函数值。
  7. 计算直方图均衡化的灰度值映射表

    • 创建一个长度为 256 的数组 LUT,用于存储直方图均衡化后的灰度值映射表。
    • 将累积分布函数值乘以 255 并四舍五入,得到灰度值映射表。
  8. 直方图均衡化

    • 创建一个与原始图像相同大小的数组 image_equal,用于存储直方图均衡化后的图像。
    • 使用双重循环遍历原始图像的每个像素,根据灰度值映射表将每个像素的灰度值替换为均衡化后的灰度值。
  9. 可视化

    • 使用 Matplotlib 的 plt.imshowplt.bar 函数分别显示原始图像和其直方图。
    • 使用 Matplotlib 的 plt.plot 函数绘制累积分布函数曲线。
    • 使用 Matplotlib 的 plt.show 函数显示图像及其直方图的子图布局。

通过以上步骤,代码实现了直方图均衡化并可视化显示了原始图像、均衡化后的图像、直方图以及累积分布函数。

20240422085208313-image

MATLAB实现图像的直方图均衡变换源码

上面的代码实现了图像的直方图均衡化,具体步骤如下:

  1. 读取图像并转换为灰度图像: 使用imread函数读取名为’dog.png’的图像,并使用rgb2gray函数将彩色图像转换为灰度图像。

  2. 计算直方图: 首先创建一个256×1的零矩阵histogram,用于存储灰度级别的直方图。然后使用嵌套的循环遍历图像的每个像素点,将每个灰度级别出现的频数累加到相应的直方图位置上。

  3. 计算累积分布函数: 创建一个256×1的零矩阵cumulative_distribution,用于存储累积分布函数的值。然后通过循环计算累积分布函数的值,其中使用变量sum累积直方图的频数,并将其除以图像的总像素数(行数乘以列数)得到归一化后的累积分布函数。

  4. 计算灰度值映射表: 创建一个256×1的零矩阵LUT,用于存储直方图均衡化后的灰度值映射表。然后通过循环遍历累积分布函数,对每个灰度级别的累积分布函数值乘以255并四舍五入,得到灰度值映射表。

  5. 直方图均衡化: 创建一个与原始图像大小相同的零矩阵image_equal,用于存储直方图均衡化后的图像。然后通过嵌套的循环遍历原始图像的每个像素点,根据灰度值映射表将每个像素点的灰度值替换为对应的直方图均衡化后的灰度值。

  6. 显示结果: 使用subplot函数将原始图像、直方图、直方图均衡化后的图像以及其直方图和累积分布图显示在一个图像窗口中。标题使用中文显示,并指定使用微软雅黑字体。

    20240422084050961-image

工程分析

工程层次图

20240422141912137-image

demo18相比,只是多了一个img_histogram的模块,也就是下面这一段代码,在从SDRAM读出来之后,经它处理后再输出hdmi_tx模块。

img_histogram u_img_histogram
(
    .i_clk          (clk_pixel                ),
    .i_rst_n        (sys_rst_n                ),
    .i_hs           (VGA_HS                   ),
    .i_vs           (VGA_VS                   ),
    .i_de           (VGA_DE                   ),
    .i_x_pos        (lcd_xpos                 ),
    .i_y_pos        (lcd_ypos                 ),
    .o_hs           (histogram_hs             ),
    .o_vs           (histogram_vs             ),
    .o_de           (histogram_de             ),
    .o_r            (histogram_r              ),
    .o_g            (histogram_g              ),
    .o_b            (histogram_b              ) 
);

img_histogram直方图均衡模块源代码分析

20240422141936638-image

从层次图可到这个模块包含两个子ROM模块, 上面这个img_histogram ROM是我们要做直方图均衡的图像数据,dog.png就存在这里,为什么这个实验没有直接对摄像头进行处理,因为直方图需要大量的RAM,而EG4的RAM没有这么大,所以我们用ROM存一个小图来验证这个算法。

下面那个u_img_equal_ram_dp 是用来存储直方图映射表。

接下来我们讲述直方图均衡化的FPGA算法关键点:

1. 从ROM读取dog.png的图像数据

由于我们需要在指定位置处理像,而我们显示屏大于图像的尺寸,因此需要行场计数到达指定位置时才从ROM读取图像数据。

我们用这几个参数定义显示处理图像的位置

parameter H_ACTIVE = 160; //显示区域宽度
parameter V_ACTIVE = 120; //显示区域高度
parameter BEGIN_X = 640; //显示起始坐标
parameter BEGIN_Y = 360; //显示起始坐标

然后分别进行/显示区域行计数和列计数,

if(h_cnt == H_ACTIVE - 1'b1)
			h_cnt <= 11'd0;
		else 
			h_cnt <= h_cnt + 11'd1;

..... 省略

		if(v_cnt == V_ACTIVE - 1'b1)
			v_cnt <= 11'd0;
		else 
			v_cnt <= v_cnt + 11'd1;

往下看就看到代码定义ROM的地方了

//存储dog.png图片数据的ROM
dog_160x120 u_image_buffer( 
	.doa	({r_d0,g_d0,b_d0}), 
	.addra	(rd_addr		 ), 
	.clka	(i_clk			 )
);

接下来的这行代码处理了图像数据ROM的地址计数。

rd_addr <= v_cnt * H_ACTIVE + h_cnt;

2.图像的灰度化

3.计算直方图

跟matlab和python代码一样,统计一帧图像数据中每个灰度出现的次数

hist_ram[gray_d0[15:8]]<=hist_ram[gray_d0[15:8]]+1'b1;

4.计算累积分布函数建立映射表

5. 将计算结果写映射表的ROM

例化双端RAM用于存储映射表

// 存储直方图均衡化后的灰度值映射表,输出时直接用灰度值作为地址进行查表
ram_dp#(
	.DATA_WIDTH 	(8					),
	.ADDRESS_WIDTH  (8 				    )
) u_img_equal_ram_dp(
	.i_clk			(i_clk				),
	.i_data_a		(image_equal		),
	.i_addr_a		(wr_addr_cnt		),
	.i_wea			(sum_hist_flag_d3	),
	.o_qout_a		(					),
	.i_data_b		(					),
	.i_addr_b		(gray_d0[15:8]		),
	.i_web			(					),
	.o_qout_b		(hist_out			)
);

显示的时候,直接用灰度值作为地址进行查表读出值即可。

管脚约束

PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

时序约束

PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

实验结果

20240422140247204-image

请登录后发表评论

    没有回复内容