FPGA实现图像的非线性变换:对数(log)变换

序章

  图像增强常用的三类基本函数:线性函数(反转和恒等变换)、对数函数(对数和反对数变换)和幂律函数(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实现

1、log函数查找表
  对于 FPGA 来说,直接实现对数公式难度很大,像这种问题我们多采用基于查找表的方式进行对数变换。ROM 表的制作可以用MATLAB实现,代码如下所示:
%--------------------------------------------------------------------------
%--           生成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_log_de_r    <= 1'b0;
        Y_log_hsync_r <= 1'b0;
        Y_log_vsync_r <= 1'b0;
    end
    else begin  
        Y_log_de_r    <= Y_de;
        Y_log_hsync_r <= Y_hsync;
        Y_log_vsync_r <= Y_vsync;
    end
end

assign Y_log_de    = Y_log_de_r;
assign Y_log_hsync = Y_log_hsync_r;
assign Y_log_vsync = Y_log_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
        log_de_r    <= 1'b0;
        log_hsync_r <= 1'b0;
        log_vsync_r <= 1'b0;
    end
    else begin  
        log_de_r    <= RGB_de;
        log_hsync_r <= RGB_hsync;
        log_vsync_r <= RGB_vsync;
    end
end

assign log_de    = log_de_r;
assign log_hsync = log_hsync_r;
assign log_vsync = log_vsync_r;

3、上板验证

  原图:

  log变换图:

  虽然我的板子坏了,但总的效果还是成功的,图像整体变亮了,和MATLAB的效果差不多,实验成功。

参考资料:

[1] OpenS Lee:FPGA开源工作室(公众号)

[2] 张铮, 王艳平, 薛桂香. 数字图像处理与机器视觉[M]. 人民邮电出版社, 2010.

猜你喜欢

转载自www.cnblogs.com/xianyufpga/p/12514725.html