> 文章列表 > 基于 Verilog HDL 设计真彩图的灰度处理模块

基于 Verilog HDL 设计真彩图的灰度处理模块

基于 Verilog HDL 设计真彩图的灰度处理模块



引言

FPGA比较擅长的是作定点数整数运算,那么对于带有小数部分的乘加运算。一般都选择先扩大若干倍,而后将运算结果缩小若干倍实现。

应用案例,真彩图灰度图的心理学计算公式:

Gray = 0.299R + 0.587G + 0.114B

本文给出具体的设计、仿真源码(Verilog HDL)。结合MATLAB平台验证结果的准确性。

Verilog 编译仿真平台:Vivado 2018.3

MATLAB版本:2022a


设计

// ==============================================================================
// 功能描述:真彩图 转 灰度图
// 作 	 者:Xu Y. B.
// 计算公式:Gray = 0.299R + 0.587G + 0.114B
// 思	 路:采用定点运算,先将系数扩大若干倍,最后的结果在缩小若干倍
// ==============================================================================`timescale 1ns / 1psmodule RGB_2_GRAY_MDL #(
// ---- ---- ---- ---- 模块可重载参数 ---- ---- ---- ---- 
parameter		P_PIXEL_DATA_WIDTH 		= 		24,    //像素数据位宽 ,RGB 分量等位宽
parameter		P_SCALE_FACTOR     		= 		1024,  //系数缩放因子
parameter		P_GRAY_DATA_WIDTH 		=		8      //输出灰度数据位宽
)(
// ---- ---- ---- ----     模块端口    ---- ---- ---- ----
input 											I_OPR_CLK,
input 											I_OPR_RSTN,
input 											I_PIXEL_VAL,
input			[P_PIXEL_DATA_WIDTH-1:0]		I_PIXEL_DATA,// R:MSB B:LSBoutput 	reg										O_GRAY_VAL,
output 			[P_GRAY_DATA_WIDTH-1:0]			O_GRAY_DATA);
// ---- ---- ---- ----     内部参数    ---- ---- ---- ----
localparam 		LP_RIGHT_SHIFT_VALUE 	=		$clog2(P_SCALE_FACTOR);
integer 		INT_COEF_R				=		0.299*P_SCALE_FACTOR;
integer 		INT_COEF_G				=		0.587*P_SCALE_FACTOR;
integer 		INT_COEF_B				=		0.114*P_SCALE_FACTOR;
localparam 		LP_GRAY_RES_WIDTH 		=		FUNC_CAL_GRAY_RES_WIDTH(P_SCALE_FACTOR,P_PIXEL_DATA_WIDTH);// ---- ---- ---- ----     内部信号    ---- ---- ---- ----
reg 			[LP_GRAY_RES_WIDTH-1:0] 		R_GRAY_RES;
// ---- ---- ---- ----     内部逻辑    ---- ---- ---- ----
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginO_GRAY_VAL <= 1'b0;R_GRAY_RES <= {LP_GRAY_RES_WIDTH{1'b0}};endelsebeginif(I_PIXEL_VAL)beginO_GRAY_VAL <= I_PIXEL_VAL;R_GRAY_RES <= (I_PIXEL_DATA[P_PIXEL_DATA_WIDTH-1-:8] * INT_COEF_R+I_PIXEL_DATA[P_PIXEL_DATA_WIDTH/3+:8] * INT_COEF_G+I_PIXEL_DATA[0+:8] * INT_COEF_B)>>LP_RIGHT_SHIFT_VALUE;//此处组合逻辑延迟较大 , 待优化endelsebeginO_GRAY_VAL <= 1'b0;R_GRAY_RES <= {LP_GRAY_RES_WIDTH{1'b0}};			endend
end
assign O_GRAY_DATA = R_GRAY_RES[0+:P_GRAY_DATA_WIDTH];// ---- ---- ---- ----     函数/任务   ---- ---- ---- ----
// 计算最终结果的右移数值
function integer FUNC_CAL_GRAY_RES_WIDTH;input integer SCALE_FACTOR;input integer PIXEL_DATA_WIDTH;beginFUNC_CAL_GRAY_RES_WIDTH = PIXEL_DATA_WIDTH/3 + $clog2(SCALE_FACTOR) + 1 + 1;end
endfunction endmodule

设计源码遵循参数化程序设计的规范,可以设置缩放因子以及输出位宽等。在使用时,直接调用即可,不用调整内部逻辑和参数。

注意,3项乘加运算的逻辑在较高频率的时钟下,可能会存在建立时间/保持时间为例。

此处提供一个思路,采用流水线结构进行运算,即先分别计算3项的乘法,然后再计算3项的加法,计算加法时需要注意,由于相加的加数个数不等于2的整数次幂,故可计算两个加数的和,然后将第三个加数延迟一拍后再与和相加,得到最终的结果。如此一来,时序的问题可以缓解,但是带来的后果就是计算延迟增大。 

仿真

// ==============================================================================
// 功能描述:测试 RGB_2_GRAY_MDL 模块
// 作 	 者:Xu Y. B.
// 计算公式:像素数据激励
// 思	 路:
// ==============================================================================`timescale 1ns / 1ps
module TB_RGB_2_GRAY_MDL();parameter		P_PIXEL_DATA_WIDTH 		= 		24;    //像素数据位宽 ,RGB 分量等位宽
parameter		P_SCALE_FACTOR     		= 		1024;  //系数缩放因子
parameter		P_GRAY_DATA_WIDTH 		=		8;     //输出灰度数据位宽reg											I_OPR_CLK;
reg											I_OPR_RSTN;
reg											I_PIXEL_VAL;
reg			[P_PIXEL_DATA_WIDTH-1:0]		I_PIXEL_DATA;// R:MSB B:LSBwire 										O_GRAY_VAL;
wire 		[P_GRAY_DATA_WIDTH-1:0]			O_GRAY_DATA;reg 		[P_PIXEL_DATA_WIDTH-1:0] 		R_PIXEL_DATA[24366:1];// 产生激励时钟
initial I_OPR_CLK = 1'b0;
always #5 I_OPR_CLK = ~I_OPR_CLK;// 数据读取
initial
begin$readmemb("D:/A_Vivado_WorkSpace/DSP_BASIC_STUDY/MAT_FILE/IMAGE_PIXEL_DATA.txt",R_PIXEL_DATA);
end// 复位、数据控制
initial
beginI_OPR_RSTN = 1'b0;// I_PIXEL_VAL  = 0;// I_PIXEL_DATA = 0;#109;I_OPR_RSTN = 1'b1;// @(posedge I_OPR_CLK)// I_PIXEL_VAL  <= 1;// I_PIXEL_DATA <= {8'd121,8'd99,8'd230};// @(posedge I_OPR_CLK)// I_PIXEL_VAL  <= 1;// I_PIXEL_DATA <= {8'd101,8'd90,8'd210};// @(posedge I_OPR_CLK)// I_PIXEL_VAL  <= 1;// I_PIXEL_DATA <= {8'd151,8'd69,8'd240};// @(posedge I_OPR_CLK)// I_PIXEL_VAL  <= 1;// I_PIXEL_DATA <= {8'd221,8'd109,8'd20};@(negedge O_GRAY_VAL)I_PIXEL_VAL  <= 0;#290;$finish;	
endinteger K=1;always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginI_PIXEL_VAL  <= 0;I_PIXEL_DATA <= 0;endelse if(K<=24366)beginI_PIXEL_VAL  <= 1;I_PIXEL_DATA <= R_PIXEL_DATA[K];K <= K+1;		endelsebeginK = K;I_PIXEL_VAL  <= 0;I_PIXEL_DATA <= 0;		end
endinteger FILE_ID ;initial
beginFILE_ID = $fopen("D:/A_Vivado_WorkSpace/DSP_BASIC_STUDY/MAT_FILE/GRAY_DATA.txt","w+");while(~O_GRAY_VAL | ~I_OPR_RSTN) @(posedge I_OPR_CLK);while(O_GRAY_VAL)begin$fdisplayb(FILE_ID,O_GRAY_DATA);@(posedge I_OPR_CLK);end$fclose(FILE_ID);
endRGB_2_GRAY_MDL #(.P_PIXEL_DATA_WIDTH(P_PIXEL_DATA_WIDTH),.P_SCALE_FACTOR    (P_SCALE_FACTOR),.P_GRAY_DATA_WIDTH (P_GRAY_DATA_WIDTH)) INST_RGB_2_GRAY_MDL (.I_OPR_CLK    (I_OPR_CLK),.I_OPR_RSTN   (I_OPR_RSTN),.I_PIXEL_VAL  (I_PIXEL_VAL),.I_PIXEL_DATA (I_PIXEL_DATA),.O_GRAY_VAL   (O_GRAY_VAL),.O_GRAY_DATA  (O_GRAY_DATA));endmodule

