FPGA数字图像处理基础:色彩空间转换(Verilog)

01图像色彩空间概述
色彩本质上是不同频率的光,人眼对于不同频率光线的不同感受产生主观感知,从而得以区分不同的颜色。尽管从客观上而言,色彩仅仅是不同频率的光,但从视觉角度而言,不同颜色的认知难度很大,因此引入色彩空间(color space)以便图像色彩的深入研究。
常见的色彩空间主要有:rgb、yuv(ycbcr)、lab、hsv和cmy(cmyk)等。
1.1 rgb空间
rgb空间是基于人眼识别的颜色所定义的色彩空间,以红(r,red)、绿(g,green)、蓝(b,blue)三种基本色为基础进行不同程度的叠加而成。红、绿、蓝作为三原色按亮度分别划分为256个等级,从而可以表示一千六百多万种不同的颜色,且每种颜色是唯一的。
rgb色彩空间的丰富性使其成为数字图像处理与显示的重要格式。通常可将rgb色彩空间模型建立在笛卡尔坐标系中,采用空间坐标的形式表示出超过人眼分辨程度数量的颜色信息。
rgb色彩空间直观且易于理解,然而r、g、b三个分量是高度相关的,由于人眼对于常见的红绿蓝三色的敏感程度不同,所以导致rgb颜色空间的均匀性非常差,且两种颜色之间的知觉差异色差不能表示为该颜色空间中两点间的距离,不过利用线性或非线性变换,可以从rgb颜色空间推导出其他的颜色特征空间。
1.2 yuv(ycbcr)空间
yuv色彩空间是彩色设备显示指定的色彩空间,y表示亮度分量,u和v表示色度分量。ycbcr色彩空间是从yuv色彩空间的基础上演变而来的,与yuv相似,但存在一定差异。
yuv最初是北美ntsc系统和欧洲pal系统中模拟电视信号编码的基础,采用亮度与色度分离的方式更易于图像处理;ycbcr则是在世界数字组织视频标准研制过程中作为itu - r bt1601建议的一部分,是yuv经过缩放和偏移的变种。在yuv系列中,ycbcr是在数字图像处理系统中应用最为广泛的格式,jpeg、mpeg、h.264均基于此格式进行处理。
ycbcr色彩空间中,y表示亮度分量,cb表示蓝色色度分量,cr表示红色色度分量。亮度和色度分量相互独立的方式,有利于利用人眼特性降低数字图像所需的存储容量,从而实现图像的压缩编码等处理。
1.3 lab空间
lab色彩空间是基于生理特征的颜色系统,与设备无关,即采用数字化的方法描述人的视觉感知。l表示亮度,取值范围为0~100,表示从黑~白;a分量取值范围为 -128~127,表示从绿~红;b分量取值范围为 -128~127,表示从蓝~黄。
lab色彩空间致力于感知均匀性,它的l分量密切匹配人类亮度感知。因此,可以被用来通过修改a和b分量的输出色阶来做精确的颜色平衡,或使用l分量来调整亮度对比。
lab色彩空间所描述的色域是最大的,因此lab色彩空间存在大量色彩超出人类视域范围,甚至无法在物理世界再现。同时,rgb等色彩空间的设备依赖性也使得lab色彩空间无法直接与其他色彩空间进行转换。
1.4 hsv空间
hsv色彩空间是根据颜色的直观特性色调(h,hue)、饱和度(s,saturation)和亮度(v,value)建立的色彩模型。由于h、s、v可以构成锥形坐标系,所以hsv模型通常也称为六角锥体模型(hexcone model)。
色调h:图像色彩信息,描述光谱颜色所在位置。采用角度度量,取值范围为0°~360°。若从红色开始按逆时针方向计算:红色为0°,绿色为120°,蓝色为240°,互补色之间相差180°;
饱和度s:图像纯度信息,描述颜色接近光谱色的程度。采用比例值度量,取值范围为0~1。颜色通常可看作某种光谱色与白色的混合结果,饱和度越高,光谱色所占比例越大,颜色越接近光谱色,表现为颜色深;饱和度越低,光谱色所占比例越小,颜色越远离光谱色,表现为颜色浅。饱和度为0时,表现出颜色仅有灰度;
亮度v:图像亮度信息,描述色彩的明亮程度。采用比例值度量,取值范围为0~1。对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。
1.5 cmy(cmyk)空间
cmyk色彩空间是一种减色模式色彩空间,常用于彩色印刷。cmyk是基于c(cyan)、m(megenta)、y(yellow)和k(black)四种颜色叠加而成的,由于在实际应用中青色(cyan)、洋红色(megenta)和黄色(yellow)叠加很难形成真正的黑色(black),而是褐色,因此引入黑色k(black)强化暗调,加深暗部色彩。
cmyk与rgb根本的区别在于cmyk采用减色色彩模式——当阳光照射到一个物体上时,物体将吸收一部分光线并反射剩下的光线,反射的光线就是所看见的物体颜色,即减色色彩模式。因此,cmyk色彩模式更接近于真实效果,尽管不如rgb模式下的色彩多,但更适合印刷。
02
色彩空间转换原理
尽管rgb是日常生活中彩色视频图像最常用的图像格式,但rgb格式图像将色调、亮度和饱和度综合在一起,细节上难以进行数字化调整,因此在科学研究中通常采用yuv(ycbcr)或灰度图格式,掌握rgb与yuv(ycbcr)/灰度图之间的转换原理有利于后续数字图像处理分析。
2.1 rgb与yuv转换原理
rgb与yuv依据以下公式进行转换:
rgb转为yuv:
y = 0.299*r + 0.587*g + 0.114*b
u = - 0.169*r - 0.331*g + 0.5*b + 128
v = 0.5*r - 0.419*g - 0.081*b  + 128
yuv转为rgb:
r = y + 1.402*(v-128)
g = y - 0.344*(u-128) - 0.714*(v-128)
b = y + 1.772*(u-128) 
通过matlab对转换算法进行验证:
matlab转换代码:
%********************************************************************** 
% -------------------------------------------------------------------
% company: cascatrix
% engineer: carson

% create date: 2023/02/11
% design name: rgb_yuv
% module name: rgb_yuv
% tool versions: v1.0
% description: convert rgb888 into yuv
%------------------------------------------------------------------- 
%*********************************************************************/
clear;clear all;clc;
% load image
image_in = imread('cascatrix.jpg');  
% seperate r g b
image_r = int16(image_in(:,:,1));          
image_g = int16(image_in(:,:,2));          
image_b = int16(image_in(:,:,3));          
% get image size 
[row,col,n] = size(image_in);
% calculate y u v
image_y  = 0.299*image_r + 0.587*image_g + 0.114*image_b;      
image_u = - 0.169*image_r - 0.331*image_g + 0.5*image_b + 128;          
image_v = 0.5*image_r - 0.419*image_g - 0.081*image_b + 128;
% recover r g b
image_out_r = image_y + 1.402*(image_v - 128);
image_out_g = image_y - 0.344*(image_u - 128) - 0.714*(image_v - 128);         
image_out_b = image_y + 1.772*(image_u - 128);
% output processed rgb image
image_out = cat(3,image_out_r,image_out_g,image_out_b);
% display before/after processed image
figure(1)
subplot(121);
imshow(uint8(image_in)), title('image in');
subplot(122);
imshow(uint8(image_out));title('image out');
% display y u v
figure(2)
subplot(131);
imshow(uint8(image_y)),title('image y');
subplot(132);
imshow(uint8(image_u)),title('image u');
subplot(133);
imshow(uint8(image_v)),title('image v');
matlab代码将输出结果:
1. 转换前/后图像对比:
2. y、u、v三通道图像显示:
2.2 rgb与ycbcr转换原理
rgb与ycbcr依据以下公式进行转换:
rgb转为ycbcr:
y = 0.257*r + 0.504*g + 0.098*b + 16
cb = - 0.148*r - 0.291*g + 0.439*b + 128
cr = 0.439*r - 0.368*g - 0.071*b + 128
ycbcr转为rgb:
r = 1.164*(y-16) + 1.596*(cr-128)
g = 1.164*(y-16) - 0.392*(cb-128) - 0.813*(cr-128)
b = 1.164*(y-16) + 2.017*(cb-128)
通过matlab对转换算法进行验证:
matlab转换代码:
%********************************************************************** 
% -------------------------------------------------------------------
% company: cascatrix
% engineer: carson

