【盘古100Pro+开发板实验例程】FPGA学习 | gamma 变化 | 图像实验指导手册

本原创文章由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处(www.meyesemi.com)

1. 实验简介

实验目的:

生成 3x3 矩阵,完成高斯滤波。

实验环境:

Window11

PDS2022.SP6.4

Modelsim10.6c

MatlabR2023b

硬件环境:

MES2L676-100HP

2. 实验原理

Gamma 校正是基于人眼对亮度感知非线性的特性,人眼对亮度的敏感度随着亮度的增加而减少,也就是说,当图像的亮度较低时,人眼对亮度的变化更为敏感。

具体的公式为:

x 大于 1 时,图像变暗。

x 小于 1 时,图像变亮。

x 等于 1 时,图像不变。

同样的,我们会发现一个问题,该公式涉及到指数和除法,用 verilog 来实现的难度是比较大的,我们可以换个角度思考,本次的亮度调整的像素是 8bit,因此是 0-255,我们可以通过查找表的 方法来完成本次算法,x 是变量,是用来调节亮度的参数,Vin 的输入范围是 0-255,假设,x 为 2.2,我们可以通 过 Matlab 用 for 循环遍历 0-255,然后计算出当 x=2.2 的时候,0-255 中的每个数的输出值是多少,提前计算 出来,做成查找表,在 verilog 中,我们就可以通过 case 语句来完成本次算法,大大降低了运算的复杂度。具体 实现过程可以看后续的 Matlab 仿真部分。

3. 接口列表

gamma_lookuip.v 模块接口

因为做成了查找表,所以接口非常简单,仅有输入像素数据和输出像素数据。

4. 工程说明

本次工程可以直接对输入进来的 RGB888 数据进行 gamma 矫正提高亮度,或者先进行灰度化后,再进行 gamma 矫正都可以。对于 RGB 的图像,需要先分出 R、G、B 三个通道,每个通道均为 8bit,然后例化三个 gamma 矫正模块即可。

4.1. 代码模块说明

主要介绍 gamma_lookuptable 模块。

复制代码
// gamma_table 1/2.2
module gamma_lookuptable 
(
    input [7:0] video_data,
    output reg [7:0] gamma_data
);

always @(*) 
begin
    case(video_data)
        8'd0   : gamma_data = 8'd0;
        8'd1   : gamma_data = 8'd21;
        8'd2   : gamma_data = 8'd28;
        8'd3   : gamma_data = 8'd34;
        8'd4   : gamma_data = 8'd39;
        8'd5   : gamma_data = 8'd43;
        8'd6   : gamma_data = 8'd46;
        8'd7   : gamma_data = 8'd50;
        8'd8   : gamma_data = 8'd53;
        8'd9   : gamma_data = 8'd56;
        8'd10  : gamma_data = 8'd59;
        8'd11  : gamma_data = 8'd61;
        8'd12  : gamma_data = 8'd64;
        8'd13  : gamma_data = 8'd66;
        8'd14  : gamma_data = 8'd68;
        8'd15  : gamma_data = 8'd70;
        8'd16  : gamma_data = 8'd72;
        8'd17  : gamma_data = 8'd74;
        8'd18  : gamma_data = 8'd76;
        8'd19  : gamma_data = 8'd78;
        8'd20  : gamma_data = 8'd80;
        8'd21  : gamma_data = 8'd82;
        8'd22  : gamma_data = 8'd84;
        8'd23  : gamma_data = 8'd85;
        8'd24  : gamma_data = 8'd87;
        8'd25  : gamma_data = 8'd89;
        8'd26  : gamma_data = 8'd90;
        8'd27  : gamma_data = 8'd92;
        8'd28  : gamma_data = 8'd93;
        8'd29  : gamma_data = 8'd95;
        8'd30  : gamma_data = 8'd96;
        8'd31  : gamma_data = 8'd98;
        8'd32  : gamma_data = 8'd99;
        8'd33  : gamma_data = 8'd101;
        8'd34  : gamma_data = 8'd102;
        8'd35  : gamma_data = 8'd103;
        8'd36  : gamma_data = 8'd105;
        8'd37  : gamma_data = 8'd106;
        8'd38  : gamma_data = 8'd107;
        8'd39  : gamma_data = 8'd109;
        8'd40  : gamma_data = 8'd110;
        8'd41  : gamma_data = 8'd111;
        8'd42  : gamma_data = 8'd112;
        8'd43  : gamma_data = 8'd114;
        8'd44  : gamma_data = 8'd115;
        8'd45  : gamma_data = 8'd116;
        8'd46  : gamma_data = 8'd117;
        8'd47  : gamma_data = 8'd118;
        8'd48  : gamma_data = 8'd119;
        8'd49  : gamma_data = 8'd120;
        8'd50  : gamma_data = 8'd122;
        8'd51  : gamma_data = 8'd123;
        8'd52  : gamma_data = 8'd124;
        8'd53  : gamma_data = 8'd125;
        8'd54  : gamma_data = 8'd126;
        8'd55  : gamma_data = 8'd127;
        8'd56  : gamma_data = 8'd128;
        8'd57  : gamma_data = 8'd129;
        8'd58  : gamma_data = 8'd130;
        8'd59  : gamma_data = 8'd131;
        8'd60  : gamma_data = 8'd132;
        8'd61  : gamma_data = 8'd133;
        8'd62  : gamma_data = 8'd134;
        8'd63  : gamma_data = 8'd135;
        8'd64  : gamma_data = 8'd136;
        8'd65  : gamma_data = 8'd137;
        8'd66  : gamma_data = 8'd138;
        8'd67  : gamma_data = 8'd139;
        8'd68  : gamma_data = 8'd140;
        8'd69  : gamma_data = 8'd141;
        8'd70  : gamma_data = 8'd142;
        8'd71  : gamma_data = 8'd143;
        8'd72  : gamma_data = 8'd144;
        8'd73  : gamma_data = 8'd144;
        8'd74  : gamma_data = 8'd145;
        8'd75  : gamma_data = 8'd146;
        8'd76  : gamma_data = 8'd147;
        8'd77  : gamma_data = 8'd148;
        8'd78  : gamma_data = 8'd149;
        8'd79  : gamma_data = 8'd150;
        8'd80  : gamma_data = 8'd151;
        8'd81  : gamma_data = 8'd151;
        8'd82  : gamma_data = 8'd152;
        8'd83  : gamma_data = 8'd153;
        8'd84  : gamma_data = 8'd154;
        8'd85  : gamma_data = 8'd155;
        8'd86  : gamma_data = 8'd156;
        8'd87  : gamma_data = 8'd156;
        8'd88  : gamma_data = 8'd157;
        8'd89  : gamma_data = 8'd158;
        8'd90  : gamma_data = 8'd159;
        8'd91  : gamma_data = 8'd160;
        8'd92  : gamma_data = 8'd160;
        8'd93  : gamma_data = 8'd161;
        8'd94  : gamma_data = 8'd162;
        8'd95  : gamma_data = 8'd163;
        8'd96  : gamma_data = 8'd164;
        8'd97  : gamma_data = 8'd164;
        8'd98  : gamma_data = 8'd165;
        8'd99  : gamma_data = 8'd166;
        8'd100 : gamma_data = 8'd167;
        8'd101 : gamma_data = 8'd167;
        8'd102 : gamma_data = 8'd168;
        8'd103 : gamma_data = 8'd169;
        8'd104 : gamma_data = 8'd170;
        8'd105 : gamma_data = 8'd170;
        8'd106 : gamma_data = 8'd171;
        8'd107 : gamma_data = 8'd172;
        8'd108 : gamma_data = 8'd173;
        8'd109 : gamma_data = 8'd173;
        8'd110 : gamma_data = 8'd174;
        8'd111 : gamma_data = 8'd175;
        8'd112 : gamma_data = 8'd175;
        8'd113 : gamma_data = 8'd176;
        8'd114 : gamma_data = 8'd177;
        8'd115 : gamma_data = 8'd178;
        8'd116 : gamma_data = 8'd178;
        8'd117 : gamma_data = 8'd179;
        8'd118 : gamma_data = 8'd180;
        8'd119 : gamma_data = 8'd180;
        8'd120 : gamma_data = 8'd181;
        8'd121 : gamma_data = 8'd182;
        8'd122 : gamma_data = 8'd182;
        8'd123 : gamma_data = 8'd183;
        8'd124 : gamma_data = 8'd184;
        8'd125 : gamma_data = 8'd184;
        8'd126 : gamma_data = 8'd185;
        8'd127 : gamma_data = 8'd186;
        8'd128 : gamma_data = 8'd186;
        8'd129 : gamma_data = 8'd187;
        8'd130 : gamma_data = 8'd188;
        8'd131 : gamma_data = 8'd188;
        8'd132 : gamma_data = 8'd189;
        8'd133 : gamma_data = 8'd190;
        8'd134 : gamma_data = 8'd190;
        8'd135 : gamma_data = 8'd191;
        8'd136 : gamma_data = 8'd192;
        8'd137 : gamma_data = 8'd192;
        8'd138 : gamma_data = 8'd193;
        8'd139 : gamma_data = 8'd194;
        8'd140 : gamma_data = 8'd194;
        8'd141 : gamma_data = 8'd195;
        8'd142 : gamma_data = 8'd195;
        8'd143 : gamma_data = 8'd196;
        8'd144 : gamma_data = 8'd197;
        8'd145 : gamma_data = 8'd197;
        8'd146 : gamma_data = 8'd198;
        8'd147 : gamma_data = 8'd199;
        8'd148 : gamma_data = 8'd199;
        8'd149 : gamma_data = 8'd200;
        8'd150 : gamma_data = 8'd200;
        8'd151 : gamma_data = 8'd201;
        8'd152 : gamma_data = 8'd202;
        8'd153 : gamma_data = 8'd202;
        8'd154 : gamma_data = 8'd203;
        8'd155 : gamma_data = 8'd203;
        8'd156 : gamma_data = 8'd204;
        8'd157 : gamma_data = 8'd205;
        8'd158 : gamma_data = 8'd205;
        8'd159 : gamma_data = 8'd206;
        8'd160 : gamma_data = 8'd206;
        8'd161 : gamma_data = 8'd207;
        8'd162 : gamma_data = 8'd207;
        8'd163 : gamma_data = 8'd208;
        8'd164 : gamma_data = 8'd209;
        8'd165 : gamma_data = 8'd209;
        8'd166 : gamma_data = 8'd210;
        8'd167 : gamma_data = 8'd210;
        8'd168 : gamma_data = 8'd211;
        8'd169 : gamma_data = 8'd212;
        8'd170 : gamma_data = 8'd212;
        8'd171 : gamma_data = 8'd213;
        8'd172 : gamma_data = 8'd213;
        8'd173 : gamma_data = 8'd214;
        8'd174 : gamma_data = 8'd214;
        8'd175 : gamma_data = 8'd215;
        8'd176 : gamma_data = 8'd215;
        8'd177 : gamma_data = 8'd216;
        8'd178 : gamma_data = 8'd217;
        8'd179 : gamma_data = 8'd217;
        8'd180 : gamma_data = 8'd218;
        8'd181 : gamma_data = 8'd218;
        8'd182 : gamma_data = 8'd219;
        8'd183 : gamma_data = 8'd219;
        8'd184 : gamma_data = 8'd220;
        8'd185 : gamma_data = 8'd220;
        8'd186 : gamma_data = 8'd221;
        8'd187 : gamma_data = 8'd221;
        8'd188 : gamma_data = 8'd222;
        8'd189 : gamma_data = 8'd223;
        8'd190 : gamma_data = 8'd223;
        8'd191 : gamma_data = 8'd224;
        8'd192 : gamma_data = 8'd224;
        8'd193 : gamma_data = 8'd225;
        8'd194 : gamma_data = 8'd225;
        8'd195 : gamma_data = 8'd226;
        8'd196 : gamma_data = 8'd226;
        8'd197 : gamma_data = 8'd227;
        8'd198 : gamma_data = 8'd227;
        8'd199 : gamma_data = 8'd228;
        8'd200 : gamma_data = 8'd228;
        8'd201 : gamma_data = 8'd229;
        8'd202 : gamma_data = 8'd229;
        8'd203 : gamma_data = 8'd230;
        8'd204 : gamma_data = 8'd230;
        8'd205 : gamma_data = 8'd231;
        8'd206 : gamma_data = 8'd231;
        8'd207 : gamma_data = 8'd232;
        8'd208 : gamma_data = 8'd232;
        8'd209 : gamma_data = 8'd233;
        8'd210 : gamma_data = 8'd233;
        8'd211 : gamma_data = 8'd234;
        8'd212 : gamma_data = 8'd234;
        8'd213 : gamma_data = 8'd235;
        8'd214 : gamma_data = 8'd235;
        8'd215 : gamma_data = 8'd236;
        8'd216 : gamma_data = 8'd236;
        8'd217 : gamma_data = 8'd237;
        8'd218 : gamma_data = 8'd237;
        8'd219 : gamma_data = 8'd238;
        8'd220 : gamma_data = 8'd238;
        8'd221 : gamma_data = 8'd239;
        8'd222 : gamma_data = 8'd239;
        8'd223 : gamma_data = 8'd240;
        8'd224 : gamma_data = 8'd240;
        8'd225 : gamma_data = 8'd241;
        8'd226 : gamma_data = 8'd241;
        8'd227 : gamma_data = 8'd242;
        8'd228 : gamma_data = 8'd242;
        8'd229 : gamma_data = 8'd243;
        8'd230 : gamma_data = 8'd243;
        8'd231 : gamma_data = 8'd244;
        8'd232 : gamma_data = 8'd244;
        8'd233 : gamma_data = 8'd245;
        8'd234 : gamma_data = 8'd245;
        8'd235 : gamma_data = 8'd246;
        8'd236 : gamma_data = 8'd246;
        8'd237 : gamma_data = 8'd247;
        8'd238 : gamma_data = 8'd247;
        8'd239 : gamma_data = 8'd248;
        8'd240 : gamma_data = 8'd248;
        8'd241 : gamma_data = 8'd249;
        8'd242 : gamma_data = 8'd249;
        8'd243 : gamma_data = 8'd249;
        8'd244 : gamma_data = 8'd250;
        8'd245 : gamma_data = 8'd250;
        8'd246 : gamma_data = 8'd251;
        8'd247 : gamma_data = 8'd251;
        8'd248 : gamma_data = 8'd252;
        8'd249 : gamma_data = 8'd252;
        8'd250 : gamma_data = 8'd253;
        8'd251 : gamma_data = 8'd253;
        8'd252 : gamma_data = 8'd254;
        8'd253 : gamma_data = 8'd254;
        8'd254 : gamma_data = 8'd255;
        8'd255 : gamma_data = 8'd255;
    endcase
end

endmodule

该代码其实均有 matlab 脚本自动生成。代码整体不难,通过查找表的方法找到输入的像素数据对应 gamma 处理后的值,然后输出即可。需要注意的是 always@(*)表示的是组合逻辑,所以并不会延迟一个时钟周期,故不需要对数据有效信号打拍。

4.2. 代码仿真
4.2.1. Matlab 仿真介绍
复制代码
%% 清空
clc; clear all;

%% 读取图像
originalImage = imread('night1.bmp');

%% 读取 RGB 三通道
R = originalImage(:,:,1);
G = originalImage(:,:,2);
B = originalImage(:,:,3);

%% 设定 gamma 值
gamma_value = 1/2.2;
figure;

%% 显示原图
subplot(1, 2, 1);
imshow(originalImage);
title('原图像');

%% 图像尺寸
[img_height, img_width, channel] = size(originalImage);
gamma_r = zeros(img_height, img_width);
gamma_g = zeros(img_height, img_width);
gamma_b = zeros(img_height, img_width);

%% 对R通道进行gamma校正
for i = 1:img_height
    for j = 1:img_width
        gamma_r(i,j) = (255/255.^(gamma_value)) * double(R(i,j)).^(gamma_value);
    end
end

%% 对G通道进行gamma校正
for i = 1:img_height
    for j = 1:img_width
        gamma_g(i,j) = (255/255.^(gamma_value)) * double(G(i,j)).^(gamma_value);
    end
