影像處理或其他多媒體處理中的值溢位處理

熊孩子開學嘍發表於2007-08-17

在網上查資料的時候,無意中連線到了“zyl910”的專欄,

看了裡面的一篇文章,感覺頗有收穫,就在此拋磚引玉一番。

原文見:http://blog.csdn.net/zyl910/archive/2006/10/11/1330614.aspx

 <?xml:namespace prefix = o />

在用VB寫影像處理程式的時候,經常會遇到運算結果超出範圍的情況。

比如在處理32位點陣圖的時候,由於每一種顏色分量都是8位,它們的值就不能超出0255的範圍。

否則,就會溢位到另一個顏色的分量裡去,或者是產生一個“負”的顏色值,前者破壞影像顯示效果,

後者將可能導致DIB崩潰。

 

其實只要在運算之後,輸出之前,加上兩句判斷就可以消除這種錯誤:

 

此處假定處理綠色分量:

if Green>255 then Green=255

if Green<0 then Green=0

當然,實際處理的時候,肯定不止一個點,因此這裡的Green也就會是一個陣列元素,外面套著N重迴圈了。

 

單單看這兩句,似乎也沒什麼不好。

可是這只是一個顏色分量而已,還有兩個顏色也得這麼處理:

if Red>255 then Red=255

if Red<0 then Red=0

if Blue>255 then Blue=255

if Blue<0 then Blue=0

而一個圖片少說也有好幾萬的象素吧,每一點象素處理完都得來這麼6句判斷。

N萬次的比較做下來,效率上的拖累可想而知。

 

而理想狀態的程式碼應該是型如 Out = Limitation(In) 的自洽方式,而不是醜陋的IF...THEN...

即用一個表示式來自然而然的得到“規範的,合乎要求”的資料。

 

首先,我這裡指的不是函式,用函式的話,我寧可用比較,因為函式更慢。

 

舉兩個例子:

例子一:

假設要寫一個很簡單的程式,介面上有一個CheckBox控制元件。

當使用者點選這個CheckBox的時候窗體背景變成黑色,反選這個CheckBox的時候窗體變成紅色。

讀者們,請先想一下,讓你寫這個程式,你會怎麼寫呢。

是否這樣寫呢:

Sub Check1_Click()

   If Check1.value=1 then

      Me.backColor=0

   Else

      Me.BackColor=VBred

   endif   

End Sub

或者是簡單點寫成一句:

If Check1.value=1 then Me.backColor=0 Else Me.BackColor=VBred

其實這兩句都是一樣的IF判斷語句,本質上沒有區別。

你有沒有想過這樣寫呢:

Sub Check1_Click()

   Me.BackColor=Not(-Check1.Value) and VBRed

End Sub

 

這樣的寫法要比前面的優美多了。

 

首先,可能很多朋友不明白這個語句,其實在VB裡面,邏輯變數也是一個LONG型變數。

True=-1

False=0

你可以在寫True的地方寫-1也可以在該寫False的地方用0代替。

 

因為CheckBox控制元件它點和不點的值是10,只要加個負號,它就成了一個邏輯變數了。

Not(-Check1.Value)

如果你點選了CheckBoxNot(-Check1.Value)=>Not(-1)=>Not True=> False

如果你反選了CheckBoxNot(-Check1.Value)=>Not(0)=>Not False=>True

 

最後就是一個邏輯“與”運算了:True And Anything Anything

                          而:False And Anything False 0

於是,一個判斷語句就變成了一個簡單的賦值語句。

 

例子二:

很多程式需要用到全域性邏輯變數來標記某個狀態。

比如,在做文件處理,資料庫操作或者是其他可以由使用者儲存修改的程式(也包括影像處理程式)

如果使用者對資料進行了修改,這時退出程式的話,應該給使用者一個提示“是否要先儲存一下呀..."

否則,你的程式100%會被使用者強烈投訴,即使你其他功能做得再好也沒用。

恩,讀者們再動一下“腦白金”吧,如果要你實現這個功能,該如何寫?

 

