Matlab+FPGA进行灰度图像处理

…# Matlab+FPGA进行灰度图像处理(两种方式)在这里插入图片描述

*MATLAB主要用于思路验证,转到FPGA的话需要对底层函数逻辑清楚才行,python也能进行matlab在这里做的所有操作,有兴趣可以深入。

1.matlab读取图片显示:

pic_rgb = imread('1.jpg'); %477x692x3
figure; imshow(pic_rgb);

//调用函数灰度显示
pic_gray = rgb2gray(pic_rgb);%477x692
figure; imshow(pic_gray);

2.matlab灰度反显

pic_reverse_gray = pic_gray;  %确定图片大小

for i = 1:477
	for j = 1:692
		pic_reverse_gray(i,j) =uint8(255-pic_gray(i,j));
	end
end
figure; imshow(pic_reverse_gray);

3.matlab二值化,将灰度进行黑白划分

pic_2 = pic_gray;  %确定图片大小

for i = 1:477
	for j = 1:692
			if (pic_gray(i,j)>140)    %140为分界线,可自由设置
				pic_2(i,j) = uint8(255);
			else
				pic_2(i,j) = uint8(0);
			end
	end
end
figure; imshow(pic_2 );

Part1.思路:用matlab将图片写成TXT发送给FPGA进行处理,然后将FPGA处理完的数据返回给matlab进行显示查看是否正确。

  1. matlab把图片转到txt格式,文件名:rgb_data.txt
clear;
clc;
pic_rgb =imread('1.jpg')

pic_txt = fopen('rgb_data.txt','w');

for i = 1:477
	for j = 1:692
		fprintf(pic_txt,'%x \n', pic_rgb (i,j,1));
		fprintf(pic_txt,'%x \n', pic_rgb (i,j,2));
		fprintf(pic_txt,'%x \n', pic_rgb (i,j,3));
	end
end

fclose(pic_txt);
  1. FPGA进行读取txt并进行处理数据,注意小数的变化,并进行仿真处理输出txt格式
