文章目录
1.什么是亮度
"图像亮度"是一个横跨物理光学、数字编码和视觉感知三个领域的概念。在不同的语境下,它的定义截然不同。
(1)物理光学维度
在严格的物理学中,亮度(Luminance),指单位面积上人眼能够感知的光通量(即光强)。
定义:光源或物体表面在特定方向上的发光强度,单位是尼特(nit)或坎德拉每平方米(cd/m²)。
本质:这是光的客观物理测量值。比如,太阳的亮度极高(约16亿尼特),而一张白纸在月光下亮度极低。
(2) 数字图像处理维度
在计算机里,图像是由像素矩阵组成的,亮度指的是像素的明度值(Lightness / Intensity / Value)。
灰度图定义:亮度就是该像素的灰度级(通常为0~255,0为纯黑,255为纯白)。
彩色图定义:通常将RGB三个通道进行加权转换,公式为:Y = 0.299×R + 0.587×G + 0.114×B,这个Y值即为亮度通道,绿色权重最大,因为人眼对绿光最敏感。
(3)人类视觉感知维度
人眼对亮度的感知是非线性的,定义上被称为明度(Brightness)。
定义:人眼对物体"看起来有多亮"的主观评价。核心规律(伽马效应):人眼对暗部细节极其敏感,但对亮部的细微变化反应迟钝。
本质:物理亮度翻倍,人眼感知的明度并不会翻倍。因此数字图像存储时大多采用伽马校正(Gamma校正),即编码时故意把暗部数据压得更丰富,以匹配人眼的非线性的"亮度定义"。