想好了麼?

我來給你個建議吧:

建立一個全域性變數:

Dim Saved As Boolean

然後在窗體啟動中設定它為True

Sub Form_Load()

...

Saved=True

...

End Sub

為什麼?窗體還剛剛載入,當然不可能是“未儲存”咯。

 

然後在使用者更改內容的事件中,將這個變數設為:Saved=False

 

比如:

Sub Text1_Change()

Saved=False

End Sub

還有其他所有使用者修改資料的地方都加上這句。

 

然後在儲存資料的模組中將這個變數設為True

Sub SaveContent()

...

...

Saved=True '儲存完了,當然就是“已儲存”了咯

End Sub

 

最後呢,只要在窗體被解除安裝的事件中判斷是“未儲存”還是“已儲存”就可以了。

是這麼寫麼?

Sub Form_Unload(Cancel As Integer)

Dim I as Long

If Saved = False then

   I = Msgbox("...要儲存麼...",,VBYESNOCANCEL) '隨手寫的,真有這個引數麼?

   Select Case I

      Case VBYesSaveContent

      Case VBNoEnd

      Case VBCancelCancel=1

   End Select

endif

End sub

 

第二個例子繞了那麼大一個圈子,其實,只是想說:

If Saved = False then 為什麼就不能寫成 If Not Saved Then 呢?

這樣寫不但VB語句優美,而且幾乎連英語語法都要通順了。

這個例子我想說明的其實是邏輯表示式的值並非一定要用=,<,>這些等式符號才能表達出來的。

 

好了,最後來說說我們開頭的那個話題吧。

什麼?你已經忘記了?那太好了,請從頭再看一遍。

由於我對邏輯運算不大感冒,因此雖然覺得那兩個醜陋If...Then...

“或許”可以用一個“優美的”邏輯運算表示式來一次完成,但是一直都沒有仔細的研究和嘗試。

直到我看到“zyl910”的那篇文章,才重新提起了這個念頭來:“哦,原來真的是可以的哦”

仔細看了半天,似乎有點理解了,但是閉上眼睛又忘記了,邏輯運算真是挺搞得哦。

所以就索性拋開文章,直接拿張白紙來推導了。

 

其實,最基本的原理已經在我舉的第一個例子中說明了,即:

True=-1

False=0

既可以參加數學運算又可以參加邏輯運算的超級運動員!

 

由於我的邏輯運算不熟練,我採用的是反推的方法來得到表示式:

先從if Green<0 then Green=0開始。

如果小於0則等於0

大家知道邏輯運算的結果要麼是True要麼是False(-10)

並且True And Anything Anything    '結論1 (And 運算)

  False And Anything False 0 

這裡Anything則是可以做文章的地方了,因為我們最後要得到的是0N2553個分段值。

上面這兩個運算就必定是關鍵,因為當0<N<255時我們需要得到的是N本身的值,而結論1

可以得到這樣的結果,我們一定要牢記。

判斷N<0 這個表示式,當N小於0的時候,N<0=True=-1

N大於等於0的時候,N<0=False=0

代入結論1

(N<0) And N

N小於0的時候

(N<0) And N => True And N => N

N大於0的時候

(N<0) And N => False And N => 0

咦,怎麼反過來了?

那也簡單,把條件改一下吧,反過來判斷N>0

N小於等於0的時候,N>0=False=0

N大於0的時候,    N>0=True=-1

再代入結論1

(N>0) And N

N小於等於0的時候

(N>0) And N => False And N => 0

N大於0的時候

(N>0) And N => True And N => N

正確.完全合乎我們的要求!

等等,別急著開香檳啊,才做了一小半啊,還有一大半沒做呢。

 

接下來我們來分析第二個判斷語句

if Green>255 then Green=255

唉,看到255這個數字,實在是想親上一口。倒不是我有什麼特殊愛好,而是因為255正好是十六進位制裡的FF

而十六進位制的FF,就是二進位制裡的11111111,這是個很奇妙的數字。