end

%% 对B通道进行gamma校正
for i = 1:img_height
    for j = 1:img_width
        gamma_b(i,j) = (255/255.^(gamma_value)) * double(B(i,j)).^(gamma_value);
    end
end

%% 合并通道并显示结果
gamma_img = cat(3, uint8(gamma_r), uint8(gamma_g), uint8(gamma_b));

subplot(1, 2, 2);
imshow(gamma_img);
imwrite(gamma_img, 'D:\pango_isp\img_test_pg\img_test_pg\01_led_test\sim\matlab_gamma.png');
title('gamma 矫正后的图像');

重复的操作将不在介绍,只介绍关键部分。

在代码的 6-8 行,分离图片的 RGB 三通道。(:,:,1)表示取出红色分量。

代码的 20-22 行定义 gamma 变化后的三个通道的二维矩阵,让其大小和我们读取的图像的大小一致。

代码 24-43 行通过 for 循环分别对 RGB 三通道里的每个像素进行 gamma 变化,按照公式进行计算。

代码的 45 行将 gamma 变化后的结果强制转为 uint8 格式,然后利用 cat 函数,将三通道重新拼成一个三维图像矩阵。

最后通过 imshow(gamma_img)将 gamma 变化后的结果显示出来,imwrite 用来将图片保存到本地。