2.亮度调节原理
这里按数字图像处理方式对亮度做调节,原理:将RGB三个分量进行加权转换得到亮度Y值和色差值Cb和Cr,对Y加一个偏移量调节亮度,之后再将调节后Y值和色差值Cb和Cr转换为RGB分量。
Y_out =Y_in + ΔY
Y_in:输入的8位亮度值(范围0-255)。
Y_out:输出的8位亮度值(限制在0~255范围)。
ΔY:亮度调节值(范围-255~255)。
3.FPGA实现代码
(1)RGB转YC代码
powershell
module rgb_yc444
(
input pix_clk ,
input rst_n ,
input [7:0] r_in ,
input [7:0] g_in ,
input [7:0] b_in ,
input din_valid ,
output dout_valid ,
output reg [7:0] y ,
output reg [7:0] cb ,
output reg [7:0] cr
);
//====================================================================
//---------------------------------------------------------
// => Y709 = 0.213R′ + 0.715G′ + 0.072B′
// => Cb = --0.117R′ -- 0.394G′ + 0.511B′ + 128
// => Cr = 0.511R′ -- 0.464G′ -- 0.047B′ + 128
//---------------------------------------------------------
// => Y709 =55/256R'+183/256G'+18/256B';
// => Cb =-30/256R'-101/256G'+131/256B'+32768/256;
// => Cr =131/256R'-119/256G'-12/256B'+32768/256;
// --------+128为四舍五入操作-----------------------------
// => Y709 =(55R'+183G'+18B'+128)>>8;
// => Cb =(-30R'-101G'+131B'+32768+128)>>8;
// => Cr =(131R'-119G'-12B'+32768+128)>>8;
// -------------------------------------------------------
// => Y709 =((64-8-1)R'+(256-64-8-1)G'+(16+2)B'+128)>>8;
// => Cb =(-(32-2)R'-(64+32+4+1)G'+(128+2+1)B'+32768+128)>>8;
// => Cr =((128+2+1)R'-(128-8-1)G'-(8+4)B'+32768+128)>>8;
// -------------------------------------------------------
wire [15:0] y_r_tmp ;
wire [15:0] y_g_tmp ;
wire [15:0] y_b_tmp ;
wire [15:0] cb_b_tmp;
wire [15:0] cb_g_tmp;
wire [15:0] cb_r_tmp;
wire [15:0] cr_r_tmp;
wire [15:0] cr_g_tmp;
wire [15:0] cr_b_tmp;
reg [15:0] y_r_reg = 16'd0;
reg [15:0] y_g_reg = 16'd0;
reg [15:0] y_b_reg = 16'd0;
reg [15:0] cb_b_reg = 16'd0;
reg [15:0] cb_g_reg = 16'd0;
reg [15:0] cb_r_reg = 16'd0;
reg [15:0] cr_r_reg = 16'd0;
reg [15:0] cr_g_reg = 16'd0;
reg [15:0] cr_b_reg = 16'd0;
wire [9:0] Y_tmp ;
wire [9:0] Cb_tmp;
wire [9:0] Cr_tmp;
reg dout_valid1 = 1'b0;
reg dout_valid2 = 1'b0;
reg dout_valid3 = 1'b0;
reg [7:0]Y_node;
reg [7:0]Cb_node;
reg [7:0]Cr_node;
//====================================================================================
assign y_r_tmp = (r_in<<6) - (r_in<<3) - (r_in<<1);
assign y_g_tmp = (g_in<<8) - (g_in<<6) - (g_in<<3) - g_in;
assign y_b_tmp = (b_in<<4) + (b_in<<1);
assign cb_r_tmp = (r_in<<5) - (r_in<<1);
assign cb_g_tmp = (g_in<<6) + (g_in<<5) + (g_in<<2) + g_in;
assign cb_b_tmp = (b_in<<7) + (b_in<<1) + b_in;
assign cr_r_tmp = (r_in<<7) + (r_in<<1) + r_in;
assign cr_g_tmp = (g_in<<7) - (g_in<<3) - g_in;
assign cr_b_tmp = (b_in<<3) + (b_in<<2);
always@(posedge pix_clk)
begin
y_r_reg <= y_r_tmp ;
y_g_reg <= y_g_tmp ;
y_b_reg <= y_b_tmp ;
cb_b_reg <= cb_b_tmp;
cb_g_reg <= cb_g_tmp;
cb_r_reg <= cb_r_tmp;
cr_r_reg <= cr_r_tmp;
cr_g_reg <= cr_g_tmp;
cr_b_reg <= cr_b_tmp;
end
// --------+128为四舍五入操作-----------------------------
assign Y_tmp = (y_r_reg +y_g_reg +y_b_reg+8'd128 )>>8;
assign Cb_tmp= (cb_b_reg-cb_g_reg-cb_r_reg+8'd128+ 32768)>>8;
assign Cr_tmp= (cr_r_reg-cr_g_reg-cr_b_reg+8'd128+ 32768)>>8;
//===========================================================
//overflow control
always@(posedge pix_clk or negedge rst_n)
begin
if(!rst_n)
Y_node<=8'b0;
else if(Y_tmp[9:8]==2'b00)
Y_node<=Y_tmp[7:0];
else if(Y_tmp[9:8]==2'b01)
Y_node<=8'hff;
else
Y_node<=8'h00;
end
always@(posedge pix_clk or negedge rst_n)
begin
if(!rst_n)
Cb_node<=8'b0;
else if(Cb_tmp[9:8]==2'b00)
Cb_node<=Cb_tmp[7:0];
else if(Cb_tmp[9:8]==2'b01)
Cb_node<=8'hff;
else
Cb_node<=8'h00;
end
always@(posedge pix_clk or negedge rst_n)
begin
if(!rst_n)
Cr_node<=8'b0;
else if(Cr_tmp[9:8]==2'b00)
Cr_node<=Cr_tmp[7:0];
else if(Cr_tmp[9:8]==2'b01)
Cr_node<=8'hff;
else
Cr_node<=8'h00;
end
always@(posedge pix_clk or negedge rst_n)
begin
if(!rst_n)
begin
y <=8'b0;
cb<=8'b0;
cr<=8'b0;
end
else
begin
y <=Y_node;
cb<=Cb_node;
cr<=Cr_node;
end
end
//-----------------------------------------------------
always@(posedge pix_clk)
begin
dout_valid1<=din_valid;
dout_valid2<=dout_valid1;
dout_valid3<=dout_valid2;
end
assign dout_valid = dout_valid3;
endmodule
(2)YC转RGB代码
powershell
module yc444_rgb
(
input pix_clk ,
input rst_n ,
input [7:0] y ,
input [7:0] cb ,
input [7:0] cr ,
input din_valid ,
output reg dout_valid,
output reg [7:0] r_o ,
output reg [7:0] g_o ,
output reg [7:0] b_o
);
//====================================================================
// r=y+1.540*(cr-128);
// g=y-0.459*(cr-128)-0.183*(cb-128);
// b=y+1.816*(cb-128);
//-----------------------------------------------------------------
// --> r=(256*y+394.24*cr-50462.72)/256;
// --> g=(256*y-117.504*cr-46.848*cb+21037.056)/256;
// --> b=(256*y+464.896*cb-59506.688)/256;
// --------+128为四舍五入操作-----------------------------
// --> r=(256*y+394*cr-50463+128)/256;
// --> g=(256*y-118*cr-47*cb+21037+128)/256;
// --> b=(256*y+465*cb-59507+128)/256;
//----------------------------------------------------------
// --> r={[256*y+(256+128+8+2)*cr]-50335}/256;
// --> g={(256*y+21165)-[(64+32+16+4+2)*cr+(32+16-1)*cb]}/256;
// --> b={[256*y+(256+128+64+16+1)*cb]-59635]}/256;
//----------------------------------------------------------
reg rgb_tmp_en;
reg [19:0] r_minuend;
reg [19:0] r_subtrahend;
reg [19:0] g_minuend;
reg [19:0] g_subtrahend;
reg [19:0] b_minuend;
reg [19:0] b_subtrahend;
reg rgb_en_flag;
reg [19:0] rgb_r_flag;
reg [19:0] rgb_g_flag;
reg [19:0] rgb_b_flag;
//====================================================================================
always @(posedge pix_clk) rgb_tmp_en <= din_valid;
always @(posedge pix_clk) r_minuend <= (y<<8) + ((cr<<8) + (cr<<7)) + ((cr<<3) + (cr<<1));
always @(posedge pix_clk) r_subtrahend <= 17'd50335;
always @(posedge pix_clk) g_minuend <= (y<<8) + 17'd21165;
always @(posedge pix_clk) g_subtrahend <= ((cr<<6) + (cr<<5)) + ((cr<<4) + (cr<<2)+ (cr<<1)) + ((cb<<5) + (cb<<4) - cb);
always @(posedge pix_clk) b_minuend <= (y<<8) + ((cb<<8) + (cb<<7)) + ((cb<<6) + (cb<<4)) + cb;
always @(posedge pix_clk) b_subtrahend <= 17'd59635;
always @( posedge pix_clk or negedge rst_n)
begin
if (rst_n==1'b0)
begin
rgb_en_flag <= 1'b0;
rgb_r_flag <= 20'h0_0000;
rgb_g_flag <= 20'h0_0000;
rgb_b_flag <= 20'h0_0000;
end
else
begin
rgb_en_flag <= rgb_tmp_en;
rgb_r_flag <= (rgb_tmp_en==1'b1) ? (r_minuend-r_subtrahend) : 20'h0_0000;
rgb_g_flag <= (rgb_tmp_en==1'b1) ? (g_minuend-g_subtrahend) : 20'h0_0000;
rgb_b_flag <= (rgb_tmp_en==1'b1) ? (b_minuend-b_subtrahend) : 20'h0_0000;
end
end
//==============================================================
//overflow calibration
always @( posedge pix_clk or negedge rst_n)
begin
if (rst_n==1'b0)
begin
dout_valid <= 1'b0;
r_o <= 8'h00;
g_o <= 8'h00;
b_o <= 8'h00;
end
else
begin
dout_valid <= rgb_en_flag;
r_o <= (rgb_r_flag[17:16] ==2'b00) ? (rgb_r_flag[15:8]) : ((rgb_r_flag[17:16] ==2'b01) ? (8'hff) : (8'h00));
g_o <= (rgb_g_flag[17:16] ==2'b00) ? (rgb_g_flag[15:8]) : ((rgb_g_flag[17:16] ==2'b01) ? (8'hff) : (8'h00));
b_o <= (rgb_b_flag[17:16] ==2'b00) ? (rgb_b_flag[15:8]) : ((rgb_b_flag[17:16] ==2'b01) ? (8'hff) : (8'h00));
end
end
endmodule
(3)亮度调节代码
powershell
module rgb_bright
(
input wire clk,
input wire rst_n,
// 输入像素
input wire [8-1:0] din_r,
input wire [8-1:0] din_g,
input wire [8-1:0] din_b,
input wire din_valid,
// 亮度调节
input wire add_sub,//0:add, 1:sub
input wire [8-1:0] delta_y,//0~255
// 输出像素
output [8-1:0] dout_r,
output [8-1:0] dout_g,
output [8-1:0] dout_b,
output dout_valid
);
// =====================
wire [7:0] y ;
wire [7:0] cb;
wire [7:0] cr;
wire yc_valid;
rgb_yc444 rgb_yc444_inst
(
.pix_clk (clk),
.rst_n (rst_n),
.r_in (din_r),
.g_in (din_g),
.b_in (din_b),
.din_valid (din_valid),
.dout_valid (yc_valid),
.y (y ),
.cb (cb),
.cr (cr)
);
// ==========================================
reg [9:0] sum;
reg [7:0] y_limit;
reg [7:0] cb_d1,cb_d2;
reg [7:0] cr_d1,cr_d2;
reg yc_valid_d1,yc_valid_d2;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
sum<=10'b0;
else if(add_sub==1'b0)
sum<= $signed({1'b0,y}) + $signed({1'b0,delta_y});
else if(add_sub==1'b1)
sum<= $signed({1'b0,y}) - $signed({1'b0,delta_y});
else
sum<=sum;
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
y_limit<=8'b0;
else
y_limit<= (sum[9:8] == 2'b01) ? 8'd255 :
(sum[9:8] == 2'b11) ? 8'd0 :
(sum[9:8] == 2'b00) ? sum[7:0] : 8'd0;
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cb_d1 <= 8'b0;
cb_d2 <= 8'b0;
cr_d1 <= 8'b0;
cr_d2 <= 8'b0;
yc_valid_d1 <= 1'b0;
yc_valid_d2 <= 1'b0;
end
else
begin
cb_d1 <= cb ;
cb_d2 <= cb_d1;
cr_d1 <= cr ;
cr_d2 <= cr_d1;
yc_valid_d1 <= yc_valid ;
yc_valid_d2 <= yc_valid_d1;
end
end
// ======================
yc444_rgb yc444_rgb_inst
(
.pix_clk (clk),
.rst_n (rst_n),
.y (y_limit ),
.cb (cb_d2),
.cr (cr_d2),
.din_valid (yc_valid_d2),
.dout_valid(dout_valid),
.r_o (dout_r),
.g_o (dout_g),
.b_o (dout_b)
);
endmodule
4.效果对比
(1)亮度提升效果
调用模块,亮度增量取64时效果。

(2)亮度降低效果
调用模块,亮度增量取-64时效果。
