基於局部直方圖的算法有很多很多,我們已經研究這類算法有以下一些:
1、中值濾波
2、表面模糊
3、選擇性模糊
4、中值銳化
5、圖像局部熵
這類算法有個通病,就是即使選擇使用SIMD指令加速,因爲其內在的特性,速度還是不能很快,但是又找不到其他合適的構架來優化他,還必須使用直方圖技術,比如我們的中值濾波, 我嘗試過各種商業軟件,其速度都和我博客裏提到的那個優化速度差不多,說明大家基本上都是那個套路。你們當確實某個場景需要更快的速度時,我們是否能有其他方法來加速呢,或者使用某個近似的方法來替代呢,經過個人的實踐,我覺得還是可以有的。
一個簡單的方法就是減少直方圖的數量,常規狀態下我們直方圖有256個元素,因爲基於局部直方圖的算法基本都是一些統計類算法,是大面積像素的統計信息,所以最終的結果其實也是個統計結果。那麼我們壓縮直方圖的量級,只要壓縮量合理,最後的結果可能差異不是很大。
我們可以把直方圖壓縮爲128等級、64等級、32等級,更小的等級可能信息損失量過大,當壓縮時,對應的直方圖統計工作不會減少,可能計算量還會稍微有點增加,如下面的代碼所示:
for (int Y = 0; Y < Height; Y++) { if (Y == 0) // 第一行的列直方圖,要重頭計算 { for (int K = -Radius; K <= Radius; K++) { unsigned char *LinePS = Src + ColOffset[K + Radius] * Stride; for (int X = -Radius; X < Width + Radius; X++) { ColHist[(X + Radius) * HistAmount + (LinePS[RowOffset[X + Radius]] >> Shift)]++; } } } else // 其他行的列直方圖,更新就可以了 { unsigned char *LinePS = Src + ColOffset[Y - 1] * Stride; for (int X = -Radius; X < Width + Radius; X++) // 刪除移出範圍內的那一行的直方圖數據 { ColHist[(X + Radius) * HistAmount + (LinePS[RowOffset[X + Radius]] >> Shift)]--; } LinePS = Src + ColOffset[Y + Radius + Radius] * Stride; for (int X = -Radius; X < Width + Radius; X++) // 增加進入範圍內的那一行的直方圖數據 { ColHist[(X + Radius) * HistAmount + (LinePS[RowOffset[X + Radius]] >> Shift)]++; } }
.........
}
很明顯,每個像素點都有右移Shift的計算,這個計算是憑空增加的,幸好,移位的計算量很小。
這個直方圖更新的耗時的輕微增加,換來的是後續,累計直方圖統計的數據量的集聚變小,以及計算過程的集聚下降,如下代碼所示:
for (int X = 0; X < Width; X++) { if (X == 0) { for (int K = -Radius; K <= Radius; K++) // 行第一個像素,需要重新計算 HistgramAddShort_PureC(ColHist + (K + Radius) * HistAmount, Hist, Shift); } else { HistgramSubAddShort_PureC(ColHist + (RowOffset[X - 1] + Radius) * HistAmount, ColHist + (RowOffset[X + Radius + Radius] + Radius) * HistAmount, Hist, Shift); // 行內其他像素,依次刪除和增加就可以了 } IM_Calc_SB_PureC(Hist, Intensity + HistAmount - 1 - (LinePS[X] >> Shift), LinePD + X, Shift); }
HistgramAddShort_PureC和HistgramSubAddShort_PureC裏的計算量隨着移位量的增加而變少,IM_Calc_SB_PureC裏的計算量也同步變少。
我們用一副1920*1080的灰度圖做測試,以表明模糊爲例,當不壓縮實現時,平均耗時276ms,壓縮到128等級時,用時160ms,64等級時,用時95ms,32等級時,用時51ms。
原始圖像 256級表面模糊 128級表面模糊
64級表面模糊 32級表面模糊 16級表面模糊
可見,對於表面模糊,甚至16級的效果都是可以接受的。
對於選擇性模糊、局部熵等算法,也是同樣的道理。
上面的幾個算法,其結果值都是某個權重累加值除以權重,其中間結果其實是個浮點數,因此,等級量化後對結果不是影響很大。
對於中值模糊,情況又有所不同,因爲中值是將直方圖分爲細分直方圖和粗分直方圖,而最終得到的結果是一個整形值,這個時候如果我們降低直方圖的色階精度,得到的結果可能會存在一定的瑕疵,特別是用在比較平滑的區域內,像素信息比較少,這個時候可以看到有較爲明顯的過渡色階。比如我們把色階調整爲64階,那麼細分則有64個元素,粗分有8個元素,這個時候算法的速度大概能提高一倍,不過效果會有一定差異,實際使用時這個差異是否在容許的範圍,就要看具體的應用了。
比如1920*1080的灰度圖,正常C語言滿色階中值模糊大概要200ms,如果調整爲64色階,大概在100ms實現。對於普通信息比較豐滿的圖,他們的效果差異也不大,如下圖所示:
原圖 256色階中值 64色階中值
如果圖像含有大面的看似純色的部分,則兩者就有一定的區別了。
原圖 256色階中值 64色階中值
明顯看到64色階的中值裏有很多halo現象。
所以具體的如何優化以及是否值得優化還要看具體的算法需求和應用場景。
有興趣的朋友可以從:https://files.cnblogs.com/files/Imageshop/SSE_Optimization_Demo.rar?t=1660121429 下載。
如果想時刻關注本人的最新文章,也可關注公衆號: