From: Tom Shelton on 1 Oct 2009 10:09 ["Followup-To:" header set to microsoft.public.vb.general.discussion.] On 2009-10-01, Mike Williams <Mike(a)WhiskyAndCoke.com> wrote: > "Tom Shelton" <tom_shelton(a)comcastXXXXXXX.net> wrote in message > news:uM481OhQKHA.4692(a)TK2MSFTNGP06.phx.gbl... > >> Well... lol. i just did it a few minutes ago - but I did >> it in C#. I used the Creek.jpg from my Pictures/Sample >> Pictures folder. Also, it is a console app. So here is >> the code: > > Okay. C# code wasn't the challenge though, so let me know when you've done > it in VB.Net using a standard 24 bit .bmp file as stated in my original > post. > > Mike Fine... Here is the code - still using creek.jpg. Send me the 24-bit map you would like to use and i'll use it. I can't find one that size and resolution on my system. Only jpg's - but, from my perspective it doesn't matter the code is the same. Option Explicit On Option Strict On Imports System Imports System.Drawing Imports System.Drawing.Imaging Imports System.Runtime.InteropServices Module Module1 Sub Main() Dim iterations As Integer = 10 Dim totalTime As Integer = 0 For i As Integer = 1 To iterations totalTime += CountColors() Next Console.WriteLine("Average Time: {0}", New TimeSpan(0, 0, 0, 0, CInt(totalTime / iterations))) End Sub Function CountColors() As Integer Dim colors As New Dictionary(Of Integer, Integer) Dim startTime As Integer = Environment.TickCount Using bmp As New Bitmap("creek.jpg") Dim bmd As BitmapData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat) Dim byteCount As Integer = bmd.Stride * bmp.Height Dim bytes(byteCount - 1) As Byte Marshal.Copy(bmd.Scan0, bytes, 0, byteCount) bmp.UnlockBits(bmd) For i As Integer = 0 To byteCount - 1 Step 3 Dim color As Integer = BitConverter.ToInt32(New Byte() {bytes(i), bytes(i + 1), bytes(i + 2), 0}, 0) If Not colors.ContainsKey(color) Then colors.Add(color, color) End If Next End Using Dim total As Integer = Environment.TickCount - startTime Console.WriteLine("Counted {0} unique colors in {1}", colors.Keys.Count, New TimeSpan(0, 0, 0, 0, total)) Return total End Function End Module Output: Counted 131763 unique colors in 00:00:00.1410000 Counted 131763 unique colors in 00:00:00.1250000 Counted 131763 unique colors in 00:00:00.1090000 Counted 131763 unique colors in 00:00:00.1100000 Counted 131763 unique colors in 00:00:00.1090000 Counted 131763 unique colors in 00:00:00.1090000 Counted 131763 unique colors in 00:00:00.1100000 Counted 131763 unique colors in 00:00:00.1090000 Counted 131763 unique colors in 00:00:00.1560000 Counted 131763 unique colors in 00:00:00.1100000 Average Time: 00:00:00.1190000 Press any key to continue . . . -- Tom Shelton
From: Mike Williams on 1 Oct 2009 14:57 "Tom Shelton" <tom_shelton(a)comcastXXXXXXX.net> wrote in message news:u9KwLvpQKHA.1372(a)TK2MSFTNGP02.phx.gbl... > Fine... Here is the code - still using creek.jpg. Send me the > 24-bit map you would like to use and i'll use it. I can't find > one that size and resolution on my system. Only jpg's - but, > from my perspective it doesn't matter the code is the same. [That was odd, the message I just posted seems to have gone astray from my sent items. Here it is again just in case] It doesn't need to be any specific size, I just suggested 1024 x 768 as an example. If you wish to do so then you can load your jpg into MS Paint and save it as a full colour 24 bit .bmp file. One of the reasons I specified a ..bmp file is the fact that the standard native VB6 methods of loading a jpg will affect the colours of the resultant bitmap if loaded on a machine that happens to be running at a lower than full colour depth (16 bits or whatever) and so in VB6 if you want to avoid that it would be necessary to add code to load a jpg in a different way, something for which I usally use the Intel jpg library. The thing is (referring back to the other discussion about the mandelbrot) it seems that something about my machine is causing your own VB.Net code to run at the same speed, whether I run your completely unmodified mandelbrot.exe file in your Release folder or whether I load the project into VB Express and run it in Debug mode. In both cases when run full screen I get about 9 to 10 seconds, which according to what you have said appears to mean that there is something odd about the way my machine runs your own mandelbrot.exe file in your Bin / Release folder. Therefore, I am not sure that I will be seeing the correct timing from your count the colours VB.Net code on this machine. Anyway, for what it's worth, the time I'm getting from your own VB.Net code (the code you just posted) when using it to count the colors in a 1024 x 768 pixel bmp file is very similar to your own reported timings at your end using your creek.jpg image file. The timings for your VB.Net code on my machine for the 1024x768 pixel bmp file I am using varies between 0.094 and 0.109 seconds. When I run my own VB6 code which is capable of dealing with either bmps or jpgs and which uses the relatively slow native VB method of loading a jpg I get about 0.062 to 0.064 to count the colours in a 1024 x 768 pixel jpg (although that can be improved considerably by using the Intel jpg library or somehting similar). If I instead use my VB6 code which is currently optimised for dealing with bmp files (completely different code to the bmp or jpg code I've just mentioned) I get between 0.014 and 0.016 to count the colours in a 1024 x 768 pixel bmp. This is very fast, but it would of course be slower when the code to deal with a jpg is added (typically I would use the Intel jpg library which would add probably something in the region of about a further 0.015 seconds). Anyway, perhaps it might be best if you tested both your own VB.Net code and my VB6 code on your own machine so that you can check out the timings for yourself. The code below is my optimised for bmp files code (the one producing a time of between 0.014 and 0.016 seconds on my own machine to count the colours in a 1024 x 768 pixel .bmp file) so until I get around to adding some fast jpg functionality you will need to run it on a .bmp file. It would be interesting to see what timings you get at your end. You will of course need to run it as a native code compiled exe because whereas you can do the job in about a dozen or so lines of code in VB6 I have optimised it by using masses of extra code to give me more flexible data handling, which improves the speed as a compiled exe but which completely destroys the speed when run in the IDE (because of all the dozens of extra lines of code, which are not a problem when compiled but which slow down the code in the IDE). I make no apologies for the rambling state of the code, because I wrote it fairly quickly for this test and I concentrated totally on speed of execution at the expense of readworthy or small or neat code. It actually looks a complete mess at the moment, and there are dozens and dozens of lines of code (maybe hundreds!) that would not otherwise be needed if I hadn't wanted to optimise it to get the sort of data pointers I wanted, and various parts which use a number of almost identical small blocks instead of calls to a suitable function for similar reasons. As I've said, it is an absolute mess, but it works okay ;-) This is my VB6 code that is currently giving me a time of between 0.014 and 0.016 seconds to count the colours on a 1024 x 768 pixel .bmp file. It would be interesting to see what timings it returns your own machine, and the size of image file you are using. If you don't want to mess about loading a jpg into MS Paint and saving it as a 24 bit .bmp file then let me know and I'll send you the .bmp file I'm using myself. Anyway, paste the code into a new VB6 project (one Form containing a Command Button) and compile to a native code exe. Mike Option Explicit Private Declare Function LoadImage Lib "user32" Alias _ "LoadImageA" (ByVal hInst As Long, ByVal lpsz As String, _ ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, _ ByVal un2 As Long) As Long Private Declare Function GetObject Lib "gdi32" _ Alias "GetObjectA" (ByVal hObject As Long, _ ByVal nCount As Long, lpObject As Any) As Long Private Declare Function DeleteObject Lib "gdi32" _ (ByVal hObject As Long) As Long Private Declare Function VarPtrArray Lib "msvbvm60.dll" _ Alias "VarPtr" (Ptr() As Any) As Long Private Declare Sub CopyMemory Lib "kernel32" _ Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, _ ByVal ByteLen As Long) Private Const IMAGE_BITMAP = 0 Private Const LR_LOADFROMFILE = &H10 Private Const LR_CREATEDIBSECTION As Long = &H2000 Private Const LR_VGACOLOR As Long = &H80 Private Type SAFEARRAY1D cDims As Integer fFeatures As Integer cbElements As Long cLocks As Long pvData As Long cElements As Long lLbound As Long End Type Private Type BITMAP bmType As Long bmWidth As Long bmHeight As Long bmWidthBytes As Long bmPlanes As Integer bmBitsPixel As Integer bmBits As Long End Type Private SourceWidth As Long, SourceHeight As Long Private clrs(0 To 2 ^ 21 - 1) As Byte Private TwoToThePowerOf(0 To 7) As Byte Private Declare Function timeGetTime _ Lib "winmm.dll" () As Long Private Declare Function timeBeginPeriod _ Lib "winmm.dll" (ByVal uPeriod As Long) As Long Private Declare Function timeEndPeriod _ Lib "winmm.dll" (ByVal uPeriod As Long) As Long Private Sub Form_Load() timeBeginPeriod 1 Dim n As Long For n = 0 To 7 TwoToThePowerOf(n) = 2 ^ n Next n End Sub Private Function CountColours(bmpFile As String) As Long Dim t1 As Long, t2 As Long, n As Long Dim j As Long, p As Long, used As Long Dim allclrsByte As Long, bitmask As Byte Dim myDIB As Long, bmpInf As BITMAP Dim sa1 As SAFEARRAY1D, sa2 As SAFEARRAY1D Dim sa3 As SAFEARRAY1D, sa4 As SAFEARRAY1D Dim sa5 As SAFEARRAY1D, bytePos As Long Dim SourceArray1() As Long, SourceArray2() As Long Dim SourceArray3() As Long, SourceArray4() As Long Dim sourcearray5() As Byte, done As Boolean Dim z As Byte, scanline As Long, LastLine As Boolean Dim bmpLongs As Long, oddPixels As Long, scanLongs As Long Dim twelves As Long Dim fours As Long, offset As Long myDIB = LoadImage(App.hInstance, bmpFile, _ IMAGE_BITMAP, 0, 0, _ LR_LOADFROMFILE Or LR_CREATEDIBSECTION) GetObject myDIB, Len(bmpInf), bmpInf If myDIB = 0 Or bmpInf.bmBitsPixel <> 24 Then MsgBox "Not a valid full colour 24 bit .bmp file" Exit Function End If Erase clrs() GetObject myDIB, Len(bmpInf), bmpInf SourceWidth = bmpInf.bmWidth SourceHeight = bmpInf.bmHeight bmpLongs = (bmpInf.bmWidthBytes \ 4) * SourceHeight twelves = ((SourceWidth * 3) \ 12) fours = twelves * 3 scanLongs = bmpInf.bmWidthBytes \ 4 oddPixels = SourceWidth - twelves * 4 On Error GoTo finish With sa1 .cDims = 1 .cbElements = 4 .lLbound = 0 .cElements = bmpLongs .pvData = bmpInf.bmBits End With LSet sa2 = sa1: sa2.pvData = sa1.pvData + 3 LSet sa3 = sa1: sa3.pvData = sa1.pvData + 6 LSet sa4 = sa1: sa4.pvData = sa1.pvData + 9 CopyMemory ByVal VarPtrArray(SourceArray1), VarPtr(sa1), 4 CopyMemory ByVal VarPtrArray(SourceArray2), VarPtr(sa2), 4 CopyMemory ByVal VarPtrArray(SourceArray3), VarPtr(sa3), 4 CopyMemory ByVal VarPtrArray(SourceArray4), VarPtr(sa4), 4 LSet sa5 = sa1: sa5.cbElements = 1: sa5.cElements = bmpLongs * 4 CopyMemory ByVal VarPtrArray(sourcearray5), VarPtr(sa5), 4 For scanline = 0 To SourceHeight - 1 If scanline = (SourceHeight - 1) Then LastLine = True End If For j = 0 To fours - 3 Step 3 p = SourceArray1(j + offset) And &HFFFFFF allclrsByte = p \ 8 bitmask = TwoToThePowerOf(7 - p Mod 8) z = clrs(allclrsByte) If (z And bitmask) = 0 Then used = used + 1 clrs(allclrsByte) = z Or bitmask End If p = SourceArray2(j + offset) And &HFFFFFF allclrsByte = p \ 8 bitmask = TwoToThePowerOf(7 - p Mod 8) z = clrs(allclrsByte) If (z And bitmask) = 0 Then used = used + 1 clrs(allclrsByte) = z Or bitmask End If p = SourceArray3(j + offset) And &HFFFFFF allclrsByte = p \ 8 bitmask = TwoToThePowerOf(7 - p Mod 8) z = clrs(allclrsByte) If (z And bitmask) = 0 Then used = used + 1 clrs(allclrsByte) = z Or bitmask End If If LastLine = True And j = (fours - 3) Then bytePos = (j + offset) * 4 + 9 p = &H10000 * sourcearray5(bytePos + 2) _ + &H100& * sourcearray5(bytePos + 1) _ + sourcearray5(bytePos) allclrsByte = p \ 8 bitmask = TwoToThePowerOf(7 - p Mod 8) z = clrs(allclrsByte) If (z And bitmask) = 0 Then used = used + 1 clrs(allclrsByte) = z Or bitmask End If Else p = SourceArray4(j + offset) And &HFFFFFF allclrsByte = p \ 8 bitmask = TwoToThePowerOf(7 - p Mod 8) z = clrs(allclrsByte) If (z And bitmask) = 0 Then used = used + 1 clrs(allclrsByte) = z Or bitmask End If End If If oddPixels > 0 Then bytePos = (j + offset + 3) * 4 For n = 0 To oddPixels - 1 p = &H10000 * sourcearray5(bytePos + 2) _ + &H100& * sourcearray5(bytePos + 1) _ + sourcearray5(bytePos) allclrsByte = p \ 8 bitmask = TwoToThePowerOf(7 - p Mod 8) z = clrs(allclrsByte) If (z And bitmask) = 0 Then used = used + 1 clrs(allclrsByte) = z Or bitmask End If bytePos = bytePos + 3 Next n End If Next j offset = offset + scanLongs Next scanline CountColours = used: done = True finish: CopyMemory ByVal VarPtrArray(SourceArray1), 0&, 4 CopyMemory ByVal VarPtrArray(SourceArray2), 0&, 4 CopyMemory ByVal VarPtrArray(SourceArray3), 0&, 4 CopyMemory ByVal VarPtrArray(SourceArray4), 0&, 4 CopyMemory ByVal VarPtrArray(sourcearray5), 0&, 4 DeleteObject myDIB If Not done Then CountColours = 0 End Function Private Sub Command1_Click() Dim tStart As Long, tFinish As Long Dim s1 As String, clrs As Long tStart = timeGetTime clrs = CountColours("c:\temp\1024x768.bmp") tFinish = timeGetTime If clrs > 0 Then s1 = Format(SourceWidth) & " x " & Format(SourceHeight) _ & " pixel bmp file containing " & Format(clrs) _ & " unique colours (" & Format _ ((tFinish - tStart) / 1000, "0.000") & " seconds)" MsgBox s1 Else MsgBox "Error dealing with bmp file" End If End Sub ' *** I told you it was a mess! ***
From: Tom Shelton on 1 Oct 2009 16:12 ["Followup-To:" header set to microsoft.public.vb.general.discussion.] On 2009-10-01, Mike Williams <Mike(a)WhiskyAndCoke.com> wrote: > "Tom Shelton" <tom_shelton(a)comcastXXXXXXX.net> wrote in message > news:u9KwLvpQKHA.1372(a)TK2MSFTNGP02.phx.gbl... > >> Fine... Here is the code - still using creek.jpg. Send me the >> 24-bit map you would like to use and i'll use it. I can't find >> one that size and resolution on my system. Only jpg's - but, >> from my perspective it doesn't matter the code is the same. > > [That was odd, the message I just posted seems to have gone astray from my > sent items. Here it is again just in case] > > It doesn't need to be any specific size, I just suggested 1024 x 768 as an > example. If you wish to do so then you can load your jpg into MS Paint and > save it as a full colour 24 bit .bmp file. One of the reasons I specified a > .bmp file is the fact that the standard native VB6 methods of loading a jpg > will affect the colours of the resultant bitmap if loaded on a machine that > happens to be running at a lower than full colour depth (16 bits or > whatever) and so in VB6 if you want to avoid that it would be necessary to > add code to load a jpg in a different way, something for which I usally use > the Intel jpg library. The thing is (referring back to the other discussion > about the mandelbrot) it seems that something about my machine is causing > your own VB.Net code to run at the same speed, whether I run your completely > unmodified mandelbrot.exe file in your Release folder or whether I load the > project into VB Express and run it in Debug mode. In both cases when run > full screen I get about 9 to 10 seconds, which according to what you have > said appears to mean that there is something odd about the way my machine > runs your own mandelbrot.exe file in your Bin / Release folder. Therefore, I > am not sure that I will be seeing the correct timing from your count the > colours VB.Net code on this machine. Anyway, for what it's worth, the time > I'm getting from your own VB.Net code (the code you just posted) when using > it to count the colors in a 1024 x 768 pixel bmp file is very similar to > your own reported timings at your end using your creek.jpg image file. The > timings for your VB.Net code on my machine for the 1024x768 pixel bmp file I > am using varies between 0.094 and 0.109 seconds. > > When I run my own VB6 code which is capable of dealing with either bmps or > jpgs and which uses the relatively slow native VB method of loading a jpg I > get about 0.062 to 0.064 to count the colours in a 1024 x 768 pixel jpg Changing it to an actual bitmap (not sure why I didn't think of using paint to convert it - duh!) moves the timing to around .07 - .083. So, the difference must be the jpg encoder overhead - though, it didn't change the code. And the C# version is actually a tad faster, running around .068 - ..078. So, what do I conclude - that for a lot less and easier code... That I'm loosing fractions of a second in performance - not even enough for anyone to ever notice. I think I'm ok with that. It lets me spend more time with the creative parts of my apps... If you want me to run your app, you'll need to email me the binary - my only box with vb6 installed is dead, and I'm not planning to install it on this one. -- Tom Shelton
From: Mike Williams on 1 Oct 2009 17:46 "Tom Shelton" <tom_shelton(a)comcastXXXXXXX.net> wrote in message news:OIIX2NtQKHA.5032(a)TK2MSFTNGP05.phx.gbl... > Changing it to an actual bitmap (not sure why I didn't think of > using paint to convert it - duh!) moves the timing to around > .07 - .083. So, the difference must be the jpg encoder > overhead - though, it didn't change the code. And the C# > version is actually a tad faster, running around .068 -.078. > So, what do I conclude - that for a lot less and easier code... > That I'm loosing fractions of a second in performance - not > even enough for anyone to ever notice. Don't forget that the .062 time I mentioned for VB6 to count the number of unique colours was for a .jpg loaded using the relatively slow LoadPicture method, whereas the VB6 code I posted (which currently deals only with .bmps and which does not use that method) takes just .014 to .016 seconds (against the .070 to .083 you report for your VB.Net code when that too is working with a .bmp), so the VB6 code is considerably faster than the VB.Net code. It will take a bit longer when proper jpg handling is added (about 0.015 seconds extra), but that is still considerably faster than the VB.Net code. Not that it really matters of course, it's not that important and I wouldn't have bothered were it not for the that I wanted to show that Cor Ligthert was massively exaggerating when he said that VB.Net code runs very much faster than VB6 code, whereas in this specific case it is the other way round. > I think I'm ok with that. It lets me spend more time with the > creative parts of my apps...If you want me to run your app, > you'll need to email me the binary - my only box with vb6 > installed is dead, and I'm not planning to install it on this one. Okay. I'll send you a compiled exe via email (I don't have a suitable web page to host it at the moment). Mike
From: Tom Shelton on 1 Oct 2009 18:06
On 2009-10-01, Mike Williams <Mike(a)WhiskyAndCoke.com> wrote: > "Tom Shelton" <tom_shelton(a)comcastXXXXXXX.net> wrote in message > news:OIIX2NtQKHA.5032(a)TK2MSFTNGP05.phx.gbl... > >> Changing it to an actual bitmap (not sure why I didn't think of >> using paint to convert it - duh!) moves the timing to around >> .07 - .083. So, the difference must be the jpg encoder >> overhead - though, it didn't change the code. And the C# >> version is actually a tad faster, running around .068 -.078. >> So, what do I conclude - that for a lot less and easier code... >> That I'm loosing fractions of a second in performance - not >> even enough for anyone to ever notice. > > Don't forget that the .062 time I mentioned for VB6 to count the number of > unique colours was for a .jpg loaded using the relatively slow LoadPicture > method, whereas the VB6 code I posted (which currently deals only with .bmps > and which does not use that method) takes just .014 to .016 seconds (against > the .070 to .083 you report for your VB.Net code when that too is working > with a .bmp), so the VB6 code is considerably faster than the VB.Net code. > It will take a bit longer when proper jpg handling is added (about 0.015 > seconds extra), but that is still considerably faster than the VB.Net code. > Not that it really matters of course, it's not that important and I wouldn't > have bothered were it not for the that I wanted to show that Cor Ligthert > was massively exaggerating when he said that VB.Net code runs very much > faster than VB6 code, whereas in this specific case it is the other way > round. > Well, Mike - I'm certainly no expert on this stuff. There may be much, much better ways to accomplish this task. But, again, the preformance difference is in factions of a second. Not even worth talking about outside of news groups. And, Mike, mine already handles jpg. I don't even have to change code. It would be trivial really to just read the bitmap data directly from the bitmap file, rather then use the bitmap class, etc - but the bitmap class can handle bmp, gif, png, etc, etc. Like with most things - if speed is unacceptable, then I find the bottle neck and then see if there is a faster way to accomplish it. I agree that people shouldn't be making all kinds of speed claims - but, it goes both ways. >> I think I'm ok with that. It lets me spend more time with the >> creative parts of my apps...If you want me to run your app, >> you'll need to email me the binary - my only box with vb6 >> installed is dead, and I'm not planning to install it on this one. > > Okay. I'll send you a compiled exe via email (I don't have a suitable web > page to host it at the moment). It errors out on the converted bitmap, telling me it's not a valid 24-bit bitmap. But, according to vista it is - and the bitmap class says it is. Maybe you can now send me your test image, and then I can run it agains something you know works? -- Tom Shelton |