问答 店铺
热搜: ZYNQ FPGA discuz

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

微信扫一扫 分享朋友圈

已有 58 人浏览分享

开启左侧

FPGA图像处理-色彩空间转换RGB_Ycbcr-连载一

[复制链接]
58 0
本帖最后由 UT发布 于 2025-3-31 10:57 编辑

软件版本:Anlogic -TD5.6.1-64bit
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
登录“米联客”FPGA社区-www.uisrc.com视频课程、答疑解惑!
1概述
   本文讲解色彩空间转换的算法,介绍RGBYUVYcbCr之间的转换公式,并使用verilog语言实现算法,进行上板实验。
2色彩空间转换算法简介
          RGBYUVYCbCr都是人为规定的色彩空间。其用途是在某些标准下以通用化的方式对彩色进行描述。本质上,彩色模型是坐标系统和子空间的描述。
          RGB是依据人眼识别的颜色定义出的空间,可表示大部分颜色。但在科学研究一般不采用RGB颜色空间,因为它的细节难以进行数字化的调整。它将色调,亮度,饱和度三个量放在一起表示,很难分开。它是最通用的面向硬件的彩色模型。该模型用于彩色监视器和一些彩色视频摄像。
   在 YUV色彩空间中,每一个颜色有一个亮度信号Y,和两个色度信号UV。亮度信号是强度的感觉,它和色度信号断开,这样的话强度就可以在不影响颜色的情况下改变。
          YUV使用RGB的信息,但它从全彩色图像中产生一个黑白图像(Y信号),然后提取出三个主要的颜色变成两个额外的信号来描述颜色。把这三个信号组合回来就可以产生一个全彩色图像。
          Y 通道描述明亮度信号,值的范围介于亮和暗之间。明亮度是黑白电视可以看到的信号。U (Cb) V (Cr)通道从红 (U) 和蓝 (V) 中提取亮度值来减少颜色信息量。这些值可以从新组合来决定红,绿和蓝的混合信号。
3 YUVRGB的转换
        Y = 0.299 R + 0.587 G + 0.114 B
        U = -0.1687 R - 0.3313 G + 0.5 B + 128
        V = 0.5 R - 0.4187 G - 0.0813 B + 128

        R = Y + 1.402 (V-128)
        G = Y - 0.34414 (U-128) - 0.71414 (V-128)
        B = Y + 1.772 (U-128)
4 YCbCrRGB的转换
         YCbCr 是在世界数字组织视频标准研制过程中作为ITU - R BT1601 建议的一部分,是YUV经过缩放和偏移的翻版。其中YYUV 中的Y含义一致, Cb , Cr 同样都指色彩, 只是在表示方法上不同而已。在YUV家族中, YCbCr 是在计算机系统中应用最多的成员,其应用领域很广泛,JPEGMPEG均采用此格式。一般人们所讲的YUV大多是指YCbCr
YCbCrRGB的相互转换:
Y  = 0.2568*R + 0.5041*G + 0.0979*B + 16;
Cb = -0.1482*R - 0.2910*G + 0.4392*B + 128;
Cr = 0.4392*R - 0.3678*G - 0.0714*B + 128;
R = 1.1644*(Y- 16) + 1.5960*(Cr - 128);
G = 1.1644*(Y - 16) - 0.3918*(Cb- 128) -0.8130*(Cr- 128);
B = 1.1644*(Y - 16) + 2.0172*(Cb- 128);
5算法仿真
5.1 Matlab算法仿真
5.1.1Matlab算法代码分析
RGBYCbCr
源代码如下:
  1. %=================================================================================
  2. %rgb2ycbcr
  3. %=================================================================================
  4. T = [ ...
  5.      0.2568 0.5041         0.0979;...
  6.     -0.1482 -0.2910 0.4392;...
  7.      0.4392 -0.3678 -0.0714];
  8. Offset = [16; 128; 128];
  9. ycbcr1 = zeros(size(image_in), 'like', image_in);
  10. <font color="#00bfff">for p = 1:3
  11.    ycbcr1(:,:,p) = imlincomb( T(p,1),image_in_r,...
  12.                               T(p,2),image_in_g,...
  13.                               T(p,3),image_in_b,...
  14.                               Offset(p));                                          
  15. end</font>
  16. image1_in_y  = ycbcr1(:,:,1);
  17. image1_in_cb = ycbcr1(:,:,2);
  18. image1_in_cr = ycbcr1(:,:,3);
  19. %=================================================================================
  20. %=================================================================================
  21. %rgb2ycbcr 2
  22. %=================================================================================
  23. <font color="#00bfff">ycbcr2 = rgb2ycbcr(image_in);</font>
  24. image2_in_y = ycbcr2(:,:,1);
  25. image2_in_cb = ycbcr2(:,:,2);
  26. image2_in_cr = ycbcr2(:,:,3);
  27. %=================================================================================
