PotatoPie 4.0 实验教程(22) —— FPGA实现摄像头图像对数(log)变换-Anlogic-安路论坛-FPGA CPLD-ChipDebug

PotatoPie 4.0 实验教程(22) —— FPGA实现摄像头图像对数(log)变换

手机扫码

20240416075513933-1713225291635

链接直达

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

什么是图像的log变换?

总的来说,对数变换是一种常用的图像增强技术,可以改善图像的视觉质量、减少噪声以及突出图像中的细节,从而提高图像在视觉感知和分析中的效果和可用性。

图像的对数变换(log transformation)是一种用于增强图像对比度的常见方法之一。它通过对图像的像素值取对数来扩展较低亮度的区域,并压缩较高亮度的区域,从而增强图像的细节和对比度。

对数变换的主要特点是可以扩展图像的暗部细节,使得低亮度区域的像素值被映射到较大的范围内,从而提高了图像的对比度。然而,对数变换对于较亮的区域的影响相对较小,可能会导致亮度信息的丢失。

对数变换常用于图像处理中的动态范围压缩、图像增强、去噪等应用中。

为什么要做图像的log变换?

对图像进行对数变换的主要目的有几个:

  1. 增强对比度:对数变换可以扩展图像的灰度动态范围,将较低灰度值的区域拉伸,同时压缩较高灰度值的区域,从而增强图像的对比度。这对于某些图像来说,特别是那些灰度范围较窄的图像,可以使图像更具视觉效果。

  2. 压缩动态范围:在一些应用中,图像的动态范围可能过大,包含了大量的细节信息。对数变换可以帮助压缩图像的动态范围,使得图像中的细节更容易被观察和分析。

  3. 减少噪声:在图像处理中,对数变换也可以用于减少图像中的噪声。由于对数函数的曲线特性,对低灰度值的像素施加更大的变换,因此可以将低灰度值的噪声信号压缩到较小的范围内,从而减少图像中的噪声影响。

  4. 增强图像细节:对数变换有助于突出图像中的细节和纹理,特别是对于暗部细节。这使得图像在观察和分析时更容易捕捉到细微的变化和特征。

log变换的公式

对于灰度图像,图像的对数变换公式如下:

20240406184404292-image

其中, 表示原始图像的像素值, 表示变换后的像素值, 是一个常数,用于调节变换后的亮度范围。其中 c 为常数系数, r 为像素值范围 0~255

20240413183032618-image

(生成这个交互式曲线图的python代码在文章的后面有提供)

如图所示, 对数曲线在像素值较低的区域斜率较大, 像素值较高的区域斜率比较低, 所以图像经过对数变换之后, 在较暗的区域对比度将得到提升, 因而能增强图像暗部的细节。

python实现图像的log变换

这段代码会读取名为 Lena.jpg 的图片文件,将其转换为灰度图像,并应用 log 变换。然后,它会显示原始图像、灰度图像和经过 log 变换后的图像。

以下是代码的详细说明:

  1. 导入必要的库:

    import cv2
    import numpy as np
    import os
    • cv2:用于图像处理和显示。
    • numpy:用于数值计算。
    • os:用于操作文件路径。
  2. 获取当前文件的路径并提取目录:

    current_file_path = __file__
    current_file_dir = os.path.dirname(current_file_path)

    使用 __file__ 获取当前脚本文件的路径,然后通过 os.path.dirname() 函数提取目录部分,并将结果存储在 current_file_dir 变量中。

  3. 读取图片:

    img = cv2.imread(current_file_dir+'/Lena.jpg')

    使用 cv2.imread() 函数读取名为 ‘Lena.jpg’ 的彩色图像文件,并将其存储在名为 img 的变量中。

  4. 将图像转换为灰度图像:

    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    使用 cv2.cvtColor() 函数将彩色图像 img 转换为灰度图像,并将结果存储在名为 gray_img 的变量中。

  5. 定义常数系数 c

    c = 30

    定义常数系数 c,用于对数变换中的缩放。

  6. 对每个像素进行对数变换:

    log_img = c * np.log(gray_img + 1)

    使用 NumPy 库中的 np.log() 函数对灰度图像 gray_img 进行对数变换,并乘以常数系数 c。为了避免对数值为零的情况,将灰度值加一。

  7. 将浮点型数据转换为 uint8 类型:

    log_img = np.uint8(log_img)

    将对数变换后的图像数据类型从浮点型转换为无符号 8 位整数型,以便于后续显示。

  8. 显示图像:

    cv2.imshow('Original Image', img)
    cv2.imshow('Gray Image', gray_img)
    cv2.imshow('Log Transformed Image', log_img)

    使用 cv2.imshow() 函数显示原始图像、灰度图像和对数变换后的图像。

  9. 等待按下任意键继续并关闭窗口:

    cv2.waitKey(0)
    cv2.destroyAllWindows()

    使用 cv2.waitKey(0) 函数等待用户按下任意键,然后使用 cv2.destroyAllWindows() 函数关闭所有打开的窗口。