运行结果如下所示:

4.2.2. Matlab 生成查找表
复制代码
%% 清空
clear all; close all; clc;

%% 设置gamma值
gamma_value = 1/2.2;

%% 打开文件
% 创建并写入gamma查找表Verilog文件
fpga_gamma = fopen('gamma_table.v', 'w');

%% 写入模块头部
fprintf(fpga_gamma, '// gamma_table 1/%.1f\n', gamma_value);
fprintf(fpga_gamma, 'module gamma_lookuptable\n');
fprintf(fpga_gamma, '(\n');
fprintf(fpga_gamma, '    input\t\t[7:0]\tvideo_data,\n');
fprintf(fpga_gamma, '    output\treg\t[7:0]\tgamma_data\n');
fprintf(fpga_gamma, ');\n\n');

%% 写入always块开始
fprintf(fpga_gamma, 'always @(*) begin\n');
fprintf(fpga_gamma, '    case(video_data)\n');

%% 计算并写入256个gamma值
gamma_array = zeros(1, 256);
for i = 1:256
    gamma_array(1, i) = (255/255.^gamma_value) * (i-1).^gamma_value;
    gamma_array(1, i) = uint8(gamma_array(1, i));
    fprintf(fpga_gamma, '        8''d%-3d : gamma_data = 8''d%-3d;\n', i-1, gamma_array(1, i));
end

%% 写入模块尾部
fprintf(fpga_gamma, '    endcase\n');
fprintf(fpga_gamma, 'end\n\n');
fprintf(fpga_gamma, 'endmodule\n');

%% 关闭文件
fclose(fpga_gamma);

其实这样的脚本我们可以通过 AI 工具来快速生成,提高开发效率,例如使用 GPT、文心一言等,都可以快速生成类似脚本。

fprintf()函数会在打开的文本里写入数据,'\n'表示换行,所以按照我们的 verilog 的语法来一步一步将数 据写入即可。

代码的 6-15 行就是定义了模块的基本框架比如端口等。

代码的 18-23 行,通过 for 循环遍历 0-255 共 256 个像素,把每个像素经过 gamma 变化后的结果写入到 文本之中。按照十进制写入,'\t'表示空四个格。

最后在末尾写入 endcase、end 以及 endmodule 即可。

下面便是通过 Matlab 脚本生成的查找表。

复制代码
// gamma_table 1/2.2
module gamma_lookuptable (
    input      [7:0] video_data,
    output reg [7:0] gamma_data
);