复制代码

   蓝色部分为关键计算部分,本次采用了两种方式进行了RGB转YCbCr的计算,一种直接带入系数进行计算,另一种直接使用matlab的库函数rgb2ycbcr进行转换的。可以看到矩阵T为1.2小节中的公式中的系数部分,Offset为常数部分,通过matlab imlincomb的函数进行计算图像线性组合,最后得到YCbCr的图像。

5.1.2Matlab实验结果
   第一行为RGBYCbCr,从左至右分别是原图、Y图、Cb图、Cr图、YCbCrRGB图。
image.jpg
   第二行为matlab库函数RGBYCbCr,从左至右分别是原图、Y图、Cb图、Cr图、YCbCrRGB图。
5.2 Verilog算法仿真
5.2.1 Modelsim仿真
5.2.1.1仿真执行
   在件夹Algorithm_simulation下进行算法的仿真,分为simsrctb三个子文件夹。在sim文件夹下win系统的快捷执行文件sim.bat,可以一键进行仿真,src文件下放的是Verilog的核心图像算法及其顶层与输入图像激励,tb文件下放的是测试激励文件及输出图像的保存。
   双击执行sim文件夹下sim.bat,自动打开Modelsim仿真,自动添加仿真波形,执行完成后自动保存图像,仿真波形如图所示:
image.jpg
5.2.1.2仿真关键部分代码解析
Sim.do执行仿真代码,文件内容如下:
  1. #
  2. # Create work library
  3. #
  4. vlib work
  5. #
  6. # Compile sources
  7. #
  8. vlog "../src/*.v"
  9. vlog "../tb/*.v"
  10. #
  11. # Call vsim to invoke simulator
  12. #
  13. vsim -voptargs=+acc work.top_tb
  14. #
  15. # Add waves
  16. #
  17. do wave.do
  18. #
  19. # Run simulation
  20. #
  21. run -all
  22. #
  23. # End
复制代码
   图像输入代码部分:
  1. reg                 en;
  2. reg [12:0]         h_syn_cnt = 'd0;
  3. reg [12:0]         v_syn_cnt = 'd0;
  4. reg [23:0]         image [0 : H_ACTIVE*V_ACTIVE-1];
  5. reg [31:0]         image_cnt = 'd0;
  6. //读取txt文件到image数组中
  7. initial begin
  8.         $readmemh("../../matlab_src/image_720_1280_3.txt", image);
  9. end
  10. // 行扫描计数器
  11. always@(posedge i_clk)
  12. begin
  13.         if(h_syn_cnt == H_TOTAL_TIME-1)
  14.         h_syn_cnt <= 0;
  15.     else
  16.         h_syn_cnt <= h_syn_cnt + 1;
  17. end
  18. // 列扫描计数器
  19. always@(posedge i_clk)
  20. begin
  21.         if(h_syn_cnt == H_TOTAL_TIME-1)
  22.         begin
  23.         if(v_syn_cnt == V_TOTAL_TIME-1)
  24.             v_syn_cnt <= 0;
  25.         else
  26.             v_syn_cnt <= v_syn_cnt + 1;
  27.         end
  28. end
  29. // 行同步控制
  30. always@(posedge i_clk)
  31. begin
  32.     if(h_syn_cnt < H_SYNC_TIME)
  33.         o_hsyn <= 0;
  34.     else
  35.         o_hsyn <= 1;
  36. end
  37. // 场同步控制
  38. always@(posedge i_clk)
  39. begin
  40.     if(v_syn_cnt < V_SYNC_TIME)
  41.         o_vsyn <= 0;
  42.     else
  43.         o_vsyn <= 1;
  44. end
  45. // 坐标使能.
  46. always@(posedge i_clk)
  47. begin
  48.     if(v_syn_cnt >= V_SYNC_TIME + V_BACK_PORCH && v_syn_cnt < V_SYNC_TIME + V_BACK_PORCH + V_ACTIVE)
  49.     begin
  50.         if(h_syn_cnt >= H_SYNC_TIME + H_BACK_PORCH && h_syn_cnt < H_SYNC_TIME + H_BACK_PORCH + H_ACTIVE)
  51.             en <= 1;
  52.         else
  53.             en <= 0;
  54.     end
  55.     else
  56.         en <= 0;
  57. end
  58. always@(posedge i_clk)
  59. begin
  60.     if(en)
  61.         begin
  62.                 o_r                     <= image[image_cnt][23:16];
  63.                 o_g                 <= image[image_cnt][15:8];
  64.                 o_b                 <= image[image_cnt][7:0];
  65.             image_cnt         <= image_cnt + 1;
  66.         end
  67.         else if(image_cnt == H_ACTIVE*V_ACTIVE)
  68.         begin
  69.                 o_r                     <= 8'h00;
  70.                 o_g                 <= 8'h00;
  71.                 o_b                 <= 8'h00;
  72.             image_cnt         <= 'd0;
  73.         end        
  74.     else
  75.         begin
  76.                 o_r                     <= 8'h00;
  77.                 o_g                 <= 8'h00;
  78.                 o_b                 <= 8'h00;
  79.             image_cnt         <= image_cnt;
  80.         end        
  81. end
  82. always@(posedge i_clk)
  83. begin
  84.         o_en <= en;
  85. end
