窗体中有三个按钮一个图片框,图片框中请在设计的时候加载一副图像及设置相关参数。
窗体代码:
Option Explicit
Private m_Width As Long '打开的图像的宽度 Private m_Height As Long '打开的图像的高度
Private Sub Form_Load() m_Width = Pic.ScaleWidth m_Height = Pic.ScaleHeight End Sub
Private Sub CmdOpen_Click() CommonDialog.FileName = "" CommonDialog.Filter = "All Suppported Images |*.bmp;*.jpg;*.gif;|BMP Images|*.bmp|JPG Images|*.jpg|Gif Images|*.gif" CommonDialog.ShowOpen If CommonDialog.FileName <> "" Then Pic.Picture = LoadPicture(CommonDialog.FileName) m_Width = Pic.ScaleWidth m_Height = Pic.ScaleHeight End If End Sub
Private Sub CmdDib_Click() Dim T As Long Dim PicData() As RGBQUAD Dim i As Long, j As Long
Dim HistRed(255) As Long, HistGreen(255) As Long Dim HistBlue(255) As Long Dim DiffRed As Long, DiffGreen As Long Dim DiffBlue As Long, Diff As Long Dim SpeedRed(255) As Byte, SpeedGreen(255) As Byte Dim SpeedBlue(255) As Byte, Speed(255) As Byte Dim Sum As Long, Integral As Long Dim Min As Long, Max As Long Dim NewMin As Long, NewMax As Long T = GetTickCount GetPicData Pic, PicData For i = 0 To m_Width - 1 For j = 0 To m_Height - 1 HistRed(PicData(i, j).Red) = HistRed(PicData(i, j).Red) + 1 HistGreen(PicData(i, j).Green) = HistGreen(PicData(i, j).Green) + 1 HistBlue(PicData(i, j).Blue) = HistBlue(PicData(i, j).Blue) + 1 Next Next For i = 0 To 255 If HistRed(i) <> 0 Then Min = i Exit For End If Next For i = 255 To 0 Step -1 If HistRed(i) <> 0 Then Max = i Exit For End If Next Sum = 0 For i = Min To Max Sum = Sum + HistRed(i) Next Integral = 0 For i = Min To Max Integral = Integral + HistRed(i) If Integral >= Sum * 0.005 Then NewMin = i Exit For End If Next For i = NewMin + 1 To Max Integral = Integral + HistRed(i) If Integral > Sum * 0.995 Then NewMax = i Exit For End If Next For i = 0 To 255 If i <= NewMin Then SpeedRed(i) = 0 ElseIf i >= NewMax Then SpeedRed(i) = 255 Else SpeedRed(i) = (i - NewMin) / (NewMax - NewMin) * 255 End If Next
'''''''''''''''''''''''''''' For i = 0 To 255 If HistGreen(i) <> 0 Then Min = i Exit For End If Next For i = 255 To 0 Step -1 If HistGreen(i) <> 0 Then Max = i Exit For End If Next Sum = 0 For i = Min To Max Sum = Sum + HistGreen(i) Next Integral = 0 For i = Min To Max Integral = Integral + HistGreen(i) If Integral >= Sum * 0.005 Then NewMin = i Exit For End If Next For i = NewMin + 1 To Max Integral = Integral + HistGreen(i) If Integral > Sum * 0.995 Then NewMax = i Exit For End If Next
For i = 0 To 255 If i <= NewMin Then SpeedGreen(i) = 0 ElseIf i > NewMax Then SpeedGreen(i) = 255 Else SpeedGreen(i) = (i - NewMin) / (NewMax - NewMin) * 255 End If Next
'''''''''''''''''''''''''
For i = 0 To 255 If HistBlue(i) <> 0 Then Min = i Exit For End If Next For i = 255 To 0 Step -1 If HistBlue(i) <> 0 Then Max = i Exit For End If Next Sum = 0 For i = Min To Max Sum = Sum + HistBlue(i) Next Integral = 0 For i = Min To Max Integral = Integral + HistBlue(i) If Integral >= Sum * 0.005 Then NewMin = i Exit For End If Next For i = NewMin + 1 To Max Integral = Integral + HistBlue(i) If Integral > Sum * 0.995 Then NewMax = i Exit For End If Next
For i = 0 To 255 If i <= NewMin Then SpeedBlue(i) = 0 ElseIf i > NewMax Then SpeedBlue(i) = 255 Else SpeedBlue(i) = (i - NewMin) / (NewMax - NewMin) * 255 End If Next
For i = 0 To m_Width - 1 For j = 0 To m_Height - 1 PicData(i, j).Red = SpeedRed(PicData(i, j).Red) PicData(i, j).Green = SpeedGreen(PicData(i, j).Green) PicData(i, j).Blue = SpeedBlue(PicData(i, j).Blue) Next Next SetPicData Pic, PicData Me.Caption = "DIB方法用时" & GetTickCount - T & "毫秒" End Sub
好了,任务完成了,编译测试一下,勾选上所有的高级优化选项,测试图片大小选为1024*768,结果显示需要300ms,啊,如此慢,PS中一点就出来了效果呢,仔细分析下代码结构,似乎能够优化的地方已经基本优化了,那问题主要是那里呢。 其实,在这里耗时的几个主要过程有,读取图像数据、计算直方图、计算新的图像数据、显示图像数据,其中又以中间两个过程为主。我们采用了二维结构体数组,众所周知,访问数组需要计算数组元素在内存中位置,而维数越高,计算地址的公式就越复杂,因此,如果能采用一维数组的方式理论上是要比二维的速度有所改进的,这部分测试可以用读者自己完成。 这里要说的是另外一种提速的方法,模拟指针。其实这个词有很多人应该见过很多次了。虽然VB不直接支持指针的操作,但是借助于安全数据的相关特性,我们可以实现类似于C的指针(包括功能和代码的编辑方式).那么这里我们用模拟指针的好处很明显。第一:我们不需要调用GetDIBits这个函数,这意味着两种节省,其一是空间上的,GetDIBits函数需要你为他事先分配好一定的内存空间来保存图像数据。但是实际上,图像已经被我们加载入picturebox中,那么在内存中他肯定已经占用了空间,我们可以利用GetGDIObject这个函数配以适当的参数得到这个内存空间的首地址。其二是时间上的,分配空间和填充数据都是耗时的,虽然这个时间很少。第二,可以减少寻址的时间消耗, 用模拟指针的过程也需要寻址。但是适当的利用中间变量保存相关地址,可以节省大量的计算时间,比如你用二维结构体保存图象数据,要得到图象在(i,j)点的红色和兰色和绿色数据,则要调用Data(i,j).Red和Data(i,j).Blue、Data(i,j).Green,这里三次调用则三次寻址,每次寻址的过程类似于下面的公式 j*width+i+Offset,一次乘法,二次加法,如果我们自己寻址,则用临时变量保存j*width+i值,则会减少计算量。对于大循环则更是明显。
好了说了这么多,下面给出代码。
'***************************自动色阶的模拟指针实现********************************* '** 作者 : laviewpbt '** 开发时间 : 2008.7.1 '** 最后修改时间 : 2008.8.28 '** 联系方式 : QQ:33184777 '** E-MAIL : laviewpbt@sina.com '** Blog : http://blog.csdn.net/laviewpbt/ '** All Rights Resered,转载请保留以上信息
'***********************************************************************
Private Sub CmdPointer_Click() Dim i As Long, j As Long Dim DataArr(0 To 3) As Byte, pDataArr(0 To 0) As Long Dim OldArrPtr As Long, OldpArrPtr As Long Dim LineAddBytes As Long, PixelAddBytes As Long Dim Bmp As Bitmap, T As Long Dim HistRed(255) As Long, HistGreen(255) As Long Dim HistBlue(255) As Long Dim DiffRed As Long, DiffGreen As Long Dim DiffBlue As Long, Diff As Long Dim SpeedRed(255) As Byte, SpeedGreen(255) As Byte Dim SpeedBlue(255) As Byte, Speed(255) As Byte Dim Sum As Long, Integral As Long Dim Min As Long, Max As Long Dim NewMin As Long, NewMax As Long
T = GetTickCount GetGDIObject Pic.Picture.Handle, Len(Bmp), Bmp If Bmp.bmBits <> 0 Then '是个有效的图片 If Bmp.bmBitsPixel < 24 Then Exit Sub '不处理费真彩色图像,实际上,VB的picture属性也支持8位索引色的Bmp,如果你为了节省内存,采用改格式的图片,可以自行修改代码。 MakePoint VarPtrArray(DataArr), VarPtrArray(pDataArr), OldArrPtr, OldpArrPtr PixelAddBytes = Bmp.bmBitsPixel / 8 '可为3,可为4 pDataArr(0) = Bmp.bmBits '首地址 LineAddBytes = Bmp.bmWidthBytes - (Bmp.bmBitsPixel \ 8) * Bmp.bmWidth '每个扫描行额外多出的字节 For j = 1 To m_Height For i = 1 To m_Width HistRed(DataArr(2)) = HistRed(DataArr(2)) + 1 HistGreen(DataArr(1)) = HistGreen(DataArr(1)) + 1 HistBlue(DataArr(0)) = HistBlue(DataArr(0)) + 1 pDataArr(0) = pDataArr(0) + PixelAddBytes Next pDataArr(0) = pDataArr(0) + LineAddBytes Next For i = 0 To 255 If HistRed(i) <> 0 Then Min = i Exit For End If Next For i = 255 To 0 Step -1 If HistRed(i) <> 0 Then Max = i Exit For End If Next Sum = 0 For i = Min To Max Sum = Sum + HistRed(i) Next Integral = 0 For i = Min To Max Integral = Integral + HistRed(i) If Integral >= Sum * 0.005 Then NewMin = i Exit For End If Next For i = NewMin + 1 To Max Integral = Integral + HistRed(i) If Integral > Sum * 0.995 Then NewMax = i Exit For End If Next For i = 0 To 255 If i <= NewMin Then SpeedRed(i) = 0 ElseIf i >= NewMax Then SpeedRed(i) = 255 Else SpeedRed(i) = (i - NewMin) / (NewMax - NewMin) * 255 End If Next '''''''''''''''''''''''''''' For i = 0 To 255 If HistGreen(i) <> 0 Then Min = i Exit For End If Next For i = 255 To 0 Step -1 If HistGreen(i) <> 0 Then Max = i Exit For End If Next Sum = 0 For i = Min To Max Sum = Sum + HistGreen(i) Next Integral = 0 For i = Min To Max Integral = Integral + HistGreen(i) If Integral >= Sum * 0.005 Then NewMin = i Exit For End If Next For i = NewMin + 1 To Max Integral = Integral + HistGreen(i) If Integral > Sum * 0.995 Then NewMax = i Exit For End If Next For i = 0 To 255 If i <= NewMin Then SpeedGreen(i) = 0 ElseIf i > NewMax Then SpeedGreen(i) = 255 Else SpeedGreen(i) = (i - NewMin) / (NewMax - NewMin) * 255 End If Next
'''''''''''''''''''''''''
For i = 0 To 255 If HistBlue(i) <> 0 Then Min = i Exit For End If Next For i = 255 To 0 Step -1 If HistBlue(i) <> 0 Then Max = i Exit For End If Next Sum = 0 For i = Min To Max Sum = Sum + HistBlue(i) Next Integral = 0 For i = Min To Max Integral = Integral + HistBlue(i) If Integral >= Sum * 0.005 Then NewMin = i Exit For End If Next For i = NewMin + 1 To Max Integral = Integral + HistBlue(i) If Integral > Sum * 0.995 Then NewMax = i Exit For End If Next For i = 0 To 255 If i <= NewMin Then SpeedBlue(i) = 0 ElseIf i > NewMax Then SpeedBlue(i) = 255 Else SpeedBlue(i) = (i - NewMin) / (NewMax - NewMin) * 255 End If Next pDataArr(0) = Bmp.bmBits For j = 1 To m_Height For i = 1 To m_Width DataArr(2) = SpeedRed(DataArr(2)) DataArr(1) = SpeedGreen(DataArr(1)) DataArr(0) = SpeedBlue(DataArr(0)) pDataArr(0) = pDataArr(0) + PixelAddBytes Next pDataArr(0) = pDataArr(0) + LineAddBytes Next FreePoint VarPtrArray(DataArr), VarPtrArray(pDataArr), OldArrPtr, OldpArrPtr Pic.Refresh End If Me.Caption = "模拟指针用时" & GetTickCount - T & "毫秒"
End Sub
Private Sub MakePoint(ByVal DataArrPtr As Long, ByVal pDataArrPtr As Long, ByRef OldArrPtr As Long, ByRef OldpArrPtr As Long) Dim Temp As Long, TempPtr As Long CopyMemory Temp, ByVal DataArrPtr, 4 '得到DataArrPtr的SAFEARRAY结构的地址 Temp = Temp + 12 '这个指针偏移12个字节后就是pvData指针 CopyMemory TempPtr, ByVal pDataArrPtr, 4 '得到pDataArrPtr的SAFEARRAY结构的地址 TempPtr = TempPtr + 12 '这个指针偏移12个字节后就是pvData指针 CopyMemory OldpArrPtr, ByVal TempPtr, 4 '保存旧地址 CopyMemory ByVal TempPtr, Temp, 4 '使pDataArrPtr指向DataArrPtr的SAFEARRAY结构的pvData指针 CopyMemory OldArrPtr, ByVal Temp, 4 '保存旧地址 End Sub
Private Sub FreePoint(ByVal DataArrPtr As Long, ByVal pDataArrPtr As Long, ByVal OldArrPtr As Long, ByVal OldpArrPtr As Long) Dim TempPtr As Long CopyMemory TempPtr, ByVal DataArrPtr, 4 '得到DataArrPtr的SAFEARRAY结构的地址 CopyMemory ByVal (TempPtr + 12), OldArrPtr, 4 '恢复旧地址 CopyMemory TempPtr, ByVal pDataArrPtr, 4 '得到pDataArrPtr的SAFEARRAY结构的地址 CopyMemory ByVal (TempPtr + 12), OldpArrPtr, 4 '恢复旧地址 End Sub
编译后测试,同样1024*768大小的图片,用模拟指针方法只需32ms左右,这个时间人是基本看不到延迟的。用VC的话也就在这个时间范围内。
怎么样,对VB的信心是不是又增加了不少,是的,VB一直是很优秀的。以前是,现在是,将来也是。
示例工程可以倒这里下载:http://files.cnblogs.com/laviewpbt/AutoLevel.rar.
示例工程中有部分代码和本文中给出的不一致,但这不影响大局。
(责任编辑:admin) |