% create date: 2023/02/11
% design name: rgb_ycbcr
% module name: rgb_ycbcr
% tool versions: v1.0
% description: convert rgb888 into ycbcr
%------------------------------------------------------------------- 
%*********************************************************************/
clear;clear all;clc;
% load image
image_in = imread('cascatrix.jpg');  
% seperate r g b
image_r = int16(image_in(:,:,1));          
image_g = int16(image_in(:,:,2));          
image_b = int16(image_in(:,:,3));          
% get image size 
[row,col,n] = size(image_in);
% calculate y cb cr
image_y  = 0.257*image_r + 0.504*image_g + 0.098*image_b + 16;      
image_cb = - 0.148*image_r - 0.291*image_g + 0.439*image_b + 128;          
image_cr = 0.439*image_r - 0.368*image_g - 0.071*image_b + 128;
% recover r g b
image_out_r = 1.164*(image_y - 16) + 1.596*(image_cr - 128);
image_out_g = 1.164*(image_y - 16) - 0.392*(image_cb - 128) - 0.813*(image_cr - 128);         
image_out_b = 1.164*(image_y - 16) + 2.017*(image_cb - 128);
% output processed rgb image
image_out = cat(3,image_out_r,image_out_g,image_out_b);
% display before/after processed image
figure(1)
subplot(121);
imshow(uint8(image_in)), title('image in');
subplot(122);
imshow(uint8(image_out));title('image out');
% display y cb cr
figure(2)
subplot(131);
imshow(uint8(image_y)),title('image y');
subplot(132);
imshow(uint8(image_cb)),title('image cb');
subplot(133);
imshow(uint8(image_cr)),title('image cr');
matlab代码将输出结果:
1. 转换前/后图像对比:
2. y、cb、cr三通道图像显示:
2.3 rgb生成灰度图像原理
rgb生成灰度图像的方法较多,常用转换公式如下:
采用rgb中任意单通道表示灰度值:
gray = r (gray = g or gray = b)
采用rgb三通道的平均值表示灰度值:
gray = (r + g + b) / 3
采用rgb三通道的加权均值表示灰度值:
gray = 0.299*r + 0.587*g + 0.114*b
采用rgb三通道的最大/最小值的均值表示灰度值:
gray = ( max(r, g, b) + min(r, g, b) ) / 2
通过matlab对转换算法进行验证:
matlab转换代码(采用不同方法,转换结果略有区别):
%********************************************************************** 
% -------------------------------------------------------------------
% company: cascatrix
% engineer: carson

% create date: 2023/02/11
% design name: rgb_gray
% module name: rgb_gray
% tool versions: v1.0
% description: convert rgb888 into gray value
%------------------------------------------------------------------- 
%*********************************************************************/
clear;clear all;clc;
% load image
image_in = imread('cascatrix.jpg');  
% seperate r g b
image_r = int16(image_in(:,:,1));          
image_g = int16(image_in(:,:,2));          
image_b = int16(image_in(:,:,3));          
% get image size 
[row,col,n] = size(image_in);
% calculate gray value by different methods
gray = rgb2gray(image_in);
% gray =  image_r;
% gray =  0.299 * image_r + 0.587 * image_g + 0.114 * image_b;
% gray =  (image_r + image_g + image_b) / 3;
% display before/after processed image
figure
subplot(121);
imshow(uint8(image_in)), title('image in');
subplot(122);
imshow(uint8(gray));title('image out');
matlab代码将输出转换前/后图像:
03
色彩空间转换实现
3.1 rgb与ycbcr转换verilog代码
本节分析基于fpga实现色彩空间转换方法,在vivado和matlab联合仿真的基础上,对verilog转换实现结果在matlab中展示,验证代码的可行性。
3.1.1 预处理操作
分析rgb转为ycbcr的原理中存在小数乘法与加法运算,fpga不擅长小数处理,因此采用扩大2^n倍后向右移n位进行实现,具体实现方法如下:
rgb转ycbcr算法:
y = 0.257*r + 0.504*g + 0.098*b + 16
cb = - 0.148*r - 0.291*g + 0.439*b + 128
cr = 0.439*r - 0.368*g - 0.071*b + 128
将方程扩大256倍后右移8位,算法依然等价:
y = 256*(0.257*r + 0.504*g + 0.098*b + 16)>>8
cb = 256*(- 0.148*r - 0.291*g + 0.439*b + 128)>>8
cr = 256*(0.439*r - 0.368*g - 0.071*b + 128)>>8
算法推导得:
y = (66*r + 129*g + 25*b + 4096)>>8
cb = (- 38*r - 74*g + 112*b + 32768)>>8
cr = (112*r - 94*g - 18*b + 32768)>>8
算法转化为fpga擅长的乘法与移位运算。
3.1.2 verilog代码
基于matlab与verilog联合仿真工程,添加格式转换模块cx_rgb_ycbcr构建顶层top模块,对top进行仿真。
各模块代码如下:
1. 顶层模块 cx_top.v:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12 
// design name: image_color_space
// module name: cx_top
// tool versions: v1.0
// description: covert rgb into yuv(ycbcr) and generate gray value 
// 
//////////////////////////////////////////////////////////////////////////////////
module cx_top(
    input   wire                clk,
    input   wire                rst_n,
    output  wireen,
    outputwirehsyn,
    output  wirevsyn,
    output  wire [23:0]ycbcr_data
    );