任何一個0255範圍內的整數和它進行“與”運算,都會得到原來的數,也就是等於沒有運算。

什麼?你說我在忽悠你?什麼叫做“等於沒有運算”?

別忘記我們要做的事,是把一個分支判斷語句放到一個表示式裡面,

在這裡“等於沒有運算”=“得到原來的值”

首先我想到的是在何種情況下才能用到這個“等於沒有運算”呢?

那就是 Something And 255

N大於255的時候,Something=True

N小於等於255的時候,Something=N

 

這時我又想到了另一個邏輯運算:

True Or Anything True      '結論2 (Or 運算)

False Or Anything Anthing

 

這可真是個好東東哦,想什麼來什麼,上面的那個Something可有著落了。

Something = AnotherThing Or N 就可以了。

對這個AnotherThing的要求是:

N大於255的時候   AnotherThingTrue

N小於等於255的時候     AnotherThingFalse

太簡單啦,直接AnotherThing(N>255)就滿足啦

 

把前面的表示式都列出來看看:

N = Something And 255               '(1)

N大於255的時候, Something=True

N小於等於255的時候,    Something=N

 

Something = AnotherThing Or N             '(2)

N大於255的時候   AnotherThingTrue

N小於等於255的時候     AnotherThingFalse

 

AnotherThing(N>255)               '(3)

N大於255的時候   N>255 = True

N小於255的時候   N>255 = False

 

最後反過來一步一步代入前面的表示式:

(3)代入(2)再代入(1)

得到:

(N>255) Or N And 255

驗算一下:

N大於255的時候:

(N>255) Or N And 255 => True Or N And 255 => True And 255 => 255

N小於等於255的時候:

(N>255) Or N And 255 => False Or N And 255 => N And 255 => N

 

Yeah!!!

==

等等,別開香檳啊,還有事要做呀。

我們只是把兩個條件判斷語句變成了兩個邏輯表示式,還沒有達到最終目的呢。

那就是把兩個表示式整合成一個表示式。

先來看一下目前的成果:

 

if N > 255 then N=255  

  =》N = (N>255) Or N And 255       ‘(1)

if N < 0 then N=0

  =》N = (N>0) And N                ‘(2)

 

似乎有點難度哦,我首先想到的方法是把整個(2)代入(1)中,

N=(((n>0) And N) > 255) Or ((N>0) And N) And 255

整個表示式就變得奇長無比,估計即使成功,執行速度也不會比原來快。

你說化簡?恩,我也很想啊,但是連不等式運算都已經忘記,更不說邏輯表示式了。

還是繼續分析吧。

表示式(1)只考慮兩種情況:  a: N>255      b: N<=255

而表示式(2)只考慮    a: N>0      b: N<=0

因此無論N是否大於0,都是被包含在表示式1的範圍之內的.

這樣的話,只需要將表示式(2)代入一半到表示式(1)中就可以了,

表示式1的括號內的不等式中的那個N不需要代入.

整個表示式變成: (N>255) Or ((N>0) And N) And 255

Or的兩邊可以交換順序,變成這樣:

((N>0) And N) Or (N>255) And 255

去括號: (N>0) And N Or (N>255) And 255

 

測試一下:

N=-1:

(-1>0) And –1 Or (-1>255) And 255

=False And –1 Or False And 255

=False Or False And 255

=False And 255

=False

=0

 

N=10:

(10>0) And 10 Or (10>255) And 255

=True And 10 Or False And 255

=10 Or False And 255

=10 And 255

=10

 

N=266

(266>0) And 266 Or (266>255) And 255

=True And 266 Or True And 255

=266 Or True  And 255

=True And 255

=255

 

哈哈,終於成功了,回頭一看,原來和”zyl910”推匯出來的還是同一個東西.

香檳香檳拿出來!

 

你說我推匯出了一個別人早就弄好的東西為什麼還這麼開心??

廢話,要是自己不這麼來上一遍,那永遠是別人的,你最多隻能把它背出來,它終究不在你心裡.

 

