读取图片的方式按照博客咸鱼里的方式来做,如下。
1、先用matlab将图片转换成txt数据,代码如下
%==========================================================================
% 彩色图片转为txt文本,格式为24bit的hex数据
%==========================================================================
clc;
clear all;
%--------------------------------------------------------------------------
pre_img = imread('pre_img.jpg'); %读取图片文件
[ROW,COL,N] = size(pre_img); %获得图片尺寸[高度,长度,维度]
RGB_ij = uint64(zeros(ROW,COL)); %定义32位宽的RGB变量
%--------------------------------------------------------------------------
fid = fopen('pre_img.txt','w'); %打开文件
for i = 1:ROW
for j = 1:COL
R = double(pre_img(i,j,1));
G = double(pre_img(i,j,2));
B = double(pre_img(i,j,3));
%-------------------------------------
RGB = R*(2^16) + G*(2^8) + B;
RGB_ij(i,j) = RGB;
RGB_hex = dec2hex(RGB);
%-------------------------------------
fprintf(fid,'%s\n',RGB_hex); %将字符打印到txt文件
end
end
fclose(fid);
%--------------------------------------------------------------------------
imshow(pre_img),title('处理前'); %查看处理前的图片
2、FPGA读取txt数据
`timescale 1 ns/1 ns
module img_gen
//========================< 参数 >==========================================
// 640x480 @60Hz 25Mhz
#(
parameter H_ADDR = 640 , //行有效数据
parameter H_SYNC = 96 , //行同步
parameter H_BACK = 48 , //行显示后沿
parameter H_TOTAL = H_ADDR+H_SYNC+H_BACK , //行扫描周期
parameter V_ADDR = 480 , //场有效数据
parameter V_SYNC = 2 , //场同步
parameter V_BACK = 33 , //场显示后沿
parameter V_TOTAL = V_ADDR+V_SYNC+V_BACK //场扫描周期
)
//========================< 端口 >==========================================
(
//system --------------------------------------------
input wire clk , //时钟
input wire rst_n , //复位,低电平有效
//img output ----------------------------------------
output wire img_hsync , //img行信号
output wire img_vsync , //img场信号
output reg [23:0] img_data , //img数据信号
output reg img_de //img数据有效指示信号
);
//========================< 信号 >==========================================
reg [15:0] cnt_h ;
wire add_cnt_h ;
wire end_cnt_h ;
reg [15:0] cnt_v ;
wire add_cnt_v ;
wire end_cnt_v ;
//---------------------------------------------------
reg [23:0] ram [H_ADDR*V_ADDR-1:0] ;
reg [31:0] i ;
//==========================================================================
//== 行、场计数
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_h <= 0;
else if(add_cnt_h) begin
if(end_cnt_h)
cnt_h <= 0;
else
cnt_h <= cnt_h + 1;
end
end
assign add_cnt_h = 1;
assign end_cnt_h = add_cnt_h && cnt_h==H_TOTAL-1;
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_v <= 0;
else if(add_cnt_v) begin
if(end_cnt_v)
cnt_v <= 0;
else
cnt_v <= cnt_v + 1;
end
end
assign add_cnt_v = end_cnt_h;
assign end_cnt_v = add_cnt_v && cnt_v==V_TOTAL-1;
//==========================================================================
//== 输出
//==========================================================================
//行场同步
//---------------------------------------------------
assign img_hsync = (cnt_h <= H_SYNC - 1) ? 0 : 1;
assign img_vsync = (cnt_v <= V_SYNC - 1) ? 0 : 1;
//数据请求
assign img_req = (cnt_h >= H_SYNC + H_BACK - 1) && (cnt_h < H_SYNC + H_BACK + H_ADDR - 1) &&
(cnt_v >= V_SYNC + V_BACK ) && (cnt_v < V_SYNC + V_BACK + V_ADDR );
//读取txt文件到raw数组中,格式为16进制
//---------------------------------------------------
initial begin
$readmemh("C:/Users/xiaozhou/Desktop/people_face/matlab/pre_img.txt", ram);
end
//数据输出
//---------------------------------------------------
always@(posedge clk or negedge rst_n) begin
if(!rst_n)begin
img_data <= 24'd0;
i <= 0;
end
else if(img_req) begin
img_data <= ram[i];
i <= i + 1;
end
else if(i==H_ADDR*V_ADDR) begin
img_data <= 24'd0;
i <= 0;
end
end
//数据使能
always @(posedge clk) begin
img_de <= img_req;
end
endmodule
由于顶层模块传给img_gen的H_ADDR和V_ADDR分别是480和272(而不是原来的640和480),在行计数器在96+48=144和480+96+48-1=623之间,以及场计数器33+2=35和272+33+2-1=306之间的时候,使能信号拉高。
当行计数器为0~95时为低电平(行同步期间), 当行计数器为0~1时为低电平(场同步期间)。
3、FPGA灰度二值化
module RGB565_YCbCr444_face
//========================< 端口 >==========================================
(
input wire clk ,
input wire rst_n ,
//input ---------------------------------------------
input wire RGB_de ,
input wire RGB_hsync ,
input wire RGB_vsync ,
input wire [15:0] RGB_data , //RGB像素
//output --------------------------------------------
output wire face_de ,
output wire face_hsync ,
output wire face_vsync ,
output reg [15:0] face_data //灰度像素
);
//========================< 信号 >==========================================
wire [ 7:0] R0, G0, B0 ;
reg [15:0] R1, G1, B1 ;
reg [15:0] R2, G2, B2 ;
reg [15:0] R3, G3, B3 ;
reg [15:0] Y1, Cb1, Cr1 ;
reg [ 7:0] Y2, Cb2, Cr2 ;
reg [ 3:0] RGB_de_r ;
reg [ 3:0] RGB_hsync_r ;
reg [ 3:0] RGB_vsync_r ;
//==========================================================================
//== RGB565转RGB888
//==========================================================================
assign R0 = {RGB_data[15:11],RGB_data[13:11]};
assign G0 = {RGB_data[10: 5],RGB_data[ 6: 5]};
assign B0 = {RGB_data[ 4: 0],RGB_data[ 2: 0]};
//==========================================================================
//== RGB888转YCbCr
//==========================================================================
//clk 1
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
{R1,G1,B1} <= {16'd0, 16'd0, 16'd0};
{R2,G2,B2} <= {16'd0, 16'd0, 16'd0};
{R3,G3,B3} <= {16'd0, 16'd0, 16'd0};
end
else begin
{R1,G1,B1} <= { {R0 * 16'd77}, {G0 * 16'd150}, {B0 * 16'd29 } };
{R2,G2,B2} <= { {R0 * 16'd43}, {G0 * 16'd85}, {B0 * 16'd128} };
{R3,G3,B3} <= { {R0 * 16'd128}, {G0 * 16'd107}, {B0 * 16'd21 } };
end
end
//clk 2
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
Y1 <= 16'd0;
Cb1 <= 16'd0;
Cr1 <= 16'd0;
end
else begin
Y1 <= R1 + G1 + B1;
Cb1 <= B2 - R2 - G2 + 16'd32768; //128扩大256倍
Cr1 <= R3 - G3 - B3 + 16'd32768; //128扩大256倍
end
end
//clk 3,除以256即右移8位,即取高8位
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
Y2 <= 8'd0;
Cb2 <= 8'd0;
Cr2 <= 8'd0;
end
else begin
Y2 <= Y1[15:8];
Cb2 <= Cb1[15:8];
Cr2 <= Cr1[15:8];
end
end
//clk 4,人脸肤色检测
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
face_data <= 'h0;
end
else if( (Cb2 > 77) && (Cb2 < 127) && (Cr2 > 133) && (Cr2 < 173) ) begin
face_data <= 16'hffff;
end
else begin
face_data <= 'h0;
end
end
//==========================================================================
//== 信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
RGB_de_r <= 4'b0;
RGB_hsync_r <= 4'b0;
RGB_vsync_r <= 4'b0;
end
else begin
RGB_de_r <= {RGB_de_r[2:0], RGB_de};
RGB_hsync_r <= {RGB_hsync_r[2:0], RGB_hsync};
RGB_vsync_r <= {RGB_vsync_r[2:0], RGB_vsync};
end
end
assign face_de = RGB_de_r[3];
assign face_hsync = RGB_hsync_r[3];
assign face_vsync = RGB_vsync_r[3];
endmodule
使能信号和行场同步信号,均打了四拍 (上两图)。
RGB转YCbCr打了三拍(上图);二值化打了一拍(下图)。加起来总共打了四拍。
4、FPGA顶层模块及仿真
`timescale 1 ns/1 ns
module top
//========================< 参数 >==========================================
#(
parameter IMG_H = 480 , //图像长度
parameter IMG_V = 272 //图像高度
)
//========================< 端口 >==========================================
(
input wire clk , //时钟
input wire rst_n , //复位,低电平有效
//---------------------------------------------------
output wire VGA_hsync , //VGA行同步
output wire VGA_vsync , //VGA场同步
output wire [23:0] VGA_data , //数据
output wire VGA_de //数据有效
);
//========================< 信号 >==========================================
wire img_hsync ;
wire img_vsync ;
wire [23:0] img_data ;
wire img_de ;
//face ----------------------------------------------
wire face_de ;
wire face_hsync ;
wire face_vsync ;
wire [15:0] face_data ;
//==========================================================================
//== 图像数据产生
//==========================================================================
img_gen
#(
.H_ADDR (IMG_H ), //图像宽度
.V_ADDR (IMG_V ) //图像高度
)
u_img_gen
(
.clk (clk ),
.rst_n (rst_n ),
//-----------------------------------------------
.img_hsync (img_hsync ), //img输出行同步
.img_vsync (img_vsync ), //img输出场同步
.img_data (img_data ), //img输出数据
.img_de (img_de ) //img输出数据有效指示
);
//==========================================================================
//== RGB565转YCbCr444,再转人脸肤色检测
//==========================================================================
RGB565_YCbCr444_face u_RGB565_YCbCr444_face
(
.clk (clk ),
.rst_n (rst_n ),
//原图 ------------------------------------------
.RGB_de (img_de ),
.RGB_hsync (img_hsync ),
.RGB_vsync (img_vsync ),
.RGB_data ({img_data[23:19],img_data[15:10],img_data[7:3]}),
//肤色图 ----------------------------------------
.face_de (face_de ),
.face_hsync (face_hsync ),
.face_vsync (face_vsync ),
.face_data (face_data )
);
assign VGA_de = face_de;
assign VGA_hsync = face_hsync;
assign VGA_vsync = face_vsync;
assign VGA_data = { face_data[15:11],face_data[13:11],
face_data[10: 5],face_data[ 7: 5],
face_data[ 4: 0],face_data[ 2: 0]
};
endmodule
`timescale 1ns/1ns //时间精度
`define Clock 20 //时钟周期
//--------------------------------------------------------------------------------RGB转YCbCr------------------------------------------------------------------------------
module top_tb;
//========================< 参数 >==========================================
parameter IMG_H = 480 ; //图像长度
parameter IMG_V = 272 ; //图像高度
//========================< 信号 >==========================================
reg clk ; //时钟,50Mhz
reg rst_n ; //复位,低电平有效
wire VGA_hsync ; //VGA行同步
wire VGA_vsync ; //VGA场同步
wire [23:0] VGA_data ; //数据
wire VGA_de ; //数据有效
//==========================================================================
//== 模块例化
//==========================================================================
top
#(
.IMG_H (IMG_H ),
.IMG_V (IMG_V )
)
u_top
(
.clk (clk ),
.rst_n (rst_n ),
.VGA_hsync (VGA_hsync ),
.VGA_vsync (VGA_vsync ),
.VGA_data (VGA_data ),
.VGA_de (VGA_de )
);
//==========================================================================
//== 时钟信号和复位信号
//==========================================================================
initial begin
clk = 1;
forever
#(`Clock/2) clk = ~clk;
end
initial begin
rst_n = 0; #(`Clock*20+1);
rst_n = 1;
end
//==========================================================================
//== 图像数据转变为txt文本
//==========================================================================
//打开post_img.txt文件
//---------------------------------------------------
integer post_img_txt;
initial begin
post_img_txt = $fopen("C:/Users/xiaozhou/Desktop/people_face/matlab/gray_img.txt");
end
//像素写入到txt中
//---------------------------------------------------
reg [20:0] pixel_cnt;
always @(posedge clk) begin
if(!rst_n) begin
pixel_cnt <= 0;
end
else if(VGA_de) begin
pixel_cnt = pixel_cnt + 1;
$fdisplay(post_img_txt,"%h",VGA_data);
if(pixel_cnt == IMG_H*IMG_V)
$stop;
end
end
endmodule
5、通过modelsim将操作后的图片数据写入gray_img.txt里面后,用matlab将txt转成图片。代码如下
%==========================================================================
% txt文本中24bit数据还原为彩色图片
%==========================================================================
clear all;
clc;
%--------------------------------------------------------------------------
ROW = 272; %高度
COL = 480; %宽度
N = 3; %维度
post_img = uint8(zeros(ROW,COL,N));
%--------------------------------------------------------------------------
fid = fopen('gray_img.txt','r');
for i = 1:ROW
for j = 1:COL
value = fscanf(fid,'%s',1);
%------------------------------------------
post_img(i,j,1) = uint8(hex2dec(value(1:2)));
post_img(i,j,2) = uint8(hex2dec(value(3:4)));
post_img(i,j,3) = uint8(hex2dec(value(5:6)));
end
end
fclose(fid);
%--------------------------------------------------------------------------
pre_img = imread('pre_img.jpg');
subplot(121);imshow(pre_img), title('处理前');
subplot(122);imshow(post_img),title('处理后');
%--------------------------------------------------------------------------
imwrite(post_img,'gray_img.jpg'); %处理后的图片输出为jpg格式
以下是效果图:
由于图片有空洞、噪声等缺陷,所以后面要进行中值滤波、闭运算(先膨胀后腐蚀)等算法处理,然后再对人脸进行框选,最后显示出来。
中值滤波图片
闭运算(先膨胀后腐蚀)图片
框选后,最终的成像是下面这样的。