RGB转YCbCr算法MATLAB与FPGA实现(Quartus)

一、说明

实现平台:Quartus17.1、MATLAB2022b和Modelsim SE-64 10.4

二、内容

本文内容主要实现图片进行RGB模式转化为YCbCr模式的算法实现,首先用MATLAB进行算法的验证,最后再通过FPGA实现具体算法。

三、原理

        YCbCr是一种数字信号,其在数字视频领域应用广泛,是计算机中应用最多的格式,JPEG,MPEG等都采用YCbCr格式。

        YCbCr格式具体可以分为两种格式:tv range格式和full range模式,主要区别如下:

        1)  tv range格式

        Y∈[16,235],Cb∈[16,240],Cr∈[16,240],主要是广播电视采用的数字标准。

        2)full range格式

        Y,Cb,Cr∈[0,255],主要是PC端采用的标准。

        图像传感器可以配置输出RGB/YCbCr格式图像,根据对应手册的转换公式即可。本文根据OV7725图像传感器手册的对应关系:

         使用硬件实现算法,首先要考虑去掉浮点,再不考虑精度的前提下,将数值扩大256倍,为防止溢出,舍去小数点而不进行四舍五入,公式如下:

Y1=R*76.544+G*150.272+B*29.184 \approx R*76+G*150+B*29

        由于数值扩大了256倍,即2的8次方,因此对上述结果再右移8bit,得到结果如下:

Y2=(R*76+G*150+B*29)>>8

        同上,Cb/Cr的计算公式如下:

Cb=(-R*43-G*84+B*128+32768)>>8

Cr=(R*128-G*107+B*20+32768)>>8

四、MATLAB实现

1)RGB转YCbCr

clear all;close all;clc;

%read pc image to matlab
IMG1 = imread('...\素材\0_images\Scart.jpg');%读取jpg图像
h = size(IMG1,1);           %读取图像高度
w = size(IMG1,2);        %读取图像宽度
subplot(221);
imshow(IMG1);
title('RGB Image');

% RGB转YCbCr
%Y = (R*76 + G*150 + B*29) >>8
%Cb = (-R*43 - G*84 + B*128 + 32768) >>8
%Cr = (R*128 - G*107 - B*20 + 32768) >>8
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

%display Y Cb Cr channel
IMG_YCbCr = uint8(IMG_YCbCr);
subplot(222);imshow(IMG_YCbCr(:,:,1));title('Y Channel');
subplot(223);imshow(IMG_YCbCr(:,:,2));title('Cb Channel');
subplot(224);imshow(IMG_YCbCr(:,:,3));title('Cr Channel');

        MATLAB执行显示图结果(RGB图,Y通道图,Cb通道图和Cr通道图)

 2)仿真数据准备

        为了后续FPGA设计中,配合ModelSim进行RTL仿真,实现仿真模型与Verilog代码的对比分析,需要提前生成Y、Cb、Cr通道的数据。

        首先生成测试图像的数据,用MATLAB转化为img_RGB.dat,保存于当前目录下。将MATLAB转换好的Y、Cb、Cr通道的数据写入到img_YCbCr.dat文件中,用于后续与RTL仿真生成的结果进行对比,具体实现代码封装成函数,如下:

function RGB2YCbCr_Data_Gen(img_RGB, img_YCbCr);
% 打印函数1:RGB输入,YCbCr输出
% 	RGB2YCbCr_Data_Gen(uinit8 img_RGB, uint8 img_YCbCr)
% 	img_RGB:输入待处理的RGB图像
% 	img_YCbCr:输入处理后的YCbCr图像
% 	img_RGB.dat:输出 待处理的RGB图像hex数据(比对源数据)
% 	img_YCbCr.dat:输出处理完的YCbCr图像hex数据(比对结果)

h1 = size(img_RGB,1);         % 读取图像高度
w1 = size(img_RGB,2);         % 读取图像宽度
h2 = size(img_YCbCr,1);       % 读取图像高度
w2 = size(img_YCbCr,2);       % 读取图像宽度

%Simulation source data generate
bar  = waitbar(0,'Speed of source data generating ...'); %creat process bar
fid  = fopen('.\img_RGB.dat','wt');
for row  = 1:h1
    r = lower(dec2hex(img_RGB(row,:,1),2))';
    g = lower(dec2hex(img_RGB(row,:,2),2))';
    b = lower(dec2hex(img_RGB(row,:,3),2))';
    str_data_tmp = [];
    for col = 1:w1
        str_data_tmp = [str_data_tmp,r(col*2-1:col*2),' ',g(col*2-1:col*2),' ',b(col*2-1:col*2),' '];
    end
    str_data_tmp = [str_data_tmp,10];
    fprintf(fid,'%s',str_data_tmp);
    waitbar(row/h1);
end
fclose(fid);
close(bar); %close waitbar

%Simulation target data generate
bar  = waitbar(0,'Speed of source data generating ...'); %creat process bar
fid  = fopen('.\img_YCbCr.dat','wt');
for row  = 1:h2
    Y = lower(dec2hex(img_YCbCr(row,:,1),2))';
    Cb = lower(dec2hex(img_YCbCr(row,:,2),2))';
    Cr = lower(dec2hex(img_YCbCr(row,:,3),2))';
    str_data_tmp = [];
    for col = 1:w2
        str_data_tmp = [str_data_tmp,Y(col*2-1:col*2),' ',Cb(col*2-1:col*2),' ',Cr(col*2-1:col*2),' '];
    end
    str_data_tmp = [str_data_tmp,10];
    fprintf(fid,'%s',str_data_tmp);
    waitbar(row/h2);
end
fclose(fid);
close(bar); %close waitbar

        执行该函数:

% -------------------------------------------------------------------------
% Generate image Source Data and Target Data
RGB2YCbCr_Data_Gen(IMG1, IMG_YCbCr);

         生成文件过程:

         最后分别产生了img_RGB.dat和img_YCbCr.dat文件:

         文件具体内容如下:

 五、FPGA实现

 1)FPGA实现代码:

`timescale 1ns/1ns

module RGB888_YCbCr444
(
//global clock 
input			clk,
input			rst_n,

//Image data prepared to be processed
input 			per_img_vsync,				//prepared Image data vsync valid signal 
input 			per_img_href,				//prepared Image data href valid 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

//processed image data
output 			post_img_vsync,				//processed Image data vsync valid signal 
output 			post_img_href,				//processed Image data href valid 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
);

//step 1  9 parallel multiplication using 1 clocks
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)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

//step2 Addition and subtraction operations using 1 clock
reg		[15:0]	img_Y_r0;
reg		[15:0]	img_Cb_r0;
reg		[15:0]	img_Cr_r0;

always @(posedge 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

//step3 Reduce the data to 1/256 and directly intercept the upper 8 bits using 1 clock
reg		[7:0]	img_Y_r1;
reg		[7:0]	img_Cb_r1;
reg		[7:0]	img_Cr_r1;

always @(posedge 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 clk or negedge rst_n)begin
	if(!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

 2)testbench文件:

        testbench中主要由两个任务,分别为image_input任务和image_result

_chech任务。其中,image_input任务从MATLAB仿真目录下的dat文件中读取分辨率为640*480的图像数据并按照一定的时序产生视频激励信号;image_result_check任务从MATLAB仿真目录下的dat文本中读取YCbCr图像数据,用于对ModelSim仿真结果进行对比校验,当发现MATLAB和ModelSim仿真结果不一致时,会将错误数据的图像行、列位置;ModelSim仿真结果和MATLAB仿真结果打印出来。

        具体实现代码如下:

`timescale 1 ns/ 1 ps
module RGB888_YCbCr444_vlg_tst();

localparam image_width  = 640;
localparam image_height = 480;
//----------------------------------------------------------------------
//  clk & rst_n
reg                             clk;
reg                             rst_n;

initial
begin
    clk = 1'b0;
    forever #5 clk = ~clk;
end

initial
begin
    rst_n = 1'b0;
    repeat(50) @(posedge clk);
    rst_n = 1'b1;
end

//----------------------------------------------------------------------
//  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
reg             [31:0]      row_cnt,row_cnt1;
reg             [31:0]      col_cnt,col_cnt1;
reg             [7:0]       mem     [image_width*image_height*3-1:0];
reg             [7:0]       mem1     [image_width*image_height*3-1:0];


task image_input;begin

    $readmemh(".../img_RGB.dat",mem);
    for(row_cnt = 0;row_cnt < image_height;row_cnt = row_cnt + 1)
    begin
        repeat(5) @(posedge clk);
        per_img_vsync = 1'b1;
        repeat(5) @(posedge 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 clk);
        end
        per_img_href  = 1'b0;
    end
    per_img_vsync = 1'b0;
    @(posedge clk);   
end
endtask

reg                             post_img_vsync_r;

always @(posedge clk)
begin
    if(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;

reg                         frame_flag;


task image_result_check;begin
    
    
	frame_flag = 0;
    $readmemh(".../img_YCbCr.dat",mem1);
    while(1)
    begin
        @(posedge clk);
        if(post_img_vsync_pos == 1'b1)
        begin
            frame_flag = 1;
            row_cnt1 = 0;
            col_cnt1 = 0;
            $display("##############image result check begin##############");
        end
        
        if(frame_flag == 1'b1)
        begin
            if(post_img_href == 1'b1)
            begin
                if((post_img_Y != mem1[(row_cnt1*image_width+col_cnt1)*3+0])||(post_img_Cb != mem1[(row_cnt1*image_width+col_cnt1)*3+1])||(post_img_Cr != mem1[(row_cnt1*image_width+col_cnt1)*3+2]))
                begin
                    $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_cnt1 = col_cnt1 + 1;
            end
            
            if(col_cnt1 == image_width)
            begin
                col_cnt1 = 0;
                row_cnt1 = row_cnt1 + 1;
            end
        end
        
        if(post_img_vsync_neg == 1'b1)
        begin
            frame_flag = 0;
            $display("##############image result check end##############");
        end
    end
end
endtask

//----------------------------------------------------------------------
RGB888_YCbCr444 u1
(
    //  global clock
    .clk            (clk            ),
    .rst_n          (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(rst_n);
    fork
        begin 
            repeat(5) @(posedge clk); 
            image_input;
        end 
        image_result_check;
    join
end 

endmodule

六、仿真结果

        仿真结果如下,没有发现错误,表明仿真结果成功。

         根据红框位置可进行验算,输入的R,G,B数据分别为133,152,169,根据公式可以分别计算出Y,Cb,Cr的值分别为:

Y=(133*76+152*150+169*29)/256=147.69

 Cb=(-133*43-152*84+169*128+32768)/256=139.24

Cr=(133*128-152*107-169*20+32768)/256=117.76

        与仿真结果一致,结果正确。

        若结果不正确的话会打印错误信息,如下图:

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值