…# 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进行显示查看是否正确。
- 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);
- 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)]
- 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,所以像素点的计数才从那里开始。
文章来源:https://www.toymoban.com/news/detail-480767.html
最后结果展示:
文章来源地址https://www.toymoban.com/news/detail-480767.html
到了这里,关于Matlab+FPGA进行灰度图像处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!