wire [23:0] data;
cx_image inst_cx_image(
.clk(clk),
.hsyn(hsyn),
.vsyn(vsyn),
.en    (en    ),
.data(data)
);
cx_rgb_ycbcr inst_cx_rgb_ycbcr
(
.clk(clk),
.rst_n    (rst_n    ),
.rgb_data(data),
.ycbcr_data(ycbcr_data)
);
endmodule
2. 图像文件读取模块 cx_image.v:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12 
// design name: image_color_space
// module name: cx_image
// tool versions: v1.0
// description: covert rgb into yuv(ycbcr) and generate gray value 
// 
//////////////////////////////////////////////////////////////////////////////////
`define pixel_1920_1080
//`define pixel_1680_1050
//`define pixel_1280_1024
//`define pixel_1280_720
//`define pixel_1024_768
//`define pixel_800_600
//`define pixel_640_480
module cx_image(
inputwireclk,
outputreghsyn,
outputregvsyn,
outputwireen,
outputreg [23:0]data
    );
//1920x1080 148.5mhz
`ifdef  pixel_1920_1080
parameter  h_active = 1920;// 行数据有效时间
parameter  h_front_porch = 88;  // 行消隐前肩时间
parameter  h_sync_time = 44;  // 行同步信号时间
parameter  h_back_porch = 148; // 行消隐后肩时间 
parameter  v_active = 1080;// 列数据有效时间
parameter  v_front_porch = 4;   // 列消隐前肩时间
parameter  v_sync_time  = 5;   // 列同步信号时间
parameter  v_back_porch = 36;  // 列消隐后肩时间
`endif
//1680x1050 119mhz
`ifdef  pixel_1680_1050
parameter  h_active = 1680;// 行数据有效时间
parameter  h_front_porch = 48;  // 行消隐前肩时间
parameter  h_sync_time = 32;  // 行同步信号时间
parameter  h_back_porch = 80;  // 行消隐后肩时间
parameter  v_active = 1050;// 列数据有效时间
parameter  v_front_porch = 3;   // 列消隐前肩时间
parameter  v_sync_time  = 6;   // 列同步信号时间
parameter  v_back_porch = 21;  // 列消隐后肩时间
`endif
//1280x1024 108mhz
`ifdef  pixel_1280_1024
parameter  h_active = 1280;// 行数据有效时间
parameter  h_front_porch = 48;  // 行消隐前肩时间
parameter  h_sync_time = 112; // 行同步信号时间
parameter  h_back_porch = 248; // 行消隐后肩时间 
parameter  v_active = 1024;// 列数据有效时间
parameter  v_front_porch = 1;   // 列消隐前肩时间
parameter  v_sync_time  = 3;   // 列同步信号时间
parameter  v_back_porch = 38;  // 列消隐后肩时间
`endif
//1280x720 74.25mhz
`ifdef  pixel_1280_720
parameter  h_active = 1280;// 行数据有效时间
parameter  h_front_porch = 110; // 行消隐前肩时间
parameter  h_sync_time = 40;  // 行同步信号时间
parameter  h_back_porch = 220; // 行消隐后肩时间    
parameter  v_active = 720; // 列数据有效时间
parameter  v_front_porch = 5;   // 列消隐前肩时间
parameter  v_sync_time  = 5;   // 列同步信号时间
parameter  v_back_porch = 20;  // 列消隐后肩时间
`endif
//1024x768 65mhz
`ifdef  pixel_1024_768
parameter  h_active = 1024;// 行数据有效时间
parameter  h_front_porch = 24;  // 行消隐前肩时间
parameter  h_sync_time = 136; // 行同步信号时间
parameter  h_back_porch = 160; // 行消隐后肩时间   
parameter  v_active = 768; // 列数据有效时间
parameter  v_front_porch = 3;   // 列消隐前肩时间
parameter  v_sync_time  = 6;   // 列同步信号时间
parameter  v_back_porch = 29;  // 列消隐后肩时间
`endif
//800x600 40mhz
`ifdef  pixel_800_600
parameter  h_active = 800;// 行数据有效时间
parameter  h_front_porch = 40 ;// 行消隐前肩时间 
parameter  h_sync_time = 128;// 行同步信号时间
parameter  h_back_porch = 88 ;// 行消隐后肩时间    
parameter  v_active = 600;// 列数据有效时间
parameter  v_front_porch = 1  ;// 列消隐前肩时间  
parameter  v_sync_time  = 4  ;// 列同步信号时间  
parameter  v_back_porch = 23 ;// 列消隐后肩时间 
`endif
//640x480 25.175mhz
`ifdef  pixel_640_480
parameter h_active = 640; // 行数据有效时间
parameter h_front_porch = 16 ; // 行消隐前肩时间
parameter h_sync_time = 96 ; // 行同步信号时间
parameter h_back_porch = 48 ; // 行消隐后肩时间
parameter v_active = 480; // 列数据有效时间
parameter v_front_porch = 10 ; // 列消隐前肩时间
parameter v_sync_time = 2 ; // 列同步信号时间
parameter v_back_porch = 33 ; // 列消隐后肩时间
`endif
parameter  h_total_time = h_active + h_front_porch + h_sync_time + h_back_porch; 
parameter  v_total_time = v_active + v_front_porch + v_sync_time + v_back_porch;
reg h_act = 'd0;
reg v_act = 'd0;
reg [12:0] h_syn_cnt = 'd0;
reg [12:0] v_syn_cnt = 'd0;
reg [23:0]  dout = 'd0;
reg [23:0] image [0 : h_active*v_active-1];
reg [31:0] image_cnt = 'd0;
assign en = h_act & v_act;
//读取txt文件到image数组中
initial begin
$readmemh(d:/fpga_document/cx_document/cx_image /02_image_color_space/image_src/image_in.txt, image);
end
// 行扫描计数器
always@(posedge clk)
begin
if(h_syn_cnt == h_total_time-1)
        h_syn_cnt <= 13'b0;
    else
        h_syn_cnt <= h_syn_cnt + 1'b1;
