天天看點

【查蟲日志】快速判斷一副灰階圖像中是否隻有黑色和白色值(即是否為二值圖像)過程中bool變量的是是非非。

二值圖像我們在圖像處理過程中是經常遇到的,有的時候我們在進行一個算法處理前,需要判斷下一副圖像的資料是否符合二值圖的需求,這個時候我們可以寫個簡單的函數來做個判斷。

  二值圖像我們在圖像處理過程中是經常遇到的,有的時候我們在進行一個算法處理前,需要判斷下一副圖像的資料是否符合二值圖的需求,這個時候我們可以寫個簡單的函數來做個判斷,比如我寫了一個很簡單的的代碼如下:

  即如果存在一個像素如果不為255,也不為0,則這副圖就不是二值圖,立即可以傳回了,而無需進行後續的判斷了。  

  當一副圖不是二值圖時,通常,我們很快就能傳回結果了,那麼最壞的情況就是他恰好是二值圖,這樣,我們就要周遊完所有的像素。我們測試過對于16MB的二值圖(4000*4000),測試需要15ms的時間,為了能盡量減少耗時,可以使用如下的SIMD指令來優化這個判斷:

  由于SIMD指令裡沒有_mm_cmpneq_epi8函數,我們該用代碼1片段裡被注釋掉的那種邏輯來判斷一個像素是否是黑色和白色,這裡當然也有一些技巧,比如_mm_movemask_epi8指令的運用。我們判斷這個像素是否等于255和0,當然,一個像素不可能同時滿足這兩個條件,不滿足的Mask傳回0,滿足則Mask傳回255,是以如果他是黑色和白色,你們這兩個Mask進行或操作肯定就為255,否則或操作後就為0,SIMD中這樣的比較可以一次性進行16個像素,如果這16個像素都符合條件,那麼或操作後的mask都為255,這樣通過使用_mm_movemask_epi8來判斷這個mask就完成了16個像素的判斷。

  很顯然,這個過程的效率要高很多,測試16MB的真二值圖,也就1ms就完成了判斷。

  好,我用上面的那個代碼寫成DLL,供C#調用,相關的函數聲明如下:

  可出來的結果令我非常詫異,我測試了下面這2幅圖:

【查蟲日志】快速判斷一副灰階圖像中是否隻有黑色和白色值(即是否為二值圖像)過程中bool變量的是是非非。
【查蟲日志】快速判斷一副灰階圖像中是否隻有黑色和白色值(即是否為二值圖像)過程中bool變量的是是非非。

            測試圖1                               測視圖2 (頁面壓縮了)

  這兩幅圖都不是二值圖,他們在某些邊緣位置都有抗鋸齒操作。但是那個IM_IsBinaryImage_C檢測圖1不是二值圖像,檢測圖2 是二值圖像,而IM_IsBinaryImage_SSE_Bug則檢測圖1是二值圖像,圖2不是二值圖像。開始我以為是我的SSE代碼寫錯了,我就又換了一種寫法,如下所示:

  這個時候測繪對所有的圖像結果都正确了。

  但是,我覺得代碼片段2應該是不會有任何錯誤的啊。為什麼會出現這種現象呢。

  後面從網上查了下,C++的bool變量就隻有true和false, 是位元組變量,這個可以用printf("%d", sizeof(false));來驗證,會列印1。而在其他語言中,似乎是int類型。但是我在C#中用 MessageBox.Show(sizeof(bool).ToString());  似乎也是彈出1。

  但是,當我們把這些函數的傳回值都改為int後,在C#中調用就正常了,比如:

  也就是說上述的IM_IsBinaryImage_SSE_Bug函數體并無Bug。這到底是怎麼回事,還請萬能的網絡高手有空予以解疑。

  附上測試工程和代碼:https://files.cnblogs.com/files/Imageshop/ISBinaryImage.rar