可以看到图像的暗部被明显提升了。

20240414104535181-image

matlab实现实现图像的log变换

这段 MATLAB 代码实现了相同的功能,包括读取图像、将图像转换为灰度图像、应用对数变换以及显示原始图像、灰度图像和对数变换后的图像。

以下是代码的详细说明:

  1. 获取当前脚本文件的路径:

    current_file_path = mfilename('fullpath');
    [current_file_dir, ~, ~] = fileparts(current_file_path);

    使用 mfilename('fullpath') 函数获取当前脚本文件的完整路径,然后使用 fileparts() 函数提取目录部分,并将结果存储在 current_file_dir 变量中。

  2. 读取图像:

    img = imread(fullfile(current_file_dir, 'Lena.jpg'));

    使用 imread() 函数读取名为 ‘Lena.jpg’ 的图像文件,并将其存储在名为 img 的变量中。fullfile() 函数用于构建完整的文件路径。

  3. 将图像转换为灰度图像:

    gray_img = rgb2gray(img);

    使用 rgb2gray() 函数将彩色图像 img 转换为灰度图像,并将结果存储在名为 gray_img 的变量中。

  4. 定义常数系数 c

    c = 30;

    定义了一个常数系数 c,用于控制对数变换的幅度。

  5. 对每个像素进行对数变换:

    log_img = c * log(double(gray_img) + 1);

    使用 log() 函数对灰度图像中的每个像素值进行对数变换,并乘以常数系数 cdouble() 函数用于将灰度图像转换为双精度浮点型数据,+ 1 是为了避免对数运算中出现零值。

  6. 将浮点型数据转换为 uint8 类型:

    log_img = uint8(log_img);

    将经过对数变换的图像数据转换为 uint8 类型,以便正确显示图像。

  7. 显示原始图像、灰度图像和对数变换后的图像:

    imshow(img);
    title('Original Image');
    figure;

    imshow(gray_img);
    title('Gray Image');
    figure;

    imshow(log_img);
    title('Log Transformed Image');

    使用 imshow() 函数分别显示原始图像、灰度图像和对数变换后的图像,并使用 title() 函数设置图像的标题。figure 函数用于创建新的图像窗口,以便将多个图像显示在不同的窗口中。

20240415114547323-image

FPGA工程解析

工程数据流图

工程层次图

20240407132249143-image

代码解析

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

img_log u_img_log
(
    .i_clk(clk_pixel),
    .i_rst_n(sys_rst_n),
    .i_hs(VGA_HS),
    .i_vs(VGA_VS),
    .i_de(VGA_DE),
    .i_r(VGA_RGB[23:16]),
    .i_g(VGA_RGB[15:8]),
    .i_b(VGA_RGB[7:0]),        
    .o_hs(log_hs),
    .o_vs(log_vs),
    .o_de(log_de),   
    .o_r(log_data[23:16]),
    .o_g(log_data[15:8]),
    .o_b(log_data[7:0])
);

对于FPGA做log运算最好的办法是我们先用matlab或者python把 c*log(0~255)的值计算并输出到文本文件中,然后填入log_rom.v这个文件中即可。下面的代码提供了直接输出log_rom.v的功能,分别提供了pyton版本和matlab版本,代码中c取30

pyton版本

matlab版代码

% 定义灰度范围的数量
num = 256;

% 定义log变换的参数
c = 30;

% 计算log变换后的值并写入 COE 文件
fid = fopen('log_para_256.coe', 'w');
fprintf(fid, 'memory_initialization_radix = 10;\n');
fprintf(fid, 'memory_initialization_vector =\n');

% 计算并写入每个地址对应的log值
for n = 0:num-1
    log_value = round(c * log(1 + n));
    fprintf(fid, '%d,\n', log_value);
end

% 写入最后一个值并结束
log_value = round(c * log(1 + num - 1));
fprintf(fid, '%d;', log_value);
fclose(fid);

% 计算灰度范围和log变换后的图像
f = 0:num-1;
g = c * log(1 + f);

% 绘制图像
plot(f, 'LineWidth', 1.5, 'DisplayName', 'Gray Scale (f)');
hold on;
plot(g, 'LineWidth', 1.5, 'DisplayName', 'Log Transformed (g)');
hold off;
xlabel('Gray Scale (f)');
ylabel('Log Transformed (g)');
title('Gray Scale vs. Log Transformed');
legend('show');
grid on;

此 MATLAB 代码将执行与Python版代码相同的操作,并生成相同的 COE 文件和图像。

20240415213947565-image

因此FPGA中只需要读出log的值即可,

log_rom u_r
(
	.clka	(i_clk		),
	.addra	(i_r		),
	.ena	(1'b1		),
	.douta	(o_r		)
);

由于是纯组合逻辑,行场信号都不需要延一拍直接输出即可。

assign o_hs = i_hs;
assign o_vs = i_vs;
assign o_de = i_de;

管脚约束

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

时序约束

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

实验结果

还是以咱们的小金猪作为主角。

20240406203712555-image

请登录后发表评论

    没有回复内容