此部分的验证提供了两种方式:

1、已经被注释的部分,只是给几个数据送入模块中验证计算的准确性;

2、读取txt文件中的像素数据,送入模块中进行运算,运算结果保存至另一个txt文件中,然后对比MATLAB和FPGA计算的误差。(此部分具体的实现往下看) 

对比验证

matlab 生成测试数据以及分析对比的源码:

%% ==================== 像素数据读取存储 ====================
% 作者:Xu Y. B.
% 说明:
%       -1- 读取真彩图的RGB数据,拼接为24位二进制数,存入txt文件
%       -2- 对真彩图作灰度处理,并与FPGA处理结果作对比
% =============================================================%% CLEAR
clc;
clearvars;
close all;%% 图片读取
FILE_PATH = "C:\\Users\\XYB\\Pictures\\高清壁纸Z\\wallhaven-5758y8.jpg";
IMAGE_DATA = imread (FILE_PATH,"jpg");
figure;
imshow(IMAGE_DATA);
title("原真彩图")
% 图片截取
PIXEL_DATA = IMAGE_DATA(240:425,868:998,:);
figure;
subplot(121)
imshow(PIXEL_DATA);
title("截取的真彩图")
subplot(122)
IM2GRAY = im2gray(PIXEL_DATA);
imshow(IM2GRAY)
title("截取的真彩图转灰度图")%% 数据转化存储
PIXEL_DATA_R = PIXEL_DATA(:,:,1);
PIXEL_DATA_G = PIXEL_DATA(:,:,2);
PIXEL_DATA_B = PIXEL_DATA(:,:,3);PIXEL_DATA_R_BIN = dec2bin(reshape(PIXEL_DATA_R,[],1));
PIXEL_DATA_G_BIN = dec2bin(reshape(PIXEL_DATA_G,[],1));
PIXEL_DATA_B_BIN = dec2bin(reshape(PIXEL_DATA_B,[],1));PIXEL_DATA_BIN   = strcat(strcat(PIXEL_DATA_R_BIN,PIXEL_DATA_G_BIN),PIXEL_DATA_B_BIN);WRITE_PATH = "D:\\A_Vivado_WorkSpace\\DSP_BASIC_STUDY\\MAT_FILE\\IMAGE_PIXEL_DATA.txt";
writematrix(PIXEL_DATA_BIN,WRITE_PATH,"WriteMode","overwrite","FileType","text")%% FPGA处理结果读取
READ_PATH = "D:\\A_Vivado_WorkSpace\\DSP_BASIC_STUDY\\MAT_FILE\\GRAY_DATA.txt";
GRAY_DATA_FPGA = uint8(bin2dec(readmatrix(READ_PATH,"OutputType","string")));
GRAY_IMAG = reshape(GRAY_DATA_FPGA,186,[]);
figure;
subplot(121)
imshow(GRAY_IMAG)
title("FPGA处理结果")
subplot(122)
imshow(IM2GRAY)
title("MATLAB处理结果")
GRAY_PROC_ERR = uint8(abs(double(GRAY_IMAG)-double(IM2GRAY)));
figure;
mesh(GRAY_PROC_ERR);
title("FPGA MATLAB处理结果差异三维图")
figure;
imagesc(GRAY_PROC_ERR)
title("FPGA MATLAB处理结果差异平面")

原图:

图片截取以及MATLAB的灰度处理:

FPGA处理结果与MATLAB处理结果对比:

误差分析:

综合以上的分析验证结果,基于Verilog HDL 设计的模块,可以准确地将真彩图转化为灰度图,与MATLAB计算结果相差的最大值为1,大概率是由于四舍五入的精度问题导致。此问题只能通过增大缩放因子来缓解,比如将1024倍的缩放因子改为2048。

说明

以上所有代码中对应的文件路径都需要个性化更改,不可以直接复制粘贴使用,否则会报错:找不到路径对应的文件。

由于本人能力有限,如果有更好的想法或者在使用中遇到问题,都可以在评论区里留言交流~~~