end
// 列扫描计数器
always@(posedge clk)
begin
if(h_syn_cnt == h_total_time-1)
begin
        if(v_syn_cnt == v_total_time-1)
            v_syn_cnt <= 13'b0;
        else
            v_syn_cnt <= v_syn_cnt + 1'b1;
end
end
// 行同步控制
always@(posedge clk)
begin
    if(h_syn_cnt < h_sync_time)
        hsyn <= 1'b0;
    else
        hsyn <= 1'b1;
end
// 场同步控制
always@(posedge clk)
begin
    if(v_syn_cnt < v_sync_time)
        vsyn <= 1'b0;
    else
        vsyn <= 1'b1;
end
always@(posedge clk)
begin
    if(v_syn_cnt == v_sync_time + v_back_porch - 1 && h_syn_cnt == 0) 
        v_act = 1'b1;
    else if(v_syn_cnt == v_sync_time + v_back_porch + v_active - 1 && h_syn_cnt == 0)
        v_act = 1'b0;
end
always@(posedge clk)
begin
    if(h_syn_cnt == h_sync_time + h_back_porch - 1) 
        h_act = 1'b1;
    else if(h_syn_cnt == h_sync_time + h_back_porch + h_active - 1)
        h_act = 1'b0;
end
always@(posedge clk)
begin
    if(h_act & v_act)
        image_cnt <= image_cnt + 1'b1;
    else if(image_cnt == h_active*v_active - 1)
    image_cnt <= 32'b0;
end
always@(posedge clk)
begin
    if(h_act & v_act)
        data <= image[image_cnt][23:0];
    else 
        data <= 24'b0;
end
endmodule
3. rgb转ycbcr模块 cx_rgb_ycbcr:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12 
// design name: image_color_space
// module name: cx_rgb_ycbcr
// tool versions: v1.0
// description: covert rgb into yuv(ycbcr) and generate gray value 
// 
//////////////////////////////////////////////////////////////////////////////////
module cx_rgb_ycbcr(
    input   wireclk,
input   wirerst_n,
inputwire [23:0]rgb_data,
output  wire [23:0]ycbcr_data
    );
reg [7:0] r;
reg [7:0] g;
reg [7:0] b;
reg [15:0] y;
reg [15:0] cb;
reg [15:0] cr;
always@(*)
begin
    r <= rgb_data[7:0];
    g <= rgb_data[15:8];
    b <= rgb_data[23:16];
end 
always@(*) 
begin
    if(!rst_n)
begin
y  <= 16'd0;
cb <= 16'd0;
cr <= 16'd0;
    end
    else 
begin
y  <= 66  * r + 129 * g + 25  * b + 4096 ;
cb <= - 38  * r - 74  * g + 112 * b + 32768;
cr <= 112 * r - 94  * g - 18  * b + 32768;     
    end
end
assign ycbcr_data = {y[15:8], cb[15:8], cr[15:8]};
endmodule
4. 仿真模块 sim_tb.v:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12
// design name: image_color_space
// module name: top
// tool versions: v1.0
// description: image output simulation 
// 
//////////////////////////////////////////////////////////////////////////////////
module sim_tb(
    );