always @(*) begin
    case(video_data)
        8'd0   : gamma_data = 8'd0;
        8'd1   : gamma_data = 8'd21;
        8'd2   : gamma_data = 8'd28;
        8'd3   : gamma_data = 8'd34;
        8'd4   : gamma_data = 8'd39;
        8'd5   : gamma_data = 8'd43;
        8'd6   : gamma_data = 8'd46;
        8'd7   : gamma_data = 8'd50;
        8'd8   : gamma_data = 8'd53;
        8'd9   : gamma_data = 8'd56;
        8'd10  : gamma_data = 8'd59;
        8'd11  : gamma_data = 8'd61;
        8'd12  : gamma_data = 8'd64;
        8'd13  : gamma_data = 8'd66;
        8'd14  : gamma_data = 8'd68;
        8'd15  : gamma_data = 8'd70;
        8'd16  : gamma_data = 8'd72;
        8'd17  : gamma_data = 8'd74;
        8'd18  : gamma_data = 8'd76;
        8'd19  : gamma_data = 8'd78;
        ......................
        省略中间部分
        ......................
        8'd255 : gamma_data = 8'd255;
    endcase
end

endmodule
4.2.3. Modelsim 仿真介绍

仿真的 TB 只需要例化三个 gamma 模块,将 RGB 分出三通道,每个通道都是 8bit。具体如下所示:

复制代码
gamma_lookuptable u_gamma_lookuptable_r (
    .video_data (video_data[23:16]),
    .gamma_data (gamma_data_r)
);

gamma_lookuptable u_gamma_lookuptable_g (
    .video_data (video_data[15:8]),
    .gamma_data (gamma_data_g)
);

gamma_lookuptable u_gamma_lookuptable_b (
    .video_data (video_data[7:0]),
    .gamma_data (gamma_data_b)
);

将 RGB888 分别给到 gamma 模块进行处理即可。

复制代码
always @(posedge video_clk or negedge rst_n) begin
    if (!rst_n)
        video_vs_d <= 1'd0;
    else
        video_vs_d <= video_vs;
end

assign frame_flag = ~video_vs & video_vs_d;  // 下降沿检测

always @(posedge video_clk or negedge rst_n) begin
    if (!rst_n)
        img_done <= 1'b0;
    else if (frame_flag)  // 下降沿判断一帧结束
        img_done <= 1'b1;
    else
        img_done <= img_done;
end

always @(posedge video_clk or negedge rst_n) begin
    if (img_done) begin
        $stop;  // 停止仿真
    end
    else if (video_de) begin  // 写入数据
        $fdisplay(output_file, "%h\t%h\t%h", 
                 gamma_data_r, 
                 gamma_data_g, 
                 gamma_data_b);  // 16进制写入
    end
end

最后的有效信号、场信号和数据按上述代码修改即可。最后运行联合仿真,将生成的 txt 在 matlab 中恢复为图片显示。

4.3. 实验现象

连接好下载器,电源、HDMI_IN 口连接电脑、HDMD_OUT 口连接显示器,然后下载程序。

上图为测试原图像。

上图为经过 gamma 矫正后的图像,相对原图,经过 gamma 矫正后的图像细节更加清晰,读者可以通过修改 gamma 的值去改善图像增强的效果。

相关推荐
武昌库里写JAVA1 小时前
使用 Java 开发 Android 应用:Kotlin 与 Java 的混合编程
java·vue.js·spring boot·sql·学习
程序员鱼皮1 小时前
这套 Java 监控系统太香了!我连夜给项目加上了
java·前端·ai·程序员·开发·软件开发
CoderJia程序员甲2 小时前
GitHub 热榜项目 - 日榜(2025-08-21)
ai·开源·github·ai编程
subuq4 小时前
Web3.0 时代的电商系统:区块链如何解决信任与溯源问题?
大数据·网络·学习
袁培宇4 小时前
python学习打卡day40
人工智能·python·学习
飞龙观奥4 小时前
光学件加工厂倚光科技:陪跑光学未来力量
科技
一念&4 小时前
今日科技风向|从AI芯片定制到阅兵高科技展示——聚焦技术前沿洞察
人工智能·科技
5 小时前
JAVA-15 (2025.08.20学习记录)
java·开发语言·学习
im_AMBER5 小时前
学习日志39 python
开发语言·python·学习