module pic_deal(
input	  wire [7:0] rgb_r,
input		wire [7:0] rgb_g,
input		wire [7:0] rgb_b,

output	wire [7:0] gray

);
wire [17:0] gray_temp;
//gray = 0.299*R+0.587*G+0.114*B,扩大1024倍,而后截取整数位
assign gray_temp= 306*rgb_r+601*rgb_g+117*rgb_b;
assign gray     = gray_temp[17:10];
/*考虑四舍五入的话就执行下列方式
assign gray     = (gray_temp[9]==1)?(gray_temp[17:10]+1):gray_temp[17:10];
*/
endmodule
`timescale 1ns/1ps
module rgb_data_tb();
	reg  [7:0] rgb_r;
	reg  [7:0] rgb_g;
	reg  [7:0] rgb_b;
	wire [7:0] gray;

	reg [7:0] rgb_data_mem[990251 : 0];//有多少个数值就改成多少位宽
	reg [19:0] addr;
	integer     fid;
pic_deal rgb2gray_inst(
	.rgb_r(rgb_r),
	.rgb_g(rgb_g),
	.rgb_b(rgb_b),
	.gray (gray)
);
//读取txt的文件内容并存放到寄存器,txt文件在Vivado中要放到xsim文件中,才可以调用
initial $readmemh("rgb_data.txt",rgb_data_mem);
//保存处理的文件
initial fid =$fopen("gray_data.txt");

initial begin
	addr = 0;
	repeat(477*692)begin
		rgb_r = rgb_data_mem[0+addr];
		rgb_g = rgb_data_mem[1+addr];
		rgb_b = rgb_data_mem[2+addr];
		#20;
		$fdisplay(fid,"%d",gray);
		addr = addr +3;
		$fclose(fid);
	end
end
endmodule

注意,在Vivado中生成txt文件的时候,仿真时间的设置对应了数据能否全部记录到生成的文件中,所以需要根据自己的图片大小设置时间长度!具体的方式就是先仿真一次看地址到达最后一个数据值时的时间是多少,再把这个时间改到下面的仿真时间里就好了。结束过后,要刷新一次存放生成文件的文件夹,txt文件才会刷新。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2EFlFYOs-1663839921041)(Matlab+FPGA%E8%BF%9B%E8%A1%8C%E7%81%B0%E5%BA%A6%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%2074d60b363556488a8f301b93f256fb7b/Untitled.png)]

  1. matlab读取txt文件
fid = fopen('gray_data.txt','r');
gray_data = fscanf(fid,'%d');%1维数据,需要转换为二维,转换的方式需要思考一下
	end
fclose(fid);

for i = 1:477
	for j = 1:692
		pic_gray_my(i,j) = uint8(gray_data((i-1)*692+j));%一定要转化格式为无符号8位
end

figure('name','pic_gray_my');
imshow(pic_gray_my);

Part2.思路:通过Matlab将图片写成24位RGB数据txt文本到FPGA中读取,处理并显示,需要有HDMI模块。(进阶,基于彩条显示实验)

准备工程:彩条的显示实验(U1,U2是后续我们自己设计的):

在这里插入图片描述

第一步:生成COE文件给Vivado读取成ROM(注意是24位的)

src = imread('02.jpg');
r = src(:,:,1);
g = src(:,:,2);
b = src(:,:,3);
data_r = reshape(r', 1,46870);
data_g = reshape(g', 1,46870);
data_b = reshape(b', 1,46870);%注意是先处理x 就是行!!!
fid=fopen('rgb_init.coe', 'wt');%打开文件
fprintf(fid, 'MEMORY_INITIALIZATION_RADIX=16;\n');
fprintf(fid, 'MEMORY_INITIALIZATION_VECTOR=\n');
for i = 1 : 46870-1
    fprintf(fid, '%x%x%x,\n', data_r(i),data_g(i),data_b(i));%使用%x表示十六进制数
end
fprintf(fid, '%x%x%x;',data_r(46870), data_g(46870),data_b(46870));%%输出结尾,每个数据后面用逗号或者空格或者换行符隔开,最后一个数据后面加分号
  • 我这里图片大小为218X215=46870,根据不同图片进行调整即可;
  • 得到COE文件如下:
MEMORY_INITIALIZATION_RADIX=16;
MEMORY_INITIALIZATION_VECTOR=
d5cdca,
e2dad7,
fff6f5,
fef4f3,
fef4f3,
...
...
fe324d,
ff3250,
ff3250;

在这里插入图片描述

随后选择Block ROM,这里选BLOCK ROM是因为选用Distribute ROM则会占用很多资源,综合都会慢,尽管用分布式rom不会有始终延迟,但是会很慢!!!所以强烈建议用块式ROM,但是会有两个时钟延迟,后续影响极大,所以要仔细观察,具体设置如下,深度一定要是16的整数倍:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//delay 1 clk
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		begin
			hs_reg_d0 <= 1'b0;
			hs_reg_d1 <= 1'b0;
			hs_reg_d2 <= 1'b0;
			vs_reg_d0 <= 1'b0;
			vs_reg_d1 <= 1'b0;
			vs_reg_d2 <= 1'b0;
			video_active_d0 <= 1'b0; //因为ROM有两个始终延迟所以这里也需要延迟两个CLK
			video_active_d1 <= video_active_d0; 
			video_active_d2 <= video_active_d1; 
		end
	else
		begin
			hs_reg_d0 <= hs_reg;
			hs_reg_d1 <= hs_reg_d0;
			hs_reg_d2 <= hs_reg_d1;
			vs_reg_d0 <= vs_reg;
			vs_reg_d1 <= vs_reg_d0;
			vs_reg_d2 <= vs_reg_d1;
			video_active_d0 <= video_active;
			video_active_d1 <= video_active_d0; 
			video_active_d2 <= video_active_d1; 
		end
end
parameter OSD_WIDTH   =  12'd218;	//设置OSD的宽度,根据图片设置
parameter OSD_HEGIHT  =  12'd215;	//设置OSD的高度,根据图片设置
//horizontal counter in total
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		h_cnt <= 12'd0;
	else if(h_cnt == H_TOTAL - 1)//horizontal counter maximum value
		h_cnt <= 12'd0;
	else
		h_cnt <= h_cnt + 12'd1;
end
//
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		active_x <= 12'd0;
	else if(h_cnt >= H_FP + H_SYNC + H_BP - 1)//horizontal video active
		active_x <= h_cnt - (H_FP[11:0] + H_SYNC[11:0] + H_BP[11:0] - 12'd1);
	else
		active_x <= active_x;
end
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		active_y <= 12'd0;
	else if(v_cnt >= V_FP + V_SYNC + V_BP - 1)//horizontal video active
		active_y <= v_cnt - (V_FP[11:0] + V_SYNC[11:0] + V_BP[11:0] - 12'd1);
	else
		active_y <= active_y;
end
//vertical counter in total
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		v_cnt <= 12'd0;
	else if(h_cnt == H_FP  - 1)//horizontal sync time
		if(v_cnt == V_TOTAL - 1)//vertical counter maximum value
			v_cnt <= 12'd0;
		else
			v_cnt <= v_cnt + 12'd1;
	else
		v_cnt <= v_cnt;
end
//delay the Sync for 1 clk, it is the same meaning
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		hs_reg <= 1'b0;
	else if(h_cnt == H_FP - 1)//horizontal sync begin
		hs_reg <= HS_POL;
	else if(h_cnt == H_FP + H_SYNC - 1)//horizontal sync end
		hs_reg <= ~hs_reg;
	else
		hs_reg <= hs_reg;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		h_active <= 1'b0;
	else if(h_cnt == H_FP + H_SYNC + H_BP - 1)//horizontal active begin
		h_active <= 1'b1;
	else if(h_cnt == H_TOTAL - 1)//horizontal active end
		h_active <= 1'b0;
	else
		h_active <= h_active;
end
//delay the vertical sync for 1 clk
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		vs_reg <= 1'd0;
	else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))//vertical sync begin
		vs_reg <= HS_POL;
	else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))//vertical sync end
		vs_reg <= ~vs_reg;  
	else
		vs_reg <= vs_reg;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		v_active <= 1'd0;
	else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))//vertical active begin
		v_active <= 1'b1;
	else if((v_cnt == V_TOTAL - 1) && (h_cnt == H_FP - 1)) //vertical active end
		v_active <= 1'b0;   
	else
		v_active <= v_active;
end
//*******************************USER_DESIGN***************************//
reg         [19:0] addr,addr0;
reg         [2:0]   cnt;
wire        [23:0] dout,dout0;//rom out 
reg pos_vs_d0,pos_vs_d1;
always @(posedge clk)
begin
    pos_vs_d0 <= vs_reg_d0;
    pos_vs_d1 <= pos_vs_d0;
    region_active_d0 <= region_active;
    region_active_d1 <= region_active_d0;
    region_ACT_d0<=region_ACT;
    region_ACT_d1<=region_ACT_d0;
end
reg region_active,region_active_d0,region_active_d1;
reg region_ACT,region_ACT_d0,region_ACT_d1;
//Region of gray,这里是确定画面上图片的位置,确定X,Y坐标区域范围(灰度图片位置)
always@(posedge clk)
begin
	if(active_y >= 12'd9+ OSD_HEGIHT - 12'd1 && active_y <= 12'd9 + OSD_HEGIHT*2 - 12'd1 && 
active_x >= 12'd9 && active_x  <= 12'd9 + OSD_WIDTH - 12'd1)
		region_ACT <= 1'b1;
	else
		region_ACT <= 1'b0;
end
//region of original pic这里是确定画面上图片的位置,确定X,Y坐标区域范围(原始图片位置)
always@(posedge clk)
begin
	if(active_y >= 12'd9 && active_y <= 12'd9 + OSD_HEGIHT - 12'd1 && active_x >= 12'd9 && 
active_x  <= 12'd9 + OSD_WIDTH - 12'd1)
		region_active <= 1'b1;
	else
		region_active <= 1'b0;
end
//有两个CLK得输出延迟,例化两个图片,这里要分开因为地址不共用,不然会出现错乱
blk_mem_gen_0 U1(
    	.clka                       (clk                    ),   
        .ena                        (region_active          ),
        .addra                      (addr                   ),
        .douta                      (dout                   )  
);
blk_mem_gen_0 U2(
    	.clka                       (clk                    ),   
        .ena                        (region_ACT             ),
        .addra                      (addr0                   ),
        .douta                      (dout0                   )  
);
//RGB2GRAY algorisim,扩大了1024倍,即左移了10位,所以后面只取高8位即可
wire [17:0] line_data;
assign line_data= 306*dout0[23:16]+601*dout0[15:8]+117*dout0[7:0];

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
	   addr <= 0;
    else if (pos_vs_d0 == 1'b0 && pos_vs_d1 == 1'b1) // each frame resets onece
	   addr <= 0;
	else if (region_active)
	   addr    <= addr +1;
end
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
	   addr0 <= 0;
    else if (pos_vs_d0 == 1'b0 && pos_vs_d1 == 1'b1)
	   addr0 <= 0;
	else if (region_ACT)
	   addr0    <= addr0 +1;
end
always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        begin
            rgb_r_reg <= 8'hFF;
            rgb_g_reg <= 8'h00;
            rgb_b_reg <= 8'h00;
         end
    else if(region_active_d1)begin   //原始图片显示
            rgb_r_reg <= dout[23:16]; //[7:0]
            rgb_g_reg <= dout[15:8];//[15:8]
            rgb_b_reg <= dout[7:0];//
           end
    else if(region_ACT_d1)begin    //灰度图片显示
            rgb_r_reg <= line_data[17:10]; //[7:0]
            rgb_g_reg <= line_data[17:10];//[15:8]
            rgb_b_reg <= line_data[17:10];//
           end
    else begin
            rgb_r_reg <= 8'h00;
            rgb_g_reg <= 8'h00;
            rgb_b_reg <= 8'hff;
           end
 end

在这里插入图片描述

代码的设计一定要根据时序来理解,例如水平计数是从FT+SYNC+BP+activeX/Y计数,有效部分为最后那一部分的activeX/Y,所以像素点的计数才从那里开始。
在这里插入图片描述

最后结果展示:
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值