RGB转YCbCr——基于《基于MATLAB与FPGA的图像处理教程》

发布时间 2023-12-01 17:01:58作者: carpe--diem

YCbCr介绍

YCbCr由Y、Cb、Cr组成。为一种数字信号
1、Y:表示颜色的明亮度和浓度,也可叫灰度阶。(通过RGB转换YCBCR提取Y分量也可以得到灰度图像)
2、Cb:表示颜色的蓝色浓度偏移量即RGB输入信号蓝色部分与RGB信号亮度值之间的差异。
3、Cr:表示颜色的红色浓度偏移量即RGB输入信号红色部分与RGB信号亮度值之间的差异。

转换公式

:::info
Y=0.299R+0.587G+0.114B R=Y+1.371(Cr-128)
Cb=128-0.169R-0.331G+0.500B G=Y-0.698(Cr-128)-0.336(Cb-128)
Cr=128+0.5R-0.419G-0.081B B=Y+1.732(Cb-128)

R、G、B、Y、Cb、Cr [0,255]
:::

FPGA硬件实现推导

Y=0.299R+0.587G+0.114B
1、去掉浮点数,暂不考虑精度,将数值扩大256倍。
Y1=76.544R+150.272G+29.184B≈76R+150G+29B
2、数值扩大了256倍,2的8次方,因此右移8bit(或取高8bit)。
Y2=Y1>>8

matlab代码实现

clc;
IMG1 = imread('./picture./19.jpg'); %读取照片
h=size(IMG1,1);
w=size(IMG1,2);%读取照片的长宽
subplot(2,2,1);imshow(IMG1);title('RGB Image')

IMG1 = double(IMG1);
IMG_YCbCr = zeros(h,w,3);
for i = 1:h
    for j = 1:w
        IMG_YCbCr(i,j,1) = bitshift((IMG1(i,j,1)*76 + IMG1(i,j,2)*150 + IMG1(i,j,3)*29),-8);
        IMG_YCbCr(i,j,2) = bitshift((-IMG1(i,j,1)*43 - IMG1(i,j,2)*84 + IMG1(i,j,3)*128 + 32768),-8);
        IMG_YCbCr(i,j,3) = bitshift((IMG1(i,j,1)*128 - IMG1(i,j,2)*107 - IMG1(i,j,3)*20 + 32768),-8);
    end
end
IMG_YCbCr = uint8(IMG_YCbCr);%0~255
subplot(2,2,2);imshow(IMG_YCbCr(:, :, 1));title('Y Channel1');
subplot(2,2,3);imshow(IMG_YCbCr(:,:,2));title('Cb Channel1');
subplot(2,2,4);imshow(IMG_YCbCr(:,:,3));title('Cr Channel1');

image.png

FPGA代码实现

`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/11/08 17:35:05
// Design Name: 
// Module Name: RGB888_YCbCr444
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////



module RGB888_YCbCr444
(
    //global clock
    input              sys_clk,                //cmos video pixel clock
    input              sys_rst_n,              //global reset

    //Image data prepred to be processed
    input               per_img_vsync,      //Prepared Image data vsync valid signal
    input               per_img_href,       //Prepared Image data href vaild signal
    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 processed
    output              post_img_vsync,     //Processed Image data vsync valid signal
    output              post_img_href,      //Processed Image data href vaild signal
    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
);

//--------------------------------------------
/*********************************************
//Refer to full/pc range YCbCr format
    Y   =  R*0.299 + G*0.587 + B*0.114
    Cb  = -R*0.169 - G*0.331 + B*0.5   + 128
    Cr  =  R*0.5   - G*0.419 - B*0.081 + 128
--->      
    Y   = (76 *R + 150*G + 29 *B)>>8
    Cb  = (-43*R - 84 *G + 128*B + 32768)>>8
    Cr  = (128*R - 107*G - 20 *B + 32768)>>8
**********************************************/
//Step 1
    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 sys_clk)
    begin
        img_red_r0   <= per_img_red   * 8'd76;
        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'd84;
        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'd20;
    end

//--------------------------------------------------
//Step 2
    reg [15:0]  img_Y_r0;   
    reg [15:0]  img_Cb_r0; 
    reg [15:0]  img_Cr_r0; 
    always@(posedge sys_clk)
    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


//--------------------------------------------------
//Step 3
    reg [7:0] img_Y_r1; 
    reg [7:0] img_Cb_r1; 
    reg [7:0] img_Cr_r1; 
    always@(posedge sys_clk)
    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

//------------------------------------------
//lag 3 clocks signal sync  
    reg [2:0] per_img_vsync_r;
    reg [2:0] per_img_href_r;   
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!sys_rst_n)
            begin
            per_img_vsync_r <= 0;
            per_img_href_r <= 0;
            end
        else
            begin
            per_img_vsync_r <=  {per_img_vsync_r[1:0],  per_img_vsync};
            per_img_href_r  <=  {per_img_href_r[1:0],   per_img_href};
            end
    end
    assign  post_img_vsync = per_img_vsync_r[2];
    assign  post_img_href  = per_img_href_r[2];
    assign  post_img_Y     = post_img_href ? img_Y_r1 : 8'd0;
    assign  post_img_Cb    = post_img_href ? img_Cb_r1: 8'd0;
    assign  post_img_Cr    = post_img_href ? img_Cr_r1: 8'd0;


endmodule

testbench

`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/11/30 19:10:17
// Design Name: 
// Module Name: testbench
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module testbench();
localparam image_width  = 640;
localparam image_height = 480;
//----------------------------------------------------------------------
//  sys_clk & sys_rst_n
reg     sys_clk;
reg     sys_rst_n;

