图像处理或其他多媒体处理中的值溢出处理

在网上查资料的时候,无意中连接到了“zyl910”的专栏,

看了里面的一篇文章,感觉颇有收获,就在此抛砖引玉一番。

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

 

在用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

 

运算五千万次计时

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

 
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值