Results 1 to 40 of 59

Thread: Array optimization - String or Integer?

Hybrid View

  1. #1
    Cumbrian Milk's Avatar
    Join Date
    Jan 2007
    Location
    0xDEADBEEF
    Posts
    2,448

    Re: Array optimization - String or Integer?

    Here's a quick way to get those pixels into an array using GetDIBits. There is a slight caveat extacting the data this way inverts the Y axis and alters the colours (they become BGRA as opposed to RGBA). I've included a conversion function to toggle between RGBA and BGRA. There is also an option to produce a two Dimensional array but you will find the single dimensional array performs better. If you want to use this with the two dimensional array you will have to invert the Y coordinates in your Database, much better would be to then convert each pair of X and inverted Y integers into a single Long and use a one dimensional array, this is easier than it sounds and can be done quickly.

    Also seeing as you are only interested in a few colours it's worth converting the image data from a long array and into a byte array that just holds the colours you are interested in, see the retByteData function.
    vb Code:
    1. 'Module code
    2. Option Explicit
    3.  
    4. Private Declare Function GetObjectA Lib "GDI32" (ByVal hObject As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long
    5. Private Declare Function GetDIBits Lib "GDI32" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
    6. Private Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As Long)
    7.  
    8. Private Type BITMAP '24 bytes
    9.  bmType As Long
    10.  bmWidth As Long
    11.  bmHeight As Long
    12.  bmWidthBytes As Long
    13.  bmPlanes As Integer
    14.  bmBitsPixel As Integer
    15.  bmBits As Long
    16. End Type
    17.  
    18. Private Type RGBQUAD
    19.  Blue As Byte
    20.  Green As Byte
    21.  Red As Byte
    22.  Alpha As Byte
    23. End Type
    24.  
    25. Private Type BITMAPINFOHEADER '40 bytes
    26.  bmSize As Long
    27.  bmWidth As Long
    28.  bmHeight As Long
    29.  bmPlanes As Integer
    30.  bmBitCount As Integer
    31.  bmCompression As Long
    32.  bmSizeImage As Long
    33.  bmXPelsPerMeter As Long
    34.  bmYPelsPerMeter As Long
    35.  bmClrUsed As Long
    36.  bmClrImportant As Long
    37. End Type
    38.  
    39. Private Type BITMAPINFO
    40.  bmHeader As BITMAPINFOHEADER
    41.  bmColors(0 To 255) As RGBQUAD
    42. End Type
    43.  
    44. Public Function RetDIBDataLong(PictureObject As Object, Optional Return2DArray As Boolean) As Long()
    45. Dim BM As BITMAP, bmi As BITMAPINFO, lngImageData() As Long
    46.    'The PictureObject must be an object with both a HDC and Image property
    47.    'such as a PictureBox, UserControl or Form
    48.    
    49.    GetObjectA PictureObject.Image, 24, BM
    50.    bmi.bmHeader.bmWidth = BM.bmWidth
    51.    bmi.bmHeader.bmHeight = BM.bmHeight
    52.    bmi.bmHeader.bmSize = 40
    53.    bmi.bmHeader.bmPlanes = 1
    54.    bmi.bmHeader.bmBitCount = 32
    55.    
    56.    If Return2DArray Then
    57.      ReDim lngImageData(BM.bmWidth - 1, BM.bmHeight - 1)
    58.      GetDIBits PictureObject.hDC, PictureObject.Image.Handle, 0, BM.bmHeight, lngImageData(0, 0), bmi, 0
    59.    Else
    60.      ReDim lngImageData(BM.bmWidth * BM.bmHeight - 1)
    61.      GetDIBits PictureObject.hDC, PictureObject.Image.Handle, 0, BM.bmHeight, lngImageData(0), bmi, 0
    62.    End If
    63.    
    64.    RetDIBDataLong = lngImageData
    65. End Function
    66.  
    67. Public Function retByteData(PictureObject As Object, SearchPalette() As Long) as Byte()
    68. 'The ImageObject must be an object with both a HDC and Image property
    69. 'such as a PictureBox, UserControl or Form
    70.  
    71. 'The searchpalette should contain no more than 255 colours and these
    72. 'colours must be in the form of DIBSection colours i.e. BGRA not RGBA see
    73. 'ConvertColour function below
    74.  
    75.  
    76. Dim i As Long, ii As Long, lPalLen As Long, lngImageData() As Long, bytImageData() As Byte
    77.   lPalLen = UBound(SearchPalette)
    78.   If lPalLen > 254 Then lPalLen = 254
    79.  
    80.   lngImageData = RetDIBDataLong(PictureObject)
    81.   i = UBound(lngImageData)
    82.   ReDim bytImageData(i)
    83.  
    84.   For i = 0 To i
    85.     For ii = 0 To lPalLen
    86.       If lngImageData(i) = SearchPalette(ii) Then
    87.         bytImageData(i) = ii + 1
    88.         Exit For
    89.       End If
    90.     Next ii
    91.   Next i
    92.  
    93.  'If you only want a couple of colours they might be better HardCoded here rather
    94.  'than in the passed SearchPalette, example loop would then be...
    95.   'For i = 0 To i
    96.       'Select Case lngImageData(i)
    97.         'Case Colour1
    98.           'bytImageData(i) = 1
    99.         'Case Colour2
    100.           'bytImageData(i) = 2
    101.       'End Select
    102.   'Next i
    103.  
    104.   retByteData = bytImageData
    105. End Function
    106.  
    107. Public Function ConvertColour(ByVal Colour As Long) As Long
    108. Dim uRGBA As RGBQUAD, Swp As Byte
    109.   RtlMoveMemory uRGBA, Colour, 4&
    110.   With uRGBA
    111.      Swp = .Red
    112.     .Red = .Blue
    113.     .Blue = Swp
    114.   End With
    115.   RtlMoveMemory Colour, uRGBA, 4&
    116.   ConvertColour = Colour
    117. End Function
    vb Code:
    1. 'Example calling code for a form with two pictureboxes and two command buttons
    2. Option Explicit
    3. Private Declare Function SetPixelV Lib "GDI32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) As Byte
    4.  
    5. Private Sub Command1_Click()
    6. Dim PicData() As Byte, pal() As Long, i As Long, W As Long
    7.   ReDim pal(3)
    8.   'First fill the pal array with the colours we want to capture, up to 255
    9.   'RGB can be used to generate 32bpp DIB Colours by swaping Red for Blue and visa versa
    10.   pal(0) = RGB(0, 3, 1) 'same as ConvertColour(RGB(1, 3, 0))
    11.   pal(1) = RGB(3, 0, 15)
    12.   pal(2) = RGB(33, 0, 206)
    13.   pal(3) = RGB(249, 252, 250)
    14.   PicData = retByteData(Picture1, pal)
    15.  
    16.   'This next bit is slow, it is just to show the data we have just gained visualy
    17.   With Picture1
    18.     W = Picture1.ScaleX(.ScaleWidth, .ScaleMode, vbPixels)
    19.   End With
    20.  
    21.   Picture2.Cls
    22.   For i = 0 To UBound(PicData)
    23.     If PicData(i) Then SetPixelV Picture2.hDC, i Mod W, i \ W, QBColor(PicData(i))
    24.   Next i
    25.   'As you can see the Y axis has been inverted
    26. End Sub
    27.  
    28.  
    29. Private Sub Command2_Click()
    30.  
    31. Dim PicData() As Long, X As Long, Y As Long, UBY As Long
    32.   'Get the pixel data very quickly
    33.   PicData = RetDIBDataLong(Picture1, True)
    34.  
    35.   'Then a slow method to show how the Y axis is inverted and the Red and Blue bytes are swapped
    36.   Picture2.Cls
    37.   UBY = UBound(PicData, 2)
    38.   For X = 0 To UBound(PicData, 1)
    39.     For Y = 0 To UBY
    40.       SetPixelV Picture2.hDC, X, Y, PicData(X, Y)
    41.     Next Y
    42.   Next X
    43. End Sub
    To copy the code, quote the post and copy from there.
    Attached is the image I tested this with.
    Attached Images Attached Images  
    Last edited by Milk; Sep 17th, 2007 at 04:51 PM. Reason: errors, errors

  2. #2

    Thread Starter
    Hyperactive Member Krass's Avatar
    Join Date
    Aug 2000
    Location
    Montreal
    Posts
    489

    Re: Array optimization - String or Integer?

    Hello.

    I think I'm done optimizing my main loop. Using the method you guys suggested (user defined types) - and avoiding strings - significantly speeds up the process.

    I don't think the code can be done better, below is an idea of what it looks like... It's designed to find matching pixels (same X,Y from my sub-array (from 1 millions record array - about 500 records each times) as the X,Y from the small image I'm comparing it to).

    Code:
    Do While xSmall <= intSmallUBound - 1 And xHuge <= HugeType(i).Length - 1
        If HugeType(i).Coords(xHuge).x < (arrSmallSubArray(0, xSmall) + xAdd) Then
            xHuge = xHuge + 1
        ElseIf HugeType(i).Coords(xHuge).x > (arrSmallSubArray(0, xSmall) + xAdd) Then
            xSmall = xSmall + 1
        ElseIf HugeType(i).Coords(xHuge).y < (arrSmallSubArray(1, xSmall) + yAdd) Then
            xHuge = xHuge + 1
        ElseIf HugeType(i).Coords(xHuge).y > (arrSmallSubArray(1, xSmall) + yAdd) Then
            xSmall = xSmall + 1
        Else
            xSmall = xSmall + 1
            xHuge = xHuge + 1
            MatchCount = MatchCount + 1
        End If
    Loop
    My next step is to read, understand and implement Milk's piece of code. Btw thank you, that seems to be pretty complete. That's the last portion slowing down my application with those .pset and .point calls. I'm hoping to have time testing this next week.

    I've got a (maybe a little off-topic) question. When populating a recordset, why must I use .movelast to be able to retrieve .recordcount? If I just populate it and check the .recordcount it will always show "0". Not a big problem, but I find it weird to have to use .movelast and .movefirst to retrieve that info.

    Thank you!
    Chris

  3. #3
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: Array optimization - String or Integer?

    Quote Originally Posted by Krass
    When populating a recordset, why must I use .movelast to be able to retrieve .recordcount? If I just populate it and check the .recordcount it will always show "0". Not a big problem, but I find it weird to have to use .movelast and .movefirst to retrieve that info.
    It's an optimization; the server doesn't send the entire recordset until it has to.

    If you have some time, I've always been curious if the With operator speeds up UDT array access. If you change the code you posted to the following, is it any faster?
    Code:
    With HugeType(i)
        Do While xSmall <= intSmallUBound - 1 And xHuge <= .Length - 1
            If .Coords(xHuge).x < (arrSmallSubArray(0, xSmall) + xAdd) Then
                xHuge = xHuge + 1
            ElseIf .Coords(xHuge).x > (arrSmallSubArray(0, xSmall) + xAdd) Then
                xSmall = xSmall + 1
            ElseIf .Coords(xHuge).y < (arrSmallSubArray(1, xSmall) + yAdd) Then
                xHuge = xHuge + 1
            ElseIf .Coords(xHuge).y > (arrSmallSubArray(1, xSmall) + yAdd) Then
                xSmall = xSmall + 1
            Else
                xSmall = xSmall + 1
                xHuge = xHuge + 1
                MatchCount = MatchCount + 1
            End If
        Loop
    End With
    Also, I see couple tweaks you could do to squeeze a little more speed out of the posted code.
    Code:
    With HugeType(i)
        Do While xSmall < intSmallUBound And xHuge < .Length ' removing the two subtractions should help a bit
            Select Case arrSmallSubArray(0, xSmall) + xAdd
                Case Is > .Coords(xHuge).x: xHuge = xHuge + 1
                Case Is < .Coords(xHuge).x: xSmall = xSmall + 1
                Case Else
                    Select Case arrSmallSubArray(1, xSmall) + yAdd
                        Case Is > .Coords(xHuge).y: xHuge = xHuge + 1
                        Case Is < .Coords(xHuge).y: xSmall = xSmall + 1
                        Case Else
                            xSmall = xSmall + 1
                            xHuge = xHuge + 1
                            MatchCount = MatchCount + 1
                    End Select
            End Select
        Loop
    End With
    While I concede that this logic is much less readable, it should be perceptibly more efficient. Every iteration of the Do...Loop has at least 2 fewer arithmetic operations, and each match saves another two operations. Given the huge number of passes this loop is making, this could add up to a fairly decent speed boost.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width