MSRCR(Multi-Scale Retinex with Color Restore)多尺度Retinex图像增强
引言
始于Edwin Herbert Land(埃德溫·赫伯特·蘭德)于1971年提出的一種被稱為色彩恒常的理論,并基于此理論的圖像增強方法。Retinex這個詞由視網膜(Retina)和大腦皮層(Cortex)合成而來.之所以這樣設計,表明Land他也不清楚視覺系統的特性究竟取決于此兩個生理結構中的哪一個,抑或兩者都有關系。不同于傳統的圖像增強算法,如線性、非線性變換、圖像銳化等只能增強圖像的某一類特征,如壓縮圖像的動態范圍,或增強圖像的邊緣等,Retinex可以在動態范圍壓縮、邊緣增強和顏色恒常三方面達到平衡,可以對各種不同類型的圖像進行自適應性地增強,在很多方面得到了廣泛的應用。
算法概述
Retinex 理論的基本內容是物體的顏色是由物體對長波(紅)、中波(綠)和短波(藍)光線的反射能力決定的,而不是由反射光強度的絕對值決定的;物體的色彩不受光照非均性的影響,具有一致性,即Retinex理論是以色感一致性(顏色恒常性)為基礎的。如下圖所示,觀察者所看到的物體的圖像S是由物體表面對入射光L反射得到的,反射率R由物體本身決定,不受入射光L變化。?
對于觀察圖像S,有公式表示為:?
其中, L(x,y) 表示亮度分量, R(x,y) 表示物體反射分量, S(x,y)表示觀測到的圖像 。?
Retinex理論增強算法的思想就是利用式(1),去除亮度分量 L 求得反射分量 R ,從而達到圖像增強效果。?
對(1)式兩邊取對數?
log(S(x,y))=log(L(x,y))+log(R(x,y))(2)
由(2)得?
log(R(x,y))=log(S(x,y))?log(L(x,y))(3)
由式(3)可知,只需要估計亮度分量 L 就能求得反射分量,因此L的估計直接決定圖像去霧恢復效果.Jobson等論證了高斯卷積函數可以從已知圖像 S 中更好地估計出亮度分量,即?
L(x,y)=S(x,y)?G(x,y)(4)
其中,’*’代表卷積操作,高斯函數 G(x,y)=k?exp(?x2+y2σ2) , σ 是高斯函數尺度參數, k 為歸一化因子,使 ?G(x,y)dxdy=1 。對于RGB彩色圖像的某一個通道,?
log(Rc(x,y))=log(Sc(x,y))?log(Sc(x,y))?G(x,y)(5)
其中, c 表示RGB3個通道的某一個通道; σ ?為尺度函數,取值大時,顏色失真小但細節恢復差,取值小時,細節恢復好,但顏色失真大。?
為彌補SSR算法不足,MSR算法對每一個通道進行3次不同尺度濾波,加權求和,處理時間也會變長,且使用不同的尺度,導致恢復的RGB比值與原圖比值不太一樣,顏色失真。?
log(R(x,y))=Weight1?log(Rσ1(x,y))+Weight2?log(Rσ2(x,y))+Weight3?log(Rσ3(x,y))(6)
Rehman等提出MSRCR算法,引入分量比值調整因子從而降低色彩失真的影響。?
RMSRCR(x,y)=C(x,y)RMSR(x,y)(7)
其中 C(x,y)=G{log[αIc(x,y)]?log[∑c=13Ic(x,y)]} 。
流程
入射分量獲取
Retinex算法核心在于入射分量的獲取,簡單來說,這個可以對原圖像進行高斯卷積獲取,卷積核的大小與原圖像大小相等。當圖像比較大的時候,其計算過程是非常復雜的,O(M2N2),M,N是原圖像的高度和寬度。實現的時候可以使用遞歸高斯濾波,復雜度O(MN)(參見paper:Recursive implementation of the Gaussian filter,源碼在GIMP中有)。下面我給出在3種不同尺度下獲取的亮度圖,等分權重。?
色彩恢復
GIMP中色彩恢復代碼如下:
<code class="hljs cpp has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Final calculation with original value and cumulated filter values. The parameters gain, alpha and offset are constants. */</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Ci(x,y)=log[a Ii(x,y)]-log[ Ei=1-s Ii(x,y)] */</span> alpha = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">128.0f</span>; gain = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0f</span>; offset = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.0f</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> ( i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < size; i += bytes ) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span> logl; psrc = src+i; pdst = dst+i; logl = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">log</span>( (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)psrc[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] + (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)psrc[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] + (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)psrc[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>] + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3.0f</span> ); pdst[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] = gain * ((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">log</span>(alpha * (psrc[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0f</span>)) - logl) * pdst[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]) + offset; pdst[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] = gain * ((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">log</span>(alpha * (psrc[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>]+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0f</span>)) - logl) * pdst[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>]) + offset; pdst[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>] = gain * ((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">log</span>(alpha * (psrc[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>]+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0f</span>)) - logl) * pdst[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>]) + offset; }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>另外,最后一步代碼又增加了調節項,用于控制飽和度和顏色,最后將其量化到[0~255],詳細代碼如下。
<code class="hljs applescript has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> /* Adapt <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> dynamics <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> colors according <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> statistics <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">first</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">second</span> order. The use <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> variance makes <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">it</span> possible <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> control <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> degree <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> saturation <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> colors. */ pdst = dst;compute_mean_var( pdst, &mean, &var, size, bytes ); mini = mean - rvals.cvar*var; maxi = mean + rvals.cvar*var; range = maxi - mini; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ( !range ) range = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> ( i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < size; i+= bytes ) { psrc = src + i; pdst = dst + i; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (j = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> ; j < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span> ; j++) { float c = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span> * ( pdst[j] - mini ) / range; psrc[j] = (unsigned char)clip( c, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span> ); } } free (dst);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li></ul>效果
我依據GIMP中插件contrast-retinex.c用Matlab實現了一遍,效果如下
寬動態圖像
水下圖像
低照度圖像
霧天圖像
評論
Retinex算法在圖像增強諸多領域應用廣泛,如上述寬動態圖像增強,水下圖像增強,霧天圖像增強,低照度圖像增強等等,更多可以參考NASA關于Retinex的介紹。Retinex的實現,C語言用了遞歸高斯濾波器,我不知道Matlab是否有對應的函數,我在這里是將圖像轉換到頻率域高斯低通濾波處理的,因此算法時間耗費也并不大。OpenCV版本的Retinex算法已傳到CSDN上共享,下載請點擊這里,記得給好評哦。
更多閱讀
http://dragon.larc.nasa.gov/?
http://www.cnblogs.com/Imageshop/archive/2013/04/17/3026881.html?
A multiscale retinex for bridging the gap between color images and the human observation of scenes
總結
以上是生活随笔為你收集整理的MSRCR(Multi-Scale Retinex with Color Restore)多尺度Retinex图像增强的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习——人工神经网络再掀研究热潮
- 下一篇: 图像处理概述