复制代码
图形输出保存代码部分:
  1. reg             clk;
  2. reg             rst_n;
  3. integer                 image_txt;
  4. reg [31:0]                 pixel_cnt;
  5. wire[23:0]          data;
  6. wire            de;
  7. wire [7:0]                y;
  8. wire [7:0]                cb;
  9. wire [7:0]                cr;
  10. top u_top
  11. (
  12.     .i_clk                 (clk                ),
  13.     .i_rst_n         (rst_n              ),
  14.         .o_hsyn     (                   ),
  15.         .o_vsyn     (                   ),
  16.         .o_y        (y                  ),
  17.         .o_cb       (cb                 ),
  18.         .o_cr       (cr                 ),
  19.         .o_de       (de                 )
  20. );
  21. assign data = {y,cb,cr};
  22. always #(1) clk = ~clk;
  23. initial
  24. begin
  25.         clk   = 1;
  26.     rst_n = 0;         
  27.         #100
  28.     rst_n = 1;
  29.         
  30. end
  31. initial
  32. begin
  33.     image_txt = $fopen("../../matlab_src/image_720_1280_rgb2ycbcr_out.txt");
  34. end
  35. always@(posedge clk or negedge rst_n)
  36. begin
  37.     if(!rst_n)
  38.         begin
  39.         pixel_cnt <= 0;
  40.     end
  41.     else if(de)
  42.         begin
  43.         pixel_cnt = pixel_cnt + 1;
  44.         $fwrite(image_txt,"%h\n",data);
  45.     end
  46. end
  47. always@(posedge clk )
  48. begin
  49.         if(pixel_cnt == 720*1280)
  50.         begin
  51.                 $display("*******************************************************************************");               
  52.                 $display("*** Success:image_720_1280_rgb2ycbcr_out.txt is output complete! %t", $realtime, "ps***");
  53.                 $display("*******************************************************************************");
  54.                         $fclose(image_txt);
  55.                 $stop;
  56.         end        
  57. end
复制代码

5.2.2
Modelsim实验结果
        matlab查看输入输出的图像代码部分:
  1. clear;clear all;clc;
  2. row = 720;  
  3. col = 1280;  
  4. n   = 3;
  5. image_sim_pass = uint8(zeros(row,col,n));
  6. fid = fopen('image_720_1280_rgb2ycbcr_out.txt','r');
  7. for x = 1:row
  8.     for y = 1:col
  9.         RGB = fscanf(fid,'%s',1);
  10.         image_sim_pass(x,y,1) = uint8(hex2dec(RGB(1:2)));
  11.         image_sim_pass(x,y,2) = uint8(hex2dec(RGB(3:4)));
  12.         image_sim_pass(x,y,3) = uint8(hex2dec(RGB(5:6)));              
  13.     end
  14. end
  15. fclose(fid);
  16. rgb = zeros(size(image_sim_pass), 'like', image_sim_pass);
  17. 将图像格式转换成RGB格式。
  18. rgb = ycbcr2rgb(image_sim_pass);
  19. image_1 = imread('lena_1280x720.jpg');
  20. figure;
  21. subplot(121);
  22. imshow(image_1), title('The original image');
  23. subplot(122);
  24. imshow(rgb),title('After processing images');
  25. imwrite(rgb,'lena_1280x720_rgb2ycbcr_sim_pass.jpg');  
