序章
圖像增強常用的三類基本函數:線性函數(反轉和恆等變換)、對數函數(對數和反對數變換)和冪律函數(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.