获取人脸图像——肤色提取(Ycbcr+阈值)——滤波处理(中值、腐蚀膨胀)——人脸框选——显示
肤色提取:顾名思义,将肤色从外界环境中提取出。在肤色识别算法中,常用YCbCr颜色空间(亮度、蓝色、红色分量),因为肤色在 YCbCr 空间受亮度信息的影响较小,从而肤色类聚性好,由此,在Ycbcr空间基础上,我们用人工阈值法将肤色与非肤色区域分开,最终形成二值图像,实现肤色的提取。
滤波处理:人脸内部可能存在黑点、人脸外的某些地方也可能会被误检测为人脸,这些情况都会造成识别失败,因此加入中值滤波以及腐蚀、膨胀,这些之前都整理过,不展开说了。
先进行Ycbcr空间转换得到亮度、蓝色、红色分量,给cb和cr设置阈值,即可将肤色提取出来。(共采用四级流水线)
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 endView Code
第四级肤色识别:
Cb和Cr设置阈值:Cb:77 ~ 127 ;Cr:133~173
;(前人大量研究得到的经验值),最终输出的结果是二值化结果
,目的是减少运算量。
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 endView Code
RGB565原图数据,内部进行Ycbcr转换,分别得到8位的Y,Cb,Cr分量,后根据蓝红分量的阈值得到16位的二值化肤色数据face_data,阈值内为白色,阈值外为黑色
检测出肤色后,为提高图像质量,进行中值滤波、腐蚀膨胀处理。
肤色检测出人脸后,我们用行列坐标画框,将人脸框选出来,最终人脸框和图像数据同时输出,原图图像数据是16位,因此前面肤色数据face_data也用的16位。RGB信号:原图数据、使能以及行场有效信号。
face信号:人脸肤色提取后的图像数据、使能以及行场有效信号。
因为两帧图像差别较小,因此我们将人脸肤色图像分两帧来处理,通过这两帧图像得到人脸框的坐标,这样可防止图像结果偏移的情况出现。其中第一帧得到框的四个顶点坐标,当前帧的输出即可实时的使用人脸框的四个顶点坐标。
always @(posedge clk) begin face_vsync_r <= face_vsync; end assign pos_vsync = face_vsync && ~face_vsync_r; assign neg_vsync = ~face_vsync && face_vsync_r;View Code
人脸图像纵坐标:
parameter COL = 11'd640 ; //图片长度 parameter ROW = 11'd480 ; //图片高度 always @(posedge clk or negedge rst_n) begin if(!rst_n) face_x <= 10'd0; else if(add_face_x) begin //人脸肤色数据有效 if(end_face_x) //数据有效且一行640像素计数完成 face_x <= 10'd0; else face_x <= face_x + 10'd1; //显示驱动生成的横坐标 end end assign add_face_x = face_de; assign end_face_x = add_face_x && face_x== COL-10'd1; always @(posedge clk or negedge rst_n) begin if(!rst_n) face_y <= 10'd0; else if(add_face_y) begin//一行数据计数完成 if(end_face_y)//一行数据完成且480场计数完成(一帧图像完成) face_y <= 10'd0; else face_y <= face_y + 10'd1;//显示驱动生成的纵坐标 end end assign add_face_y = end_face_x; assign end_face_y = add_face_y && face_y== ROW-10'd1;View Code
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin x_min <= COL; end else if(pos_vsync) begin //场有效上升沿 x_min <= COL; end else if(face_data==16'hffff && x_min > face_x && face_de) begin //有肤色数据,且框x最小坐标>肤色处x坐标 x_min <= face_x; //当前肤色x坐标就是框的x最小值 end end //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin x_max <= 0; end else if(pos_vsync) begin x_max <= 0; end else if(face_data==16'hffff && x_max < face_x && face_de) begin//框x最大坐标<肤色x坐标,那肤色x坐标就是框x最大值 x_max <= face_x; end end //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin y_min <= ROW; end else if(pos_vsync) begin y_min <= ROW; end else if(face_data==16'hffff && y_min > face_y && face_de) begin //同理 y_min <= face_y; end end //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin y_max <= 0; end else if(pos_vsync) begin y_max <= 0; end else if(face_data==16'hffff && y_max < face_y && face_de) begin//同理 y_max <= face_y; end endView Code
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin x_min_r <= 0; x_max_r <= 0; y_min_r <= 0; y_max_r <= 0; end else if(neg_vsync) begin x_min_r <= x_min; x_max_r <= x_max; y_min_r <= y_min; y_max_r <= y_max; end endView Code
原图行列技术器
always @(posedge clk or negedge rst_n) begin if(!rst_n) RGB_x <= 10'd0; else if(add_RGB_x) begin //原图像数据有效 if(end_RGB_x) //原图像数据有效且一行计数完成 RGB_x <= 10'd0; else RGB_x <= RGB_x + 10'd1; end end assign add_RGB_x = RGB_de; assign end_RGB_x = add_RGB_x && RGB_x== COL-10'd1; always @(posedge clk or negedge rst_n) begin if(!rst_n) RGB_y <= 10'd0; else if(add_RGB_y) begin //一行计数完成 if(end_RGB_y) //一行计数完成且一场计数完成 RGB_y <= 10'd0; else RGB_y <= RGB_y + 10'd1; end end assign add_RGB_y = end_RGB_x; assign end_RGB_y = add_RGB_y && RGB_y== ROW-10'd1;View Code
人脸原图输出:
always @(posedge clk or negedge rst_n) begin if(!rst_n) key_num <= 1'b0; else if(key_vld) key_num <= ~key_num; end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin TFT_de <= 1'b0; TFT_hsync <= 1'b0; TFT_vsync <= 1'b0; TFT_data <= 16'b0; end else if(key_num==1'b0) begin //按键按下的时候得到白色方框和原图 if((RGB_y >= y_min_r-1 && RGB_y <= y_min_r+1) && RGB_x >= x_min_r && RGB_x <= x_max_r) begin TFT_data <= 16'b11111_000000_00000; end else if((RGB_y >= y_max_r-1 && RGB_y <= y_max_r+1) && RGB_x >= x_min_r && RGB_x <= x_max_r) begin TFT_data <= 16'b11111_000000_00000; end else if((RGB_x >= x_min_r-1 && RGB_x <= x_min_r+1) && RGB_y >= y_min_r && RGB_y <= y_max_r) begin TFT_data <= 16'b11111_000000_00000; end else if((RGB_x >= x_max_r-1 && RGB_x <= x_max_r+1) && RGB_y >= y_min_r && RGB_y <= y_max_r) begin TFT_data <= 16'b11111_000000_00000; end else begin TFT_de <= RGB_de; TFT_hsync <= RGB_hsync; TFT_vsync <= RGB_vsync; TFT_data <= RGB_data; end end else if(key_num==1'b1) begin //按键释放的时候得到白色方框和二值化腐蚀膨胀后的图像数据 if((face_y >= y_min_r-1 && face_y <= y_min_r+1) && face_x >= x_min_r && face_x <= x_max_r) begin TFT_data <= 16'b11111_000000_00000; end else if((face_y >= y_max_r-1 && face_y <= y_max_r+1) && face_x >= x_min_r && face_x <= x_max_r) begin TFT_data <= 16'b11111_000000_00000; end else if((face_x >= x_min_r-1 && face_x <= x_min_r+1) && face_y >= y_min_r && face_y <= y_max_r) begin TFT_data <= 16'b11111_000000_00000; end else if((face_x >= x_max_r-1 && face_x <= x_max_r+1) && face_y >= y_min_r && face_y <= y_max_r) begin TFT_data <= 16'b11111_000000_00000; end else begin TFT_de <= face_de; TFT_hsync <= face_hsync; TFT_vsync <= face_vsync; TFT_data <= face_data; endView Code
前一帧获得AB
always @(posedge clk) begin face_vsync_r <= face_vsync; endView Code
图像流经未开始或者结束的时候,最小像素值255,最大像素值0;当图像流过且像素有效时候,开始进行像素比较,得到一帧图像中的最小最大像素值。
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin max <= 8'd0; min <= 8'd255; end else if(Y_vsync && Y_de) begin //像素有效时 max <= (max > Y_data) ? max : Y_data; min <= (min < Y_data) ? min : Y_data; end else if(neg_Y_vsync) begin //一帧图像结束时 max <= 8'd0; min <= 8'd255; end endView Code
基于FPGA:RGB转YCBCR
//RGB分量转Gray灰度图 module RGB_Gray //========================< 端口 >========================================== ( input wire clk , //时钟 input wire rst_n , //复位 //原图 ---------------------------------------------- input wire RGB_hsync , input wire RGB_vsync , input wire [23:0] RGB_data , input wire RGB_de , //灰度转换图 ---------------------------------------------- output wire gray_hsync , //这里的行场同步信号也就是最终的VGA行场同步信号。 output wire gray_vsync , //需要根据实际消耗的时钟进行延迟 output wire [23:0] gray_data , output wire gray_de ); 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 [16:0] Y1,Cb1,Cr1; reg [23:0] Y2,Cb2,Cr2; reg [7:0] RGB_de_r ; reg [7:0] RGB_hsync_r ; reg [7:0] RGB_vsync_r ; //将24位RGB分成三分量 assign R0 = RGB_data[23:16]; assign G0 = RGB_data[15:8]; assign B0 = RGB_data[7:0]; //=============根据RGB转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位 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 >> 8; Cb2 <= Cb1>> 8; Cr2 <= Cr1>> 8; end end // 取YcbCr三分量中的Y分量,将其赋值给RGB888通道即可。 assign gray_data = {Y2[7:0],Y2[7:0],Y2[7:0]}; //=================== 信号同步================================== //为确保图像能正常显示,要保持数据与数据使能和行场有效信号同步 //前面我们花费了三个clk来计算,因此延迟三拍 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin RGB_de_r <= 3'b0; RGB_hsync_r <= 3'b0; RGB_vsync_r <= 3'b0; end else begin RGB_de_r <= {RGB_de_r[1:0], RGB_de}; RGB_hsync_r <= {RGB_hsync_r[1:0], RGB_hsync}; RGB_vsync_r <= {RGB_vsync_r[1:0], RGB_vsync}; end end assign gray_de = RGB_de_r[2]; assign gray_hsync = RGB_hsync_r[2]; assign gray_vsync = RGB_vsync_r[2]; endmoduleView Code
基于FPGA:运动目标检测
在Verlog里面,使用组合逻辑不能直接按上述公式(2)在计算,不然组合逻辑延时太大,导致时序不收敛,这里就需要添加寄存器来切割流水线;利用FPGA 并行处理的特点加速计算。这里分三级流水线处理:第一级流水线计算所有乘法; 第二级流水线计算所有加法,把正的和负的分开进行加法;第三级流水线计算最终的和,若为负数取0。输入到输岀有三个clock的时延仿真波形如下图所示
always@(posedge clk or negedge rst_n) begin if(!rst_n) begin {matrix_p11, matrix_p12, matrix_p13} <= 3'b0; {matrix_p21, matrix_p22, matrix_p23} <= 3'b0; {matrix_p31, matrix_p32, matrix_p33} <= 3'b0; end else if(read_frame_href) begin if(read_frame_clken) //Shift_RAM data read clock enable begin {matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data}; //1th shift input {matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data}; //2th shift input {matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data}; //3th shift input end else begin {matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13}; {matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23}; {matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33}; end endView Code
通过行场信号,设计行列计数器,从而可以获取图像每个像素点的坐标信息,然后设计4个寄存器分别是edg_up 、 edg_down、edg_left、edg_right目标的上下左右四个点,实时与行列计数器比较,也就是求最大值( edg_down和edg_right)和最小值(edg_up和edg_left),代码如下所示:
always@(posedge clk or negedge rst_n) begin if(!rst_n) begin edg_up <= 10'd479; edg_down <= 10'd0; edg_left <= 10'd639; edg_right <= 10'd0; end else if(vsync_rising) begin edg_up <= 10'd479; edg_down <= 10'd0; edg_left <= 10'd639; edg_right <= 10'd0; end else if(per_frame_clken & per_frame_href)begin if(per_img_Y == 1'b1) begin if(edg_up > v_cnt) edg_up <=v_cnt ; else edg_up <=edg_up ; if(edg_down < v_cnt) edg_down <=v_cnt ; else edg_down <=edg_down ; if(edg_left > h_cnt) edg_left <= h_cnt ; else edg_left <=edg_left ; if(edg_right < h_cnt) edg_right <=h_cnt ; else edg_right <=edg_right ; end end endView Code
标签:begin,人脸识别,FPGA,clk,face,RGB,图像处理,&&,rst From: https://www.cnblogs.com/shiningleo007/p/18011687