最後,再花幾分鐘寫了一個測試程式,來驗證一下.

再回過頭去看,之前這個東東就是為了要放在影像處理中使用的,因此驗證的時候也應該用一個大陣列來做.

 

最後那個” CombWithVar”按鈕的事件其實也驗證了另外一個想法:用變數代替陣列元素運算確實可以提高速度,前提是在非常大的運算次數下才能顯現出這個差異來.

 

測試程式碼:

Private Declare Function timeGetTime Lib "winmm.dll" () As Long

Dim A(49999999) As Integer

Dim B(49999999) As Integer

Dim T As Long

 

Private Sub Form_Unload(Cancel As Integer)

 

End Sub

 

Private Sub Traditional_Click()     最原始的方法

Dim I As Long

T = timeGetTime

For I = LBound(A) To UBound(A)     

   If A(I) > 255 Then B(I) = 255

   If A(I) < 0 Then A(I) = 0

Next

I = timeGetTime - T

Me.Print "Traditional Compare:" & I

Text1.Text = Text1.Text & Chr(13) & "Traditional Compare:" & I

End Sub

 

Private Sub BooleanExp_Click()      分成2個邏輯運算

Dim I As Long

T = timeGetTime

For I = LBound(A) To UBound(A)

   B(I) = (A(I) > 255) Or A(I) And 255

   B(I) = A(I) And (A(I) > 0)

Next

I = timeGetTime - T

Me.Print "Boolean Expression:" & I

Text1.Text = Text1.Text & Chr(13) & "Boolean Expression:" & I

End Sub

 

Private Sub Combination_Click()     合成一個邏輯運算

Dim I As Long

T = timeGetTime

For I = LBound(A) To UBound(A)

   B(I) = (A(I) > 0 And A(I) Or (A(I) > 255)) And 255

Next

I = timeGetTime - T

Me.Print "Combination Bool-Exp:" & I

Text1.Text = Text1.Text & Chr(13) & "Combination Bool-Exp:" & I

End Sub

 

Private Sub CombWithVar_Click()     使用變數代替陣列元素

Dim I As Long

Dim L As Long

T = timeGetTime

For I = LBound(A) To UBound(A)

   L = A(I)

   B(I) = (L > 0 And L Or (L > 255)) And 255

Next

I = timeGetTime - T

Me.Print "Com_Bool_Exp_Var:" & I

Text1.Text = Text1.Text & Chr(13) & "Com_Bool_Exp_Var:" & I

End Sub

 

Private Sub Form_Load()             初始化陣列

Dim I As Long

For I = 0 To 1000

   A(I) = Rnd * I / 2

Next

Me.Print "運算五千萬次計時"

Text1.Text = "運算五千萬次計時"

End Sub

<?xml:namespace prefix = v /> 

運算五千萬次計時

Traditional Compare:577

Traditional Compare:579

Traditional Compare:536

Traditional Compare:578

Traditional Compare:534

Traditional Compare:553

Traditional Compare:577

Traditional Compare:530

Traditional Compare:530

Traditional Compare:530

Boolean Expression:926

Boolean Expression:839

Boolean Expression:907

Boolean Expression:929

Boolean Expression:809

Boolean Expression:913

Boolean Expression:927

Boolean Expression:877

Boolean Expression:912

Boolean Expression:786

Combination Bool-Exp:593

Combination Bool-Exp:420

Combination Bool-Exp:426

Combination Bool-Exp:433

Combination Bool-Exp:467

Combination Bool-Exp:421

Combination Bool-Exp:415

Combination Bool-Exp:459

Combination Bool-Exp:422

Combination Bool-Exp:428

Com_Bool_Exp_Var:518

Com_Bool_Exp_Var:326

Com_Bool_Exp_Var:320

Com_Bool_Exp_Var:353

Com_Bool_Exp_Var:320

Com_Bool_Exp_Var:343

Com_Bool_Exp_Var:346

Com_Bool_Exp_Var:376

Com_Bool_Exp_Var:351

Com_Bool_Exp_Var:334

 

相關文章