reg             clk; 
reg             rst_n;
reg [31:0] pixel_cnt;
wire            de; 
wire [23:0]  data; 
integer image_txt;
parameter pixel_total = 1920*1080;
//parameter pixel_total = 1680*1050;
//parameter pixel_total = 1280*1024;
//parameter pixel_total = 1280*720;
//parameter pixel_total = 1024*768;
//parameter pixel_total = 800*600;
//parameter pixel_total = 640*480;
cx_top inst_cx_top
(
    .clk                (clk                ),
    .en                 (de                 ),
    .hsyn               (                   ),
    .vsyn               (                   ),
    .ycbcr_data         (data               )
);
always #1 clk = ~clk;
initial 
begin
clk   = 1;
    rst_n = 0;
#100
    rst_n = 1;
end
initial 
begin
    image_txt = $fopen(d:/fpga_document/ cx_document/ cx_image/02_image_color_space/image_src/image_out.txt);
end
always@(posedge clk or negedge rst_n) 
begin
    if(!rst_n) 
begin
        pixel_cnt >8
算法推导得:
gray = (77*r + 150*g + 29*b)>>8
算法转化为fpga擅长的乘法与移位运算。
3.2.2 verilog代码
基于matlab与verilog联合仿真工程,添加格式转换模块cx_rgb_gray构建顶层top模块,对top进行仿真。
各模块代码如下:
1. 顶层模块 cx_top.v:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12 
// design name: image_color_space
// module name: cx_top
// tool versions: v1.0
// description: covert rgb into yuv(ycbcr) and generate gray value 
// 
//////////////////////////////////////////////////////////////////////////////////
module cx_top(
    input   wire  clk,
    input   wire  rst_n,
    output  wireen,
    output  wirehsyn,
    output  wirevsyn,
    output  wire [7:0]gray_data
    );
wire [23:0] data;
cx_image inst_cx_image(
.clk    (clk),
.hsyn  (hsyn),
.vsyn  (vsyn),
.en      (en    ),
.data   (data)
);
cx_rgb_gray inst_cx_rgb_gray
(
.clk            (clk),
.rst_n         (rst_n    ),
.rgb_data    (data),
.gray_data  (gray_data)
);
endmodule
2. 图像文件读取模块 cx_image.v:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12 
// design name: image_color_space
// module name: cx_image
// tool versions: v1.0
// description: covert rgb into yuv(ycbcr) and generate gray value 
// 
//////////////////////////////////////////////////////////////////////////////////
`define pixel_1920_1080
//`define pixel_1680_1050
//`define pixel_1280_1024
//`define pixel_1280_720
//`define pixel_1024_768
//`define pixel_800_600
//`define pixel_640_480
module cx_image(
inputwireclk,
outputreghsyn,
outputregvsyn,
outputwireen,
outputreg [23:0]data
    );