复制代码
   使用matlab查看输入输出的图像,如图所示:
          RGBYCbCr
image.jpg
6工程实现
6.1代码分析
按照1.2小节中的公式中进行公式变换:
// 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;
先乘以256然后右移8位。
// 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;
将乘法运算单独在一个时钟周期中完成,代码如下:
  1. always@(posedge i_clk or negedge i_rst_n)
  2. begin
  3.     if(!i_rst_n)
  4.         begin
  5.         r_d0 <= 16'd0;
  6.         g_d0 <= 16'd0;
  7.         b_d0 <= 16'd0;
  8.                 r_d1 <= 16'd0;
  9.                 g_d1 <= 16'd0;
  10.                 b_d1 <= 16'd0;
  11.                 r_d2 <= 16'd0;
  12.                 g_d2 <= 16'd0;
  13.                 b_d2 <= 16'd0;        
  14.     end
  15.     else
  16.         begin
  17.         r_d0 <= 66  * i_r;
  18.         g_d0 <= 129 * i_g;
  19.         b_d0 <= 25  * i_b;
  20.                 r_d1 <= 38  * i_r;
  21.                 g_d1 <= 74  * i_g;
  22.                 b_d1 <= 112 * i_b;
  23.                 r_d2 <= 112 * i_r;
  24.                 g_d2 <= 94  * i_g;
  25.                 b_d2 <= 18  * i_b;        
  26.     end
  27. end
复制代码
      完成乘法后进行加减法运算:
  1. always@(posedge i_clk or negedge i_rst_n)
  2. begin
  3.     if(!i_rst_n)
  4.         begin
  5.                 y_d0  <= 16'd0;
  6.                 cb_d0 <= 16'd0;
  7.                 cr_d0 <= 16'd0;
  8.     end
  9.     else
  10.         begin
  11.                 y_d0  <= r_d0 + g_d0 + b_d0 + 4096 ;
  12.                 cb_d0 <= b_d1 - r_d1 - g_d1 + 32768;
  13.                 cr_d0 <= r_d2 - g_d2 - b_d2 + 32768;
  14.     end
  15. end
  16. 最后进行信号的同步:
  17. always@(posedge i_clk )
  18. begin
  19.         hsyn        <= {hsyn[0],i_hsyn};
  20.         vsyn        <= {vsyn[0],i_vsyn};
  21.         de                <= {de[0],i_de};
  22. end
  23. assign         o_hsyn        = hsyn[1];
  24. assign         o_vsyn        = vsyn[1];
  25. assign         o_y                = y_d0 [15:8];
  26. assign         o_cb        = cb_d0[15:8];
  27. assign         o_cr        = cr_d0[15:8];
  28. assign         o_de        = de[1];
复制代码
      最后进行信号的同步:
  1. always@(posedge i_clk )
  2. begin
  3.         hsyn        <= {hsyn[0],i_hsyn};
  4.         vsyn        <= {vsyn[0],i_vsyn};
  5.         de                <= {de[0],i_de};
  6. end
  7. assign         o_hsyn        = hsyn[1];
  8. assign         o_vsyn        = vsyn[1];
  9. assign         o_y                = y_d0 [15:8];
  10. assign         o_cb        = cb_d0[15:8];
  11. assign         o_cr        = cr_d0[15:8];
  12. assign         o_de        = de[1];
复制代码
6.2工程结构
    工程结构如图所示:
image.jpg
    图像数据通过摄像头采集进来,先缓存在fifo中,然后通过写状态机,将图像数据送进DDR进行缓存,缓存后的图像数据从DDR中取出,通过读状态机送出到fifo中,然后算法处理模块在fifo中取出数据,完成数据处理后送到LCD进行显示输出。
7上板实验
        插好摄像头,连接好板子与液晶屏之间的连接,如图所示:
image.jpg
   然后给开发板上电,连接JTAG下载器到电脑,然后打开下载界面,如图所示:
image.jpg
  点击下载后,可以看到正常的输出如下所示,摄像头的分辨率为640x480
image.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

269

主题
精彩推荐
热门资讯
网友晒图
图文推荐

  • 微信公众平台

  • 扫描访问手机版