FPGA图像处理_色彩空间转换(含源码)

在日常生活中我们最常用的是RGB色彩空间,此外还有一些色彩空间(也称彩色模型),它们在某些应用中可能比RGB更方便和更恰当,这些模型是RGB模型的变换,包括NTSC、YCbCr、HSV、CMY和HSI等模型,它们之间存在在相互转换的计算公式,下面就常用的YCbCr和
HSV模型展开介绍。
YCbCr模型介绍:YCbCr模型广泛用于数字视频中,在这种格式下,亮度信息用单个分量Y表示,彩色信息存储为两个色差分量Cb和Cr,分量Cb是蓝色分量和参考值的差,分量Cr是红色分量和参考值的差,其转换公式为:
{ Y = 0.299 R + 0.587 G + 0.114 B C b = − 0.172 R − 0.339 G + 0.511 B + 128 C r = 0.511 R − 0.428 G − 0.083 B + 128 \left\{ \begin{array}{c} Y=0.299R + 0.587G + 0.114B \\ Cb=-0.172R - 0.339G + 0.511B +128 \\ Cr= 0.511R - 0.428G -0.083B + 128 \\ \end{array} \right. Y=0.299R+0.587G+0.114BCb=0.172R0.339G+0.511B+128Cr=0.511R0.428G0.083B+128
注意:由于Verilog HDL无法进行浮点运算,因此使用扩大256倍,再向右移8Bit的方式,来转换公式,则变换之后的公式为:
{ Y = ( ( 77 R + 150 G + 29 B ) > > 8 ) C b = ( ( − 43 R − 85 G + 128 B + 32768 ) > > 8 ) C r = ( ( 128 R − 107 G − 21 B + 32768 ) > > 8 ) \left\{ \begin{array}{c} Y=((77R + 150G + 29B)>>8) \\ Cb=((-43R - 85G + 128B + 32768)>>8) \\ Cr= ((128R - 107G -21B + 32768)>>8) \\ \end{array} \right. Y=((77R+150G+29B)>>8)Cb=((43R85G+128B+32768)>>8)Cr=((128R107G21B+32768)>>8)
由公式可以看出,转换过程中,并不会出现结果为负数的情形,在算法移植中记得对运算过程进行拆分,其代码编写如下,可以看到,输出相较于输出分量延时3个CLK:

`timescale 1ns / 1ps

module VIP_RGB888_YCbCr(
    input clk,
    input rst_n,
    
    //Image data prepred to be processd
	input				per_frame_vsync,	//Prepared Image data vsync valid signal
	input				per_frame_href,		//Prepared Image data href vaild  signal
	input				per_frame_clken,	//Prepared Image data output/capture enable clock	
	input		[7:0]	per_img_red,		//Prepared Image red data to be processed
	input		[7:0]	per_img_green,		//Prepared Image green data to be processed
	input		[7:0]	per_img_blue,		//Prepared Image blue data to be processed
	
	//Image data has been processd
	output				post_frame_vsync,	//Processed Image data vsync valid signal
	output				post_frame_href,	//Processed Image data href vaild  signal
	output				post_frame_clken,	//Processed Image data output/capture enable clock	
	output		[7:0]	post_img_Y,			//Processed Image brightness output
	output		[7:0]	post_img_Cb,		//Processed Image blue shading output
	output		[7:0]	post_img_Cr			//Processed Image red shading output

    );
    
//Step 1 计算出Y\Cb\Cr中每一个乘法的乘积
reg [15:0] img_red_r0, img_red_r1, img_red_r2;
reg [15:0] img_green_r0, img_green_r1, img_green_r2;
reg [15:0] img_blue_r0, img_blue_r1, img_blue_r2;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        img_red_r0 <= 0;
        img_red_r1 <= 0;
        img_red_r2 <= 0;
        img_green_r0 <= 0;
        img_green_r1 <= 0;
        img_green_r2 <= 0;
        img_blue_r0 <= 0;
        img_blue_r1 <= 0;
        img_blue_r2 <= 0;
    end
    else begin
        img_red_r0 <= per_img_red * 8'd77;
        img_red_r1 <= per_img_red * 8'd43;
        img_red_r2 <= per_img_red * 8'd128;
        img_green_r0 <= per_img_green * 8'd150;
        img_green_r1 <= per_img_green * 8'd85;
        img_green_r2 <= per_img_green * 8'd107;
        img_blue_r0 <= per_img_blue * 8'd29;
        img_blue_r1 <= per_img_blue * 8'd128;
        img_blue_r2 <= per_img_blue * 8'd21;
    end
end

//Step 2 计算出Y、Cb、Cr括号内的值
reg [15:0] img_Y_r0;
reg [15:0] img_Cb_r0;
reg [15:0] img_Cr_r0;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        img_Y_r0 <= 0;
        img_Cb_r0 <= 0;
        img_Cr_r0 <= 0;
    end
    else begin
        img_Y_r0 <= img_red_r0 + img_green_r0 + img_blue_r0;
        img_Cb_r0 <= img_blue_r1 - img_red_r1 - img_green_r1 + 16'd32768;
        img_Cr_r0 <= img_red_r2 - img_green_r2 - img_blue_r2 + 16'd32768;
    end
end

//Step 3 右移8bit,将数值缩小256倍
reg [7:0] img_Y_r1;
reg [7:0] img_Cb_r1;
reg [7:0] img_Cr_r1;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        img_Y_r1 <= 0;
        img_Cb_r1 <= 0;
        img_Cr_r1 <= 0;
    end
    else begin
        img_Y_r1 <= img_Y_r0[15:8];
        img_Cb_r1 <= img_Cb_r0[15:8];
        img_Cr_r1 <= img_Cr_r0[15:8];
    end
end

//lag 3 clocks signal sync
reg [2:0] per_frame_vsync_r;
reg [2:0] per_frame_href_r;
reg [2:0] per_frame_clken_r;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        per_frame_vsync_r <= 0;
        per_frame_href_r <= 0;
        per_frame_clken_r <= 0;
    end
    else begin
        per_frame_vsync_r <= {per_frame_vsync_r[1:0], per_frame_vsync};
        per_frame_href_r <= {per_frame_href_r[1:0], per_frame_href};
        per_frame_clken_r <= {per_frame_clken_r[1:0], per_frame_clken};
    end
end

assign post_frame_vsync = per_frame_vsync_r[2];
assign post_frame_href = per_frame_href_r[2];
assign post_frame_clken = per_frame_clken_r[2];
assign post_img_Y = post_frame_href ? img_Y_r1 : 8'd0;
assign post_img_Cb = post_frame_href ? img_Cb_r1: 8'd0;
assign post_img_Cr = post_frame_href ? img_Cr_r1: 8'd0;

endmodule

HSV模型展开介绍
h = { 0 ° , max = min 60 ° ∗ g − b m a x − m i n + 0 ° , max = r,g>=b 60 ° ∗ g − b m a x − m i n + 360 ° , max = r,g=b} \\[2ex] 60°*\frac{g-b}{max-min}+360°, & \text{max = r,gh= ,60°maxmingb+,60°maxmingb+360°,60°maxminbr+120°,60°maxminrg+240°,max = minmax = r,g>=bmax = r,gmax =gmax =b
s = { 0 , max = 0 m a x − m i n m a x , otherwise s = \begin{cases} 0, & \text{max = 0} \\[2ex] \frac{max-min}{max}, & \text{otherwise} \\[2ex] \end{cases} s= 0,maxmaxmin,max = 0otherwise
v = m a x v=max v=max
类比RGB转YCbCr代码,同样需要对RGB转HSV公式进行拆分,延迟于输出分量R、G、B分量4个CLK,即可得到H(色调)、S(饱和度)、V(亮度)分量,但需要注意的是H和S分量位宽为9bits,而V分量位宽为8bits。

`timescale 1ns / 1ps

module VIP_RGB888_HSV(
    input   clk,
    input   rst_n,
    
    //Image data prepred to be processd
	input				per_frame_vsync,	//Prepared Image data vsync valid signal
	input				per_frame_href,		//Prepared Image data href vaild  signal
	input				per_frame_clken,	//Prepared Image data output/capture enable clock	
	input		[7:0]	per_img_red,		//Prepared Image red data to be processed
	input		[7:0]	per_img_green,		//Prepared Image green data to be processed
	input		[7:0]	per_img_blue,		//Prepared Image blue data to be processed
	
	//Image data has been processd
	output				post_frame_vsync,	//Processed Image data vsync valid signal
	output				post_frame_href,	//Processed Image data href vaild  signal
	output				post_frame_clken,	//Processed Image data output/capture enable clock	
	output		[8:0]	post_img_H,			//Processed Image brightness output
	output		[8:0]	post_img_S,		//Processed Image blue shading output
	output		[7:0]	post_img_V			//Processed Image red shading output

    );
        
//step1 将RGB分量统一扩大60倍,与输入相比,延时1个CLK
reg [13:0] per_img_red_r    ;
reg [13:0] per_img_green_r  ;
reg [13:0] per_img_blue_r   ;

always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        per_img_red_r   <=  14'd0;
        per_img_green_r <=  14'd0;
        per_img_blue_r  <=  14'd0;
    end
    else begin
        per_img_red_r   <=  per_img_red *60;
        per_img_green_r <=  per_img_green *60;
        per_img_blue_r  <=  per_img_blue *60;
    end
end

//step2 计算最大值和最小值,与输入相比,延时1个CLK
reg [7:0] max ;  //用来存放R\G\B三分量最大值
reg [7:0] min ;  //用来存放R\G\B三分量最小值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        max <= 8'd0;
    else if((per_img_red >= per_img_blue)&&(per_img_red >= per_img_green))
        max <= per_img_red  ;
    else if((per_img_blue >= per_img_red)&&(per_img_blue >= per_img_green))
        max <= per_img_blue  ;
    else if((per_img_green >= per_img_blue)&&(per_img_green >= per_img_red))
        max <= per_img_green  ;    
end

always @ (posedge clk or negedge rst_n) begin
     if(!rst_n)
        min <= 8'd0;
    else if((per_img_red <= per_img_blue)&&(per_img_red <= per_img_green))
        min <= per_img_red;
    else if((per_img_blue <= per_img_red)&&(per_img_blue <= per_img_green))
        min <= per_img_blue;
    else if((per_img_green <= per_img_blue)&&(per_img_green <= per_img_red))
        min <= per_img_green;   
end

//step3 为了与最大值进行比较,与输入相比,延时1个CLK
reg [7:0] per_img_red_dly;
reg [7:0] per_img_blue_dly;
reg [7:0] per_img_green_dly;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        per_img_red_dly     <=  8'd0;  
        per_img_blue_dly    <=  8'd0; 
        per_img_green_dly   <=  8'd0;
    end
    else begin
        per_img_red_dly     <=  per_img_red;
        per_img_blue_dly    <=  per_img_blue;
        per_img_green_dly   <=  per_img_green;
    end
end

//step4 存放除数,与输入相比,延时2个CLK
reg [7:0] max_min;
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n)
        max_min <=  8'd0;
    else
        max_min <=  max - min;
end

//step5 存放被除数,与输入相比,延时2个CLK
reg [13:0] g_b;
reg [13:0] b_r;
reg [13:0] r_g;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        g_b <=  14'd0;
        b_r <=  14'd0;
        r_g <=  14'd0;
    end
    else if(per_img_green_r >= per_img_blue_r)
        g_b <=  per_img_green_r - per_img_blue_r;
    else if(per_img_green_r < per_img_blue_r)
        g_b <=  per_img_blue_r - per_img_green_r;   
        
    else if(per_img_blue_r >= per_img_red_r)
        b_r <=  per_img_blue_r - per_img_red_r;
    else if(per_img_blue_r < per_img_red_r)
        b_r <=  per_img_red_r - per_img_blue_r;   
        
    else if(per_img_red_r >= per_img_green_r)
        r_g <=  per_img_red_r - per_img_green_r;   
    else if(per_img_red_r < per_img_green_r)
        r_g <=  per_img_green_r - per_img_red_r; 
end

//step 6 将max min r g b 再次延时,与输入相比,延时2个CLK
reg [7:0] per_img_red_r2    ;
reg [7:0] per_img_blue_r2   ;
reg [7:0] per_img_green_r2  ;
reg [7:0] max_r2    ;
reg [7:0] min_r2    ;
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        per_img_red_r2      <=  8'd0;
        per_img_blue_r2     <=  8'd0;  
        per_img_green_r2    <=  8'd0;
        max_r2              <=  8'd0;
        min_r2              <=  8'd0;
    end
    else begin
        per_img_red_r2      <=  per_img_red_dly;
        per_img_blue_r2     <=  per_img_blue_dly;
        per_img_green_r2    <=  per_img_green_dly;
        max_r2              <=  max;
        min_r2              <=  min;
    end
end

//step7 存放除法结果,与输入相比,延时3个CLK
reg [13:0] temp;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        temp    <=  14'd0;
    else if(max_r2 == min_r2)
        temp    <=  14'd0;
    else if(max_r2 == per_img_red_r2)
        temp    <=  g_b/{6'd0,max_min};
    else if(max_r2 == per_img_green_r2)
        temp    <=  b_r/{6'd0,max_min};
    else if(max_r2 == per_img_blue_r2)
        temp    <=  r_g/{6'd0,max_min};
end

//step 6 将max min r g b 再次延时,与输入相比,延时3个CLK
reg [7:0] per_img_red_r3    ;
reg [7:0] per_img_blue_r3   ;
reg [7:0] per_img_green_r3  ;
reg [7:0] max_r3    ;
reg [7:0] min_r3    ;
reg [7:0] max_min_r3;
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        per_img_red_r3      <=  8'd0;
        per_img_blue_r3     <=  8'd0;  
        per_img_green_r3    <=  8'd0;
        max_r3              <=  8'd0;
        min_r3              <=  8'd0;
        max_min_r3          <=  8'd0;
    end
    else begin
        per_img_red_r3      <=  per_img_red_r2;
        per_img_blue_r3     <=  per_img_blue_r2;
        per_img_green_r3    <=  per_img_green_r2;
        max_r3              <=  max_r2;
        min_r3              <=  min_r2;
        max_min_r3          <=  max_min;
    end
end

//step8 计算H分量,与输入相比,延时4个CLK
reg [8:0] post_img_H_r;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        post_img_H_r    <=  9'd0;
    else if(max_r3 == min_r3)
        post_img_H_r    <=  9'd0;
    else if(max_r3 == per_img_red_r3)
        post_img_H_r    <=  (per_img_green_r3 >= per_img_blue_r3)?temp:(14'd360 - temp);
    else if(max_r3 == per_img_green_r3)
        post_img_H_r    <=  (per_img_blue_r3 >= per_img_red_r3)?(temp + 14'd120):(14'd120 - temp);
    else if(max_r3 == per_img_blue_r3)
        post_img_H_r    <=  (per_img_red_r3 >= per_img_green_r3)?(temp + 14'd240):(14'd240 - temp);
end

//step9 计算S分量,与输入相比,延时4个CLK
reg [15:0] post_img_S_r;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        post_img_S_r    <=  16'd0;
    else if(max_r3 == 8'd0)
        post_img_S_r    <=  16'd0;
    else
        post_img_S_r    <=  {max_min_r3,8'b0}/{8'b0,max_r3};
end

//step10 计算V分量,与输入相比,延时4个CLK
reg [7:0] post_img_V_r;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        post_img_V_r    <=  8'd0;
    else 
        post_img_V_r    <= max_r3;
end

assign post_img_H = post_img_H_r[8:0];
assign post_img_S = post_img_S_r[8:0];
assign post_img_V = post_img_V_r     ;

//为了保持同步,控制信号,相比于输入信号,延时4个CLK
reg [3:0] post_frame_vsync_delay;
reg [3:0] post_frame_href_delay ;
reg [3:0] post_frame_clken_delay;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        post_frame_vsync_delay <=4'd0;
        post_frame_href_delay  <=4'd0;
        post_frame_clken_delay <=4'd0;
    end
    else begin
        post_frame_vsync_delay <={post_frame_vsync_delay[2:0],per_frame_vsync};
        post_frame_href_delay  <={post_frame_href_delay [2:0],per_frame_href};
        post_frame_clken_delay <={post_frame_clken_delay[2:0],per_frame_clken};
    end
end

assign post_frame_vsync = post_frame_vsync_delay[3];
assign post_frame_href = post_frame_href_delay[3];
assign post_frame_clken = post_frame_clken_delay[3];
endmodule

参考文献:阮秋琦 《数字图像处理》;
CrazyBingo 《基于VIP_Board Big的FPGA入门进阶及图像处理算法开发教程》;

你可能感兴趣的:(#,基于FPGA的图像处理,fpga开发,图像处理,人工智能)