序章
图像增强常用的三类基本函数:线性函数(反转和恒等变换)、对数函数(对数和反对数变换)和幂律函数(n次幂和n次根变换)。如下图所示:
其中恒等变换和反转变换都属于线性变换,在之前的博客中我整理过反转变换,而直接的线性变换的效果其实不太好,分段线性变换的效果会更常用些,但分段线性变换需要输入过多参数,且对不同的图片输入的参数都要调整,比较繁琐,所以没有去研究。
现在用两篇博客来整理一下非线性变换吧。
一、对数变换理论
对数变换的一般表达式为:s = c log(1+r)
其中 c 为尺度比例常数,r 为源灰度值,s 为变换后的目标灰度值。由对数函数曲线可知,这种变换可以增强一幅图像中较暗部分的细节,从而可用来扩展被压缩的高值图像中的较暗像素,因此对数变换被广泛地应用于频谱图像的显示中。一个典型的应用是傅立叶频谱,其动态范围可能宽达0~ 10%。直接显示频谱时,图像显示设备的动态范围往往不能满足要求,从而丢失大量的暗部细节。而在使用对数变换之后,图像的动态范围被合理地非线性压缩,从而可以清晰地显示。
说的通俗一点:该变换将输入中范围较窄的低灰度值映射为输出中较宽范围的灰度值,相反地,对高的输入灰度值也是如此。我们使用这种类型的变换来扩展图像中的暗像素的值,同时压缩更高灰度级的值。反对数变换的作用与此相反。
对数函数的一般形状的任何曲线,都能完成图像灰度级的扩展/压缩,但是,下一篇博客的伽马(幂律)变换对这个则更为通用。
二、MATLAB实现
%-------------------------------------------------------------------------- %-- 灰度log变换 %-------------------------------------------------------------------------- clear all; RGB = imread('log.jpg'); %读取图片文件 gray = rgb2gray(RGB); gray_log = mat2gray(log(1+double(gray))); subplot(1,2,1);imshow(gray); xlabel('灰度图像'); subplot(1,2,2);imshow(gray_log);xlabel('对数变换');
这里把 c 取值为1,点击运行,得到如下结果:
三、FPGA实现
%-------------------------------------------------------------------------- %-- 生成log变换所需的rom mif文件 %-------------------------------------------------------------------------- clear all close all clc depth = 256; width = 8; r = [0:1:255]; s = 45*log(1+r); fid = fopen('log2.mif','w'); fprintf(fid,'depth= %d; \n',depth); fprintf(fid,'width= %d; \n',width); fprintf(fid,'address_radix=uns;\n'); fprintf(fid,'data_radix = uns;\n'); fprintf(fid,'Content Begin \n'); z = round(s); for(k=1:depth) fprintf(fid,'%d: %d; \n',k-1,z(k)); end fprintf(fid,'end;'); %-------------------------------------------------------------------------- %-- 曲线展示 %-------------------------------------------------------------------------- hold on plot(r); plot(s); legend('原曲线','log变换'); hold off
点击运行,就可以得到 log2.mif 文件,此外可以看到这次采用的 log 曲线如图所示:
2、代码设计
有了 rom 查找表,代码设计变得异常简单,直接拿数据进查找表对照即可,关键代码如下所示:
//========================================================================== //== log变换 //========================================================================== rom_log u_rom_log ( .address (Y_data ), .clock (clk ), .q (Y_log_data ) ); //========================================================================== //== 信号同步,rom给地址到出数据会延迟1clk //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) begin Y_de_r <= 1'b0; Y_hsync_r <= 1'b0; Y_vsync_r <= 1'b0; end else begin Y_de_r <= Y_de; Y_hsync_r <= Y_hsync; Y_vsync_r <= Y_vsync; end end assign Y_log_de = Y_de_r; assign Y_log_hsync = Y_hsync_r; assign Y_log_vsync = Y_vsync_r;
注意一下位宽,这次设计的查找表地址为 8 bit 256个数据,刚好和 Y 分量的位数相等。
3、上板验证
灰度图:
log变换图:
和MATLAB有细微差异,手机拍照时的光线也有问题,总的来说我们实现了灰度图的 log 变换。
四、彩色图像的 log 变换
虽然 log 变换最开始是针对灰度图像的,但是拿彩色图像的RGB三通道出来做 log 变换也能起到不错的效果。
1、MATLAB效果
%-------------------------------------------------------------------------- %-- 彩色图像log变换 %-------------------------------------------------------------------------- clear all; RGB = imread('img.jpg'); %读取图片文件 LOG = mat2gray(log(1+double(RGB))); subplot(1,2,1);imshow(RGB);xlabel('原始图像'); subplot(1,2,2);imshow(LOG);xlabel('对数变换');
2、代码设计
//========================================================================== //== RGB565转RGB888 //========================================================================== assign R = {RGB_data[15:11],RGB_data[13:11]}; assign G = {RGB_data[10: 5],RGB_data[ 6: 5]}; assign B = {RGB_data[ 4: 0],RGB_data[ 2: 0]}; //========================================================================== //== log变换 //========================================================================== rom_log u_R ( .address (R ), .clock (clk ), .q (log_R ) ); rom_log u_G ( .address (G ), .clock (clk ), .q (log_G ) ); rom_log u_B ( .address (B ), .clock (clk ), .q (log_B ) ); assign log_data = {log_R[7:3],log_G[7:2],log_B[7:3]}; //========================================================================== //== 信号同步,rom给地址到出数据会延迟1clk //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) begin RGB_de_r <= 1'b0; RGB_hsync_r <= 1'b0; RGB_vsync_r <= 1'b0; end else begin RGB_de_r <= RGB_de; RGB_hsync_r <= RGB_hsync; RGB_vsync_r <= RGB_vsync; end end assign log_de = RGB_de_r; assign log_hsync = RGB_hsync_r; assign log_vsync = RGB_vsync_r;
3、上板验证
原图:
log变换图:
图像整体变亮了,和MATLAB的效果差不多,实验成功。
如果希望图像整体变暗,则可以试试反对数变换,或者采用下一篇的伽玛(幂律)变换。
参考资料:
[1] OpenS Lee:FPGA开源工作室(公众号)
[2] 张铮, 王艳平, 薛桂香. 数字图像处理与机器视觉[M]. 人民邮电出版社, 2010.