initial
begin
    sys_clk = 1'b0;
    sys_rst_n <= 1'b0;
    #10
    sys_rst_n <= 1'b1;
end

always #10 sys_clk=~sys_clk;

//----------------------------------------------------------------------
//  Image data prepred to be processed
reg                             per_img_vsync; //场同步信号、低电平有效
reg                             per_img_href; //行同步信号 低电平有效
reg             [7:0]           per_img_red;
reg             [7:0]           per_img_green;
reg             [7:0]           per_img_blue;

//  Image data has been processed
wire                            post_img_vsync;
wire                            post_img_href;
wire            [7:0]           post_img_Y;
wire            [7:0]           post_img_Cb;
wire            [7:0]           post_img_Cr;

//----------------------------------------------------------------------
//  task and function //读取图像RGB数据,并按照一定时序产生视频激励
task image_input;
begin:img_input
    reg             [31:0]      row_cnt;//列
    reg             [31:0]      col_cnt;//行
    reg             [7:0]       mem     [image_width*image_height*3-1:0]; //memory存储器
    $readmemh("../../../../../data/img_RGB.dat",mem); //读取数据
    
    for(row_cnt = 0;row_cnt < image_height;row_cnt=row_cnt+1) //循环480次
    begin
        repeat(5) @(posedge sys_clk);
        per_img_vsync = 1'b1;  //一帧数据传输开始
        repeat(5) @(posedge sys_clk);
        for(col_cnt = 0;col_cnt < image_width;col_cnt=col_cnt+1)
        begin
            per_img_href  = 1'b1; //一行数据传输开始
            per_img_red   = mem[(row_cnt*image_width+col_cnt)*3+0];
            per_img_green = mem[(row_cnt*image_width+col_cnt)*3+1];
            per_img_blue  = mem[(row_cnt*image_width+col_cnt)*3+2];
            @(posedge sys_clk);
        end
        per_img_href  = 1'b0; //一行数据传输完成
    end
    per_img_vsync = 1'b0;//一帧数据传输完成
    @(posedge sys_clk);
end   
endtask : image_input

reg                             post_img_vsync_r;

always @(posedge sys_clk) //打一拍
begin
    if(sys_rst_n == 1'b0)
        post_img_vsync_r <= 1'b0;
    else
        post_img_vsync_r <= post_img_vsync;
end

wire                            post_img_vsync_pos;
wire                            post_img_vsync_neg;

assign post_img_vsync_pos = ~post_img_vsync_r &  post_img_vsync; //上升沿
assign post_img_vsync_neg =  post_img_vsync_r & ~post_img_vsync; //下降沿

task image_result_check; //检测数据转换的数据与原始数据不同的地方
begin:check
    reg                         frame_flag; //帧信号
    reg         [31:0]          row_cnt; //列计数
    reg         [31:0]          col_cnt; //行计数
    reg         [ 7:0]          mem     [image_width*image_height*3-1:0];
    
    frame_flag = 0;
    $readmemh("../../../../../data/img_YCbCr.dat",mem);
    
    while(1)
    begin
        @(posedge sys_clk);
        if(post_img_vsync_pos == 1'b1)  //一帧开始 上升沿
        begin
            frame_flag = 1;
            row_cnt = 0;
            col_cnt = 0;
            $display("##############image result check begin##############");
        end
        
        if(frame_flag == 1'b1)
        begin
            if(post_img_href == 1'b1) //一行传输信号开始
            begin
                if((post_img_Y != mem[(row_cnt*image_width+col_cnt)*3+0])||(post_img_Cb != mem[(row_cnt*image_width+col_cnt)*3+1])||(post_img_Cr != mem[(row_cnt*image_width+col_cnt)*3+2]))
                begin //如果Y、Cb、Cr与初始数据不同
                    $display("result error ---> row_num : %0d;col_num : %0d;pixel data(y cb cr) : (%h %h %h);reference data(y cb cr) : (%h %h %h)",row_cnt+1,col_cnt+1,post_img_Y,post_img_Cb,post_img_Cr,mem[(row_cnt*image_width+col_cnt)*3+0],mem[(row_cnt*image_width+col_cnt)*3+1],mem[(row_cnt*image_width+col_cnt)*3+2]);
                end
                col_cnt = col_cnt + 1;
            end
            
            if(col_cnt == image_width)
            begin
                col_cnt = 0;
                row_cnt = row_cnt + 1;
            end
        end
        
        if(post_img_vsync_neg == 1'b1)//下降沿
        begin
            frame_flag = 0;
            $display("##############image result check end##############");
        end
    end
end
endtask : image_result_check

//----------------------------------------------------------------------
RGB888_YCbCr444 u_RGB888_YCbCr444
(
    //  global clock
    .sys_clk            (sys_clk            ),
    .sys_rst_n          (sys_rst_n          ),
    
    //  Image data prepred to be processed
    .per_img_vsync  (per_img_vsync  ),
    .per_img_href   (per_img_href   ),
    .per_img_red    (per_img_red    ),
    .per_img_green  (per_img_green  ),
    .per_img_blue   (per_img_blue   ),
    
    //  Image data has been processed
    .post_img_vsync (post_img_vsync ),
    .post_img_href  (post_img_href  ),
    .post_img_Y     (post_img_Y     ),
    .post_img_Cb    (post_img_Cb    ),
    .post_img_Cr    (post_img_Cr    )
);

initial
begin
    per_img_vsync = 0;
    per_img_href  = 0;
    per_img_red   = 0;
    per_img_green = 0;
    per_img_blue  = 0;
end

initial 
begin
    wait(sys_rst_n);
    fork
        begin 
            repeat(5) @(posedge sys_clk); 
            image_input;
        end 
        image_result_check;
    join
end 
    
endmodule