//1920x1080 148.5mhz
`ifdef  pixel_1920_1080
parameter  h_active = 1920;// 行数据有效时间
parameter  h_front_porch = 88;  // 行消隐前肩时间
parameter  h_sync_time = 44;  // 行同步信号时间
parameter  h_back_porch = 148; // 行消隐后肩时间 
parameter  v_active = 1080;// 列数据有效时间
parameter  v_front_porch = 4;   // 列消隐前肩时间
parameter  v_sync_time  = 5;   // 列同步信号时间
parameter  v_back_porch = 36;  // 列消隐后肩时间
`endif
//1680x1050 119mhz
`ifdef  pixel_1680_1050
parameter  h_active = 1680;// 行数据有效时间
parameter  h_front_porch = 48;  // 行消隐前肩时间
parameter  h_sync_time = 32;  // 行同步信号时间
parameter  h_back_porch = 80;  // 行消隐后肩时间
parameter  v_active = 1050;// 列数据有效时间
parameter  v_front_porch = 3;   // 列消隐前肩时间
parameter  v_sync_time  = 6;   // 列同步信号时间
parameter  v_back_porch = 21;  // 列消隐后肩时间
`endif
//1280x1024 108mhz
`ifdef  pixel_1280_1024
parameter  h_active = 1280;// 行数据有效时间
parameter  h_front_porch = 48;  // 行消隐前肩时间
parameter  h_sync_time = 112; // 行同步信号时间
parameter  h_back_porch = 248; // 行消隐后肩时间 
parameter  v_active = 1024;// 列数据有效时间
parameter  v_front_porch = 1;   // 列消隐前肩时间
parameter  v_sync_time  = 3;   // 列同步信号时间
parameter  v_back_porch = 38;  // 列消隐后肩时间
`endif
//1280x720 74.25mhz
`ifdef  pixel_1280_720
parameter  h_active = 1280;// 行数据有效时间
parameter  h_front_porch = 110; // 行消隐前肩时间
parameter  h_sync_time = 40;  // 行同步信号时间
parameter  h_back_porch = 220; // 行消隐后肩时间    
parameter  v_active = 720; // 列数据有效时间
parameter  v_front_porch = 5;   // 列消隐前肩时间
parameter  v_sync_time  = 5;   // 列同步信号时间
parameter  v_back_porch = 20;  // 列消隐后肩时间
`endif
//1024x768 65mhz
`ifdef  pixel_1024_768
parameter  h_active = 1024;// 行数据有效时间
parameter  h_front_porch = 24;  // 行消隐前肩时间
parameter  h_sync_time = 136; // 行同步信号时间
parameter  h_back_porch = 160; // 行消隐后肩时间   
parameter  v_active = 768; // 列数据有效时间
parameter  v_front_porch = 3;   // 列消隐前肩时间
parameter  v_sync_time  = 6;   // 列同步信号时间
parameter  v_back_porch = 29;  // 列消隐后肩时间
`endif
//800x600 40mhz
`ifdef  pixel_800_600
parameter  h_active = 800;// 行数据有效时间
parameter  h_front_porch = 40 ;// 行消隐前肩时间 
parameter  h_sync_time = 128;// 行同步信号时间
parameter  h_back_porch = 88 ;// 行消隐后肩时间    
parameter  v_active = 600;// 列数据有效时间
parameter  v_front_porch = 1  ;// 列消隐前肩时间  
parameter  v_sync_time  = 4  ;// 列同步信号时间  
parameter  v_back_porch = 23 ;// 列消隐后肩时间 
`endif
//640x480 25.175mhz
`ifdef  pixel_640_480
parameter h_active = 640; // 行数据有效时间
parameter h_front_porch = 16 ; // 行消隐前肩时间
parameter h_sync_time = 96 ; // 行同步信号时间
parameter h_back_porch = 48 ; // 行消隐后肩时间
parameter v_active = 480; // 列数据有效时间
parameter v_front_porch = 10 ; // 列消隐前肩时间
parameter v_sync_time = 2 ; // 列同步信号时间
parameter v_back_porch = 33 ; // 列消隐后肩时间
`endif
parameter  h_total_time = h_active + h_front_porch + h_sync_time + h_back_porch; 
parameter  v_total_time = v_active + v_front_porch + v_sync_time + v_back_porch;
reg h_act = 'd0;
reg v_act = 'd0;
reg [12:0] h_syn_cnt = 'd0;
reg [12:0] v_syn_cnt = 'd0;
reg [23:0]  dout = 'd0;
reg [23:0] image [0 : h_active*v_active-1];
reg [31:0] image_cnt = 'd0;
assign en = h_act & v_act;
//读取txt文件到image数组中
initial begin
$readmemh(d:/fpga_document/cx_document/cx_image /02_image_color_space/image_src/image_in.txt, image);
end
// 行扫描计数器
always@(posedge clk)
begin
if(h_syn_cnt == h_total_time-1)
        h_syn_cnt <= 13'b0;
    else
        h_syn_cnt <= h_syn_cnt + 1'b1;
end
// 列扫描计数器
always@(posedge clk)
begin
if(h_syn_cnt == h_total_time-1)
begin
        if(v_syn_cnt == v_total_time-1)
            v_syn_cnt <= 13'b0;
        else
            v_syn_cnt <= v_syn_cnt + 1'b1;
end
end
// 行同步控制
always@(posedge clk)
begin
    if(h_syn_cnt < h_sync_time)
        hsyn <= 1'b0;
    else
        hsyn <= 1'b1;
end
// 场同步控制
always@(posedge clk)
begin
    if(v_syn_cnt < v_sync_time)
        vsyn <= 1'b0;
    else
        vsyn <= 1'b1;
end
always@(posedge clk)
begin
    if(v_syn_cnt == v_sync_time + v_back_porch - 1 && h_syn_cnt == 0) 
        v_act = 1'b1;
    else if(v_syn_cnt == v_sync_time + v_back_porch + v_active - 1 && h_syn_cnt == 0)
        v_act = 1'b0;
end
always@(posedge clk)
begin
    if(h_syn_cnt == h_sync_time + h_back_porch - 1) 
        h_act = 1'b1;
    else if(h_syn_cnt == h_sync_time + h_back_porch + h_active - 1)
        h_act = 1'b0;
end
always@(posedge clk)
begin
    if(h_act & v_act)
        image_cnt <= image_cnt + 1'b1;
    else if(image_cnt == h_active*v_active - 1)
    image_cnt <= 32'b0;
end
always@(posedge clk)
begin
    if(h_act & v_act)
        data <= image[image_cnt][23:0];
    else 
        data <= 24'b0;
end
endmodule
3. rgb转gray模块 cx_rgb_gray:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12 
// design name: image_color_space
// module name: cx_rgb_gray
// tool versions: v1.0
// description: covert rgb into yuv(ycbcr) and generate gray value 
// 
//////////////////////////////////////////////////////////////////////////////////
module cx_rgb_gray(
    input   wireclk,
    input   wirerst_n,
    input  wire [23:0]rgb_data,
    output  wire [7:0]gray_data
    );
reg [7:0] r;
reg [7:0] g;
reg [7:0] b;
reg [15:0] gray_value;
always@(*)
begin
    r <= rgb_data[7:0];
    g <= rgb_data[15:8];
    b <= rgb_data[23:16];
end 
always@(*) 
begin
    if(!rst_n)
    begin
        gray_value  <= 16'd0;
    end
    else 
    begin
        gray_value <= 77 * r + 150 * g + 29 * b;
    end
end
assign gray_data = gray_value[15:8];
endmodule
4. 仿真模块 sim_tb.v:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// company: cascatrix
// engineer: carson
// 
// create date: 2023/02/12
// design name: image_color_space
// module name: top
// tool versions: v1.0
// description: image output simulation 
// 
//////////////////////////////////////////////////////////////////////////////////
module sim_tb(
    );
reg             clk; 
reg             rst_n;
reg [31:0] pixel_cnt;
wire          de; 
wire [7:0]  data; 
integer image_txt;
parameter pixel_total = 1920*1080;
//parameter pixel_total = 1680*1050;
//parameter pixel_total = 1280*1024;
//parameter pixel_total = 1280*720;
//parameter pixel_total = 1024*768;
//parameter pixel_total = 800*600;
//parameter pixel_total = 640*480;
cx_top inst_cx_top
(
    .clk                 (clk                ),
    .en                  (de                 ),
    .hsyn               (                   ),
    .vsyn               (                   ),
    .gray_data       (data               )
);
always #1 clk = ~clk;
initial 
begin
clk   = 1;
    rst_n = 0;
#100
    rst_n = 1;
end
initial 
begin
    image_txt = $fopen(d:/fpga_document/ cx_document/ cx_image/02_image_color_space/image_src/image_out.txt);
end
always@(posedge clk or negedge rst_n) 
begin
    if(!rst_n) 
begin
        pixel_cnt <= 0;
    end
    else if(de) 
begin
        pixel_cnt = pixel_cnt + 1;
        $fwrite(image_txt,%h ,data);
    end
end
always@(posedge clk) 
begin
if(pixel_cnt == pixel_total)
begin
$display(cx: image_out.txt is output completed successfully! %t, $realtime, ps);
$fclose(image_txt);
$stop;
end
end
endmodule
3.2.3 实现效果分析
通过matlab显示verilog处理后的image_out.txt文件:
%********************************************************************** 
% -------------------------------------------------------------------
% company: cascatrix
% engineer: carson
%  
% create date: 2023/02/13
% design name: gray_display
% module name: gray_display
% tool versions: v1.0
% description: display gray image
%------------------------------------------------------------------- 
%*********************************************************************/
clear;clear all;clc;
% image resolution
row = 1080;  
col = 1920;  
% create output image
image_out = uint8(zeros(row,col));
% write data into output image 
fileimage = fopen('image_out.txt','r');
for x = 1:row
    for y = 1:col
        gray = fscanf(fileimage,'%s',1);
        image_out(x,y) = uint8(hex2dec(gray(1:2)));             
    end 
end
fclose(fileimage);
% show the output image
imshow(image_out),title('image output');
% create image in .jpg format
imwrite(image_out,'cascatrix_output.jpg');   
matlab图像显示结果:


第二代多处理器SoC,实现最佳低成本电源解决方案
智能音箱背后的技术原理是什么
基于VCSEL的神经形态光子脉冲处理器
机器人在钢结构的广泛应用场景
明星企业齐聚PEC电力电子,共讨行业发展新趋势
FPGA数字图像处理基础:色彩空间转换(Verilog)
岚豹扫地机器人完全无人工 彻底解放妈妈的双手
仿真Xilinx网表
创新驱动 专业创领------pickering专访
我国半导体产业与美国的差距有多大
高压放大器在硅氧烷近晶相单体合成中的应用
微软存储技术成为企业最受欢迎云存储
八措并举提升配电网发展质量
三极管的基本结构和优缺点 三极管的作用和工作原理图讲解
保护物联网产品免受独特威胁
可编程逻辑:路在何方?
智能动感单车测评:莫比凭什么领跑市场
芯盾时代国产密码创新应用方案
【案例】智能渔业养殖方案
PCBA加工为什么会有锡珠的产生?