本帖最后由 UT发布 于 2025-4-3 10:31 编辑
软件版本:Anlogic -TD5.6.1-64bit 操作系统:WIN10 64bit 硬件平台:适用安路(Anlogic)FPGA 1概述本文简述了图像肤色设别的人脸检测的算法,讲解如何进行Verilog的算法实现,并进行上板实验。 2算法原理简介在《human skin color clustering for face detection》一篇文章中,讲解了如何使用肤色识别进行了人脸检测,算法的推理过程,这里不讲解,感兴趣的可以去看看原文。这里只拿来论文中的结论进行实践,论文中提到了基于RGB颜色空间的肤色识别,判别式如下: R>95and G>40 and B>20 and R>G and R>B and Max(R,G,B)-Min(R,G,B)>15 and abs(R-G)>15 基于YCbCr颜色空间的肤色识别,将图像转换到YCbCr颜色空间,然后按下述计算式判断是否属于皮肤域: (Cb > 77 and Cb7 < 127) and (Cr > 133 and Cr < 173) 肤色识别算法计算量小,算法实践简单,缺点也很明显,容易受到外界背景干扰。 具体的计算过程是,将实时的帧图像从RGB转换成YCbCr,然后按照上述的肤色识别计算公式,将肤色的区域标记,然后进行图像二值化运算,区分出背景和肤色部分,进行膨胀、腐蚀去除噪点,最后将肤色区域进行框选叠加在实时的视频中。 在框选的算法实现中需要找出两个坐标点,被检测到的肤色形状的左上和右下两个点的坐标,这个两个坐标点的特点是肤色形状的横纵坐标的最大最小值,通过遍历所有点,可以得出这两个坐标点。然后以这两个点得出长方形的框所在行和列,进行颜色的替换。 3工程实现
3.1Verilog代码分析
- // ycbcr0(:,:,1) = 0.2568*image_in_r + 0.5041*image_in_g + 0.0979*image_in_b + 16;
- // ycbcr0(:,:,2) = -0.1482*image_in_r - 0.2910*image_in_g + 0.4392*image_in_b + 128;
- // ycbcr0(:,:,3) = 0.4392*image_in_r - 0.3678*image_in_g - 0.0714*image_in_b + 128;
-
- // ycbcr0(:,:,1) = 256*( 0.2568*image_in_r + 0.5041*image_in_g + 0.0979*image_in_b + 16 )>>8;
- // ycbcr0(:,:,2) = 256*(-0.1482*image_in_r - 0.2910*image_in_g + 0.4392*image_in_b + 128)>>8;
- // ycbcr0(:,:,3) = 256*( 0.4392*image_in_r - 0.3678*image_in_g - 0.0714*image_in_b + 128)>>8;
-
- // ycbcr0(:,:,1) = (66*image_in_r + 129*image_in_g + 25*image_in_b + 4096 )>>8;
- // ycbcr0(:,:,2) = (-38*image_in_r - 74*image_in_g + 112*image_in_b + 32768)>>8;
- // ycbcr0(:,:,3) = (112*image_in_r - 94*image_in_g - 18*image_in_b + 32768 )>>8;
- 声明变量的名字如下:
- reg [15:0] r_d0;
- reg [15:0] g_d0;
- reg [15:0] b_d0;
-
- reg [15:0] r_d1;
- reg [15:0] g_d1;
- reg [15:0] b_d1;
-
- reg [15:0] r_d2;
- reg [15:0] g_d2;
- reg [15:0] b_d2;
-
- reg [15:0] y_d0;
- reg [15:0] cb_d0;
- reg [15:0] cr_d0;
-
- reg [7:0] y_d1;
- reg [7:0] cb_d1;
- reg [7:0] cr_d1;
-
- reg [7:0] skin_color_r;
- reg [7:0] skin_color_g;
- reg [7:0] skin_color_b;
-
- reg [3:0] hsyn;
- reg [3:0] vsyn;
- reg [3:0] de;
复制代码
执行乘法运算如下: - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- r_d0 <= 16'd0;
- g_d0 <= 16'd0;
- b_d0 <= 16'd0;
- r_d1 <= 16'd0;
- g_d1 <= 16'd0;
- b_d1 <= 16'd0;
- r_d2 <= 16'd0;
- g_d2 <= 16'd0;
- b_d2 <= 16'd0;
- end
- else
- begin
- r_d0 <= 66 * i_r;
- g_d0 <= 129 * i_g;
- b_d0 <= 25 * i_b;
- r_d1 <= 38 * i_r;
- g_d1 <= 74 * i_g;
- b_d1 <= 112 * i_b;
- r_d2 <= 112 * i_r;
- g_d2 <= 94 * i_g;
- b_d2 <= 18 * i_b;
- end
- end
复制代码
执行加法如下: - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- y_d0 <= 16'd0;
- cb_d0 <= 16'd0;
- cr_d0 <= 16'd0;
- end
- else
- begin
- y_d0 <= r_d0 + g_d0 + b_d0 + 4096 ;
- cb_d0 <= b_d1 - r_d1 - g_d1 + 32768;
- cr_d0 <= r_d2 - g_d2 - b_d2 + 32768;
- end
- end
- 求得Ycbcr的值如下:
- always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- y_d1 <= 16'd0;
- cb_d1 <= 16'd0;
- cr_d1 <= 16'd0;
- end
- else
- begin
- y_d1 <= y_d0[15:8];
- cb_d1 <= cb_d0[15:8];
- cr_d1 <= cr_d0[15:8];
- end
- end
复制代码
进行肤色的识别计算,如下: - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- skin_color_r <= 8'd0;
- skin_color_g <= 8'd0;
- skin_color_b <= 8'd0;
- end
- else if(cb_d1 > 75 && cb_d1 < 125 && cr_d1 > 131 && cr_d1 < 185)
- begin
- skin_color_r <= 8'd255;
- skin_color_g <= 8'd255;
- skin_color_b <= 8'd255;
- end
- else
- begin
- skin_color_r <= 8'd0;
- skin_color_g <= 8'd0;
- skin_color_b <= 8'd0;
- end
- end
复制代码
进行时序的同步设计: - always@(posedge i_clk )
- begin
- hsyn <= {hsyn[2:0],i_hsyn};
- vsyn <= {vsyn[2:0],i_vsyn};
- de <= {de[2:0],i_en};
- end
复制代码
进行输出的赋值: - assign o_hs = hsyn[3];
- assign o_vs = vsyn[3];
- assign o_r = skin_color_r;
- assign o_g = skin_color_g;
- assign o_b = skin_color_b;
- assign o_en = de[3];
复制代码
以下是求取识别区域框选的算法实现。 定义显示区域的大小与选框的颜色。 - parameter H_ACTIVE = 1024; //显示区域宽度
- parameter V_ACTIVE = 600; //显示区域高度
- parameter R_VALUE = 0;//显示选框颜色
- parameter G_VALUE = 0;//显示选框颜色
- parameter B_VALUE = 0;//显示选框颜色
复制代码
变量声明如下: - reg [10:0] h_cnt;
- reg [10:0] v_cnt;
- reg i_vsyn_d;
-
- reg [10:0] skin_h_min;
- reg [10:0] skin_h_max;
- reg [10:0] skin_v_min;
- reg [10:0] skin_v_max;
-
- reg [10:0] skin_h_min_d;
- reg [10:0] skin_h_max_d;
- reg [10:0] skin_v_min_d;
- reg [10:0] skin_v_max_d;
复制代码
进行数据的打拍: 复制代码
将得到的坐标值进行锁存,供后面使用,每帧数据更新一次。 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- skin_h_min_d <= 'd0;
- skin_h_max_d <= 'd0;
- skin_v_min_d <= 'd0;
- skin_v_max_d <= 'd0;
- end
- else if(~i_vsyn & i_vsyn_d)
- begin
- skin_h_min_d <= skin_h_min;
- skin_h_max_d <= skin_h_max;
- skin_v_min_d <= skin_v_min;
- skin_v_max_d <= skin_v_max;
- end
- end
复制代码
计算框选的四边的位置进行框的替换写入: - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- o_r <= 'd0;
- o_g <= 'd0;
- o_b <= 'd0;
- end
- // else if((i_x_pos == skin_h_min_d && i_y_pos >= skin_v_min_d && i_y_pos <= skin_v_max_d)||
- // (i_x_pos == skin_h_max_d && i_y_pos >= skin_v_min_d && i_y_pos <= skin_v_max_d)||
- // (i_y_pos == skin_v_min_d && i_x_pos >= skin_h_min_d && i_x_pos <= skin_h_max_d)||
- // (i_y_pos == skin_v_max_d && i_x_pos >= skin_h_min_d && i_x_pos <= skin_h_max_d))
- else if(((i_x_pos == skin_h_min_d || i_x_pos == skin_h_max_d ) && i_y_pos >= skin_v_min_d && i_y_pos <= skin_v_max_d)||
- ((i_y_pos == skin_v_min_d || i_y_pos == skin_v_max_d ) && i_x_pos >= skin_h_min_d && i_x_pos <= skin_h_max_d))
- begin
- o_r <= R_VALUE;
- o_g <= G_VALUE;
- o_b <= B_VALUE;
- end
- else
- begin
- o_r <= i_r_original;
- o_g <= i_g_original;
- o_b <= i_b_original;
- end
- end
复制代码
3.2工程结构工程结构如图所示: 图像数据通过摄像头采集进来,先缓存在fifo中,然后通过写状态机,将图像数据送进DDR进行缓存,缓存后的图像数据从DDR中取出,通过读状态机送出到fifo中,然后算法处理模块在fifo中取出数据,取出的数据分别送入肤色识别的算法和款选的算法,进行肤色识别后需要进行腐蚀和膨胀算法去除噪点,最后完成数据处理后送到LCD进行显示输出。 4上板实验点击下载后,可以看到正常的输出如下所示,摄像头的分辨率为640x480,以画中画的形式与LCD一起进行显示。 在背景不复杂的情况下,可以实时识别肤色相关的物体,如图所示:
|