小梅哥书上的教程讲得也很好
《小梅哥-FPGA系统设计与验证实战指南_V24》
P780 7.7 RGB转灰度算法的实现
在图像处理中,用RGB三个分量(R:Red,G:Green,B:Blue),即红、绿、蓝三原色来表示真彩色,R分量,G分量,B分量的取值范围均为0~255,比如电脑屏幕上的一个红色的像素点的三个分量的值分别为:255,0,0。
图像的灰度化就是让像素点矩阵中的每一个像素点都满足下面的关系:R=G=B(就是红色变量的值,绿色变量的值,和蓝色变量的值,这三个值相等,“=”的意思不是程序语言中的赋值,是数学中的相等),此时的这个值叫做灰度值。
灰度处理的方法
一般灰度处理经常使用两种方法来进行处理
灰度化后的R=(处理前的R + 处理前的G +处理前的B)/ 3
灰度化后的G=(处理前的R + 处理前的G +处理前的B)/ 3
灰度化后的B=(处理前的R + 处理前的G +处理前的B)/ 3
灰度化后的R = 处理前的R * 0.3+ 处理前的G * 0.59 +处理前的B * 0.11
灰度化后的G = 处理前的R * 0.3+ 处理前的G * 0.59 +处理前的B * 0.11
灰度化后的B = 处理前的R * 0.3+ 处理前的G * 0.59 +处理前的B * 0.11
计算公式
Y = 0.183R + 0.614G + 0.062B + 16;
CB = -0.101R - 0.338G + 0.439B + 128;
CR = 0.439R - 0.399G - 0.040B + 128;
RGB 图像转换成 Ycbcr 图像,使用 Y 分量去显示图像,来实现彩色图像转灰度图,是更合理和常用的灰度化方法。
其中,时序在计算过程中完全没有用到
输入到输出有三个 clock 的时延。
第一级流水线计算所有乘法;
第二级流水线计算所有加法,把正的和负的分开进行加法;
第三级流水线计算最终的和,若为负数取 0。
/*
RGB 转 Ycbcr 算法
计算公式:
Y = 0.183R + 0.614G + 0.062B + 16;
CB = -0.101R - 0.338G + 0.439B + 128;
CR = 0.439R - 0.399G - 0.040B + 128;
其中,时序在计算过程中完全没有用到
输入到输出有三个 clock 的时延。
第一级流水线计算所有乘法;
第二级流水线计算所有加法,把正的和负的分开进行加法;
第三级流水线计算最终的和,若为负数取 0;
*/
`timescale 1ns/1ps
module rgb_to_ycbcr(
input clk,
input [7 : 0] i_r_8b,
input [7 : 0] i_g_8b,
input [7 : 0] i_b_8b,
input i_h_sync,
input i_v_sync,
input i_data_en,
output [7 : 0] o_y_8b,
output [7 : 0] o_cb_8b,
output [7 : 0] o_cr_8b,
output o_h_sync,
output o_v_sync,
output o_data_en
);
/****************parameters**********************/
//multiply 256
parameter para_0183_10b = 10'd47; //0.183 定点数,将小数整数化方便计算
parameter para_0614_10b = 10'd157;
parameter para_0062_10b = 10'd16;
parameter para_0101_10b = 10'd26;
parameter para_0338_10b = 10'd86;
parameter para_0439_10b = 10'd112;
parameter para_0399_10b = 10'd102;
parameter para_0040_10b = 10'd10;
parameter para_16_18b = 18'd4096;
parameter para_128_18b = 18'd32768;
/******************************************************/
/**************signals*********************************/
wire sign_cb;
wire sign_cr;
reg[17: 0] mult_r_for_y_18b;
reg[17: 0] mult_r_for_cb_18b;
reg[17: 0] mult_r_for_cr_18b;
reg[17: 0] mult_g_for_y_18b;
reg[17: 0] mult_g_for_cb_18b;
reg[17: 0] mult_g_for_cr_18b;
reg[17: 0] mult_b_for_y_18b;
reg[17: 0] mult_b_for_cb_18b;
reg[17: 0] mult_b_for_cr_18b;
reg[17: 0] add_y_0_18b;
reg[17: 0] add_cb_0_18b;
reg[17: 0] add_cr_0_18b;
reg[17: 0] add_y_1_18b;
reg[17: 0] add_cb_1_18b;
reg[17: 0] add_cr_1_18b;
reg[17: 0] result_y_18b;
reg[17: 0] result_cb_18b;
reg[17: 0] result_cr_18b;
reg[9:0] y_tmp;
reg[9:0] cb_tmp;
reg[9:0] cr_tmp;
reg i_h_sync_delay_1;
reg i_v_sync_delay_1;
reg i_data_en_delay_1;
reg i_h_sync_delay_2;
reg i_v_sync_delay_2;
reg i_data_en_delay_2;
reg i_h_sync_delay_3;
reg i_v_sync_delay_3;
reg i_data_en_delay_3;
/**************************************************/
/******************initial*************************/
initial
begin
mult_r_for_y_18b <= 18'd0;
mult_r_for_cb_18b <= 18'd0;
mult_r_for_cr_18b <= 18'd0;
mult_g_for_y_18b <= 18'd0;
mult_g_for_cb_18b <= 18'd0;
mult_g_for_cr_18b <= 18'd0;
mult_b_for_y_18b <= 18'd0;
mult_g_for_cb_18b <= 18'd0;
mult_b_for_cr_18b <= 18'd0;
add_y_0_18b <= 18'd0;
add_cb_0_18b <= 18'd0;
add_cr_0_18b <= 18'd0;
add_y_1_18b <= 18'd0;
add_cb_1_18b <= 18'd0;
add_cr_1_18b <= 18'd0;
result_y_18b <= 18'd0;
result_cb_18b <= 18'd0;
result_cr_18b <= 18'd0;
i_h_sync_delay_1 <= 1'd0;
i_v_sync_delay_1 <= 1'd0;
i_data_en_delay_1 <= 1'd0;
i_h_sync_delay_2 <= 1'd0;
i_v_sync_delay_2 <= 1'd0;
i_data_en_delay_2 <= 1'd0;
end
/********************************************************/
/**************arithmetic********************************/
//LV1 pipeline : mult
always @ (posedge clk)
begin
mult_r_for_y_18b <= i_r_8b * para_0183_10b;
mult_r_for_cb_18b <= i_r_8b * para_0101_10b;
mult_r_for_cr_18b <= i_r_8b * para_0439_10b;
end
always @ (posedge clk)
begin
mult_g_for_y_18b <= i_g_8b * para_0614_10b;
mult_g_for_cb_18b <= i_g_8b * para_0338_10b;
mult_g_for_cr_18b <= i_g_8b * para_0399_10b;
end
always @ (posedge clk)
begin
mult_b_for_y_18b <= i_b_8b * para_0062_10b;
mult_b_for_cb_18b <= i_b_8b * para_0439_10b;
mult_b_for_cr_18b <= i_b_8b * para_0040_10b;
end
//LV2 pipeline : add
always @ (posedge clk)
begin
add_y_0_18b <= mult_r_for_y_18b + mult_g_for_y_18b;
add_y_1_18b <= mult_b_for_y_18b + para_16_18b;
add_cb_0_18b <= mult_b_for_cb_18b + para_128_18b;
add_cb_1_18b <= mult_r_for_cb_18b + mult_g_for_cb_18b;
add_cr_0_18b <= mult_r_for_cr_18b + para_128_18b;
add_cr_1_18b <= mult_g_for_cr_18b + mult_b_for_cr_18b;
end
//LV3 pipeline : y + cb + cr
assign sign_cb = (add_cb_0_18b >= add_cb_1_18b);
assign sign_cr = (add_cr_0_18b >= add_cr_1_18b);
always @ (posedge clk)
begin
result_y_18b <= add_y_0_18b + add_y_1_18b;
result_cb_18b <= sign_cb ? (add_cb_0_18b - add_cb_1_18b) : 18'd0;
result_cr_18b <= sign_cr ? (add_cr_0_18b - add_cr_1_18b) : 18'd0;
end
always @ (posedge clk)
begin
y_tmp <= result_y_18b[17:8] + {9'd0,result_y_18b[7]};
cb_tmp <= result_cb_18b[17:8] + {9'd0,result_cb_18b[7]};
cr_tmp <= result_cr_18b[17:8] + {9'd0,result_cr_18b[7]};
end
//output
assign o_y_8b = (y_tmp[9:8] == 2'b00) ? y_tmp[7 : 0] : 8'hFF;
assign o_cb_8b = (cb_tmp[9:8] == 2'b00) ? cb_tmp[7 : 0] : 8'hFF;
assign o_cr_8b = (cr_tmp[9:8] == 2'b00) ? cr_tmp[7 : 0] : 8'hFF;
/****************************************************************/
/*******************timing***************************************/
always @ (posedge clk)
begin
i_h_sync_delay_1 <= i_h_sync;
i_v_sync_delay_1 <= i_v_sync;
i_data_en_delay_1 <= i_data_en;
i_h_sync_delay_2 <= i_h_sync_delay_1;
i_v_sync_delay_2 <= i_v_sync_delay_1;
i_data_en_delay_2 <= i_data_en_delay_1;
i_h_sync_delay_3 <= i_h_sync_delay_2;
i_v_sync_delay_3 <= i_v_sync_delay_2;
i_data_en_delay_3 <= i_data_en_delay_2;
end
//--------------------------------------
//timing
//--------------------------------------
assign o_h_sync = i_h_sync_delay_3;
assign o_v_sync = i_v_sync_delay_3;
assign o_data_en = i_data_en_delay_3;
/***********************************************************/
endmodule
/*
代码 2: Ycbcr 转灰度图像
*/
wire [15:0] rgb;
wire hs;
wire vs;
wire de;
wire[7 : 0] o_y_8b;
wire[7 : 0] o_cb_8b;
wire[7 : 0] o_cr_8b;
assign TFT_rgb = {o_y_8b[7:3],o_y_8b[7:2],o_y_8b[7:3]}; //Y
//assign TFT_rgb = {o_cb_8b[7:3],o_cb_8b[7:2],o_cb_8b[7:3]}; //cb
//assign TFT_rgb = {o_cr_8b[7:3],o_cr_8b[7:2],o_cr_8b[7:3]}; //cr
另外,这篇博客讲的也很细致, 图像处理的灰度化和二值化