borderAndreaVB free resources for Visual Basic developersborder

AndreaVB Visual Basic and VB.NET source code resources - Copyright © 1999-2010 Andrea Tincani

AndreaVB Home | News Home | Forum Home | Downloads | Register | Search | PM | Profile

Previous Topic (Animations using DIB Section)Next Topic (DIB SECTION) New Topic Post Reply
AndreaVB OnLine : Articles and tutorials : Ways of doing Image Processing
Poster Resource
Asim-GDI GURU
Level: Sage

Registered: 29-07-2005
Posts: 54
icon Ways of doing Image Processing

     The Windows GUI is standing upon two pillers. Vector Graphics and Bitmap Graphics. Being quite difficult to manage, Vector Graphics are always very painful to handle but the possiblities are nearly impossible to find a limit. Where as Bitmaps are concerned, they are heavy Windows objects ( though DIBs are not ). Since the arrival of electrifying GUIs in the Windows, the interest of programmers towards Image Processing has awfully increased. This article shall tell you how to do it and how to make it work faster.
    There are many ways to do it, according to the requirements of the Image Filter, we are dealing with. A simple method is to use GetPixel and SetPixel API.This method is very slow but better for novice. I still remember 4 years ago when I created my first Image Processor, it used to take 1.5 seconds to Grayscale a picture of 250x250 pixels. But it was obviously the first step towards my new Greyscale function which does the same task on a picture of same dimensions in just 16 milliseconds. So lets see my first Greyscale function.

Option Explicit

' Structures Declaration
Private Type RGBQUAD
    rgbBlue As Byte
    rgbGreen As Byte
    rgbRed As Byte
    rgbReserved As Byte
End Type

Private Type BITMAPINFOHEADER
  biSize          As Long
  biWidth         As Long
  biHeight        As Long
  biPlanes        As Integer
  biBitCount      As Integer
  biCompression   As Long
  biSizeImage     As Long
  biXPelsPerMeter As Long
  biYPelsPerMeter As Long
  biClrUsed       As Long
  biClrImportant  As Long
End Type

Private Type BITMAPINFO
    bmiHeader As BITMAPINFOHEADER
    bmiColors As RGBQUAD
End Type

' API Declaration
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function SetPixelV Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) As Long
Private Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, _
ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFOHEADER, ByVal wUsage As Long) As Long
Private Declare Function SetDIBits Lib "gdi32" (ByVal hdc As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, _
ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFOHEADER, ByVal wUsage As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long
Private Declare Function CreateDIBSection Lib "gdi32.dll" (ByVal hdc As Long, ByRef pBitmapInfo As BITMAPINFO, ByVal un As Long, lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long

'Constants Declaration
Private Const SRCCOPY = &HCC0020
Private Const BI_RGB = 0&
Private Const DIB_RGB_COLORS = 0

' Personal Declarations
Dim bDIB() As RGBQUAD
Dim bih As BITMAPINFOHEADER
Dim DC As Long, DIBSection As Long, oldDIB As Long
Dim bi As BITMAPINFO

Private Sub Grey1_click()
Dim ti As Long
ti = GetTickCount
Dim x As Long, y As Long, Pixel As Long, GrayScale As Long
For x = 0 To Picture1.ScaleWidth
For y = 0 To Picture1.ScaleHeight
        Pixel = GetPixel(Picture1.hdc, x, y) ' Get one pixel from the PictureBox
        ' Now we process the acquired pixel
        GrayScale = ((77& * (Pixel And &HFF&) + 152& * (Pixel And &HFF00&) \ &H100& + 28& * (Pixel \ &H10000)) \ 256&) * &H10101
        ' Then setting back the processed pixel in the PictureBox
        Call SetPixelV(Picture1.hdc, x, y, GrayScale)
Next y
Next x
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub


The script above is a simple thing to understand. Nothing more than like getting a single pixel at a time, processing it and then setting it back. But this is is quit slow especially when you talk like a professional practical programmer. Let start coding a faster version of the same filter.

Private Sub Grey2_Click()
Dim x As Long, y As Long, TempDC As Long, TempBIT As Long, OldTempBIT As Long, Pixel As Long, GrayScale As Long,ti As Long
ti = GetTickCount
TempDC = CreateCompatibleDC(Picture1.hdc) ' Creating a Device Handle compatible with the Picturebox
TempBIT = CreateCompatibleBitmap(Picture1.hdc, Picture1.ScaleWidth, Picture1.ScaleHeight) ' Creating a DDB compatible with the Picturebox
OldTempBIT = SelectObject(TempDC, TempBIT) ' Selecting the compatible DDB in the compatible DC
Call BitBlt(TempDC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY) ' Copying the contents of Picturebox in the compatible DC
For x = 0 To Picture1.ScaleWidth
For y = 0 To Picture1.ScaleHeight
        Pixel = GetPixel(TempDC, x, y) ' Get one pixel from the Temporary Memory DC
        ' Now we process the acquired pixel
        GrayScale = ((77& * (Pixel And &HFF&) + 152& * (Pixel And &HFF00&) \ &H100& + 28& * (Pixel \ &H10000)) \ 256&) * &H10101
        ' Then setting back the processed pixel in the temporary DC
        Call SetPixelV(TempDC, x, y, GrayScale)
Next y
Next x
' Now copying back the contents of compatible DC to Picturebox
Call BitBlt(Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, TempDC, 0, 0, SRCCOPY)
Call SelectObject(TempDC, OldTempBIT) ' Good Programming Practice
Call DeleteObject(TempBIT) ' Deleting the compatible DDB
Call DeleteDC(TempDC) ' Deleting the temporary compatible DC
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub

By now I suppose you got the backround of what we are trying to do here. Do the Processing as fast as possible, and with lesser memory space being acquired. The above method is the most simplest to do if we are after speed. But in this script we are using DDB which some programmers may deslike. But this really affects the speed because then you are talking in terms of memory, and obviously the calculations on the data that is in memory will be much faster than getting and then setting back the data from the screen itself.

Private Sub Grey3_Click()
Dim ti As Long
ti = GetTickCount
Dim x As Long, y As Long
' Initiallizing the BITMAPINFOHEADER Structure
With bih
        .biSize = 40
        .biPlanes = 1
        .biHeight = -Picture1.ScaleHeight
        .biWidth = Picture1.ScaleWidth
        .biBitCount = 32
End With
' Redifing the limits of our local Pixel Array to the
' dimensions of the Picture in the Picturebox
ReDim bDIB(1 To Picture1.ScaleWidth, 1 To Picture1.ScaleHeight) As RGBQUAD
' Finally getting a copy of the whole original
' Pixel Array in our local Pixel Array bDIB()
Call GetDIBits(Picture1.hdc, Picture1.Picture.Handle, 0&, Picture1.ScaleHeight, bDIB(1, 1), bih, 0)
' Now we are about to edit each and every pixel in the array
For x = 1 To Picture1.ScaleWidth
  For y = 1 To Picture1.ScaleHeight
        bDIB(x, y).rgbBlue = 0.3 * bDIB(x, y).rgbRed + 0.587 * bDIB(x, y).rgbGreen + 0.114 * bDIB(x, y).rgbBlue
        bDIB(x, y).rgbGreen = bDIB(x, y).rgbBlue
        bDIB(x, y).rgbRed = bDIB(x, y).rgbBlue
  Next y
Next x
' The local array is edited. At last we've to copy the
' local array to the Picturebox original array
Call SetDIBits(Picture1.hdc, Picture1.Picture.Handle, 0, Picture1.ScaleHeight, bDIB(1, 1), bih, 0)
' You've to refresh the Picturebox regardless the property of AutoRedraw.
Picture1.Refresh ' This statement is very neccessary
Erase bDIB()
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub

This is a simple way to do it. No extra DIBs or DDBs are created. In the frst phase the Pixel Array of the picture allready present in the Picturebox is being copied to a local made array called bDIB(). Then we process this local array and then at last we finally put back the processed Pixel Array to the Picturebox. A simple effective and very fast method to edit the pixels of a picture allready present in any Picturebox. This is a very simple way to achieve speed, and when I say speed I mean it. This function takes 31 milliseconds on my machine(AMD Sempron, 1.20 GHz, 192 MB RAM). But you see the day you relax on the accuracy of your results will be your last day in programming. There is no restriction over trying any other way to get the results in lesser time. And obviously there is no cure for the speed geeks. So I'm putting down another version of the same filter. But remember this is a organizational function which only implies that you must use it when you are dealing with a whole sole Image Processing project. Actually this is the version that I coded in the DIB Section article.


Private Sub Form_Load()
' Creating the HDC to contain the DIB Section
DC = CreateCompatibleDC(Picture1.hdc)
If (DC <> 0) Then   ' Checking whether the DC is made or not
' Finally making the 6 settings required by the programmer to set
With bi.bmiHeader
    .biSize = 40
    .biWidth = Picture1.ScaleWidth     ' Picture Width
    .biHeight = -Picture1.ScaleHeight  ' Picture Height(dont confuse with the (-) sign.Just make it compulsory)
    .biPlanes = 1
    .biBitCount = 32
    .biSizeImage = (((Picture1.ScaleWidth * 3) + 3) And &HFFFFFFFC) * Picture1.ScaleHeight ' This is how we can calculate the Image Size in Bytes
End With
Dim addr As Long ' This will contain the memory address to the Bitmap data in the memory
' And now making the EMPTY DIB Section with above settings
DIBSection = CreateDIBSection(DC, bi, DIB_RGB_COLORS, addr, 0, 0)
If (DIBSection <> 0) Then ' Checking whether the DIB Section is made or not
    ' Selecting the DIB Section into the DC to do drawing on it
    oldDIB = SelectObject(DC, DIBSection)
Else
    Call DeleteDC(DC) ' If the DIB Section could not be made then delete the DC we made earlier
End If
End If
End Sub

Private Sub Grey4_Click()
Dim X As Long, Y As Long, ti As Long
ti = GetTickCount
' Now copying the Picturebox Image in the EMPTY DIB Section
Call BitBlt(DC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY)
' Getting the Pixel Array of the DIB Section
ReDim bDIB(1 To Picture1.ScaleWidth, 1 To Picture1.ScaleHeight) As RGBQUAD
' The following API is the ONE which gets the Pixel Array of the DIB Section
Call GetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), bi.bmiHeader, 0&)
' We have to go through each and every pixel in the Bitmap
For X = 1 To Picture1.ScaleWidth
  For Y = 1 To Picture1.ScaleHeight
        bDIB(X, Y).rgbBlue = 0.3 * bDIB(X, Y).rgbRed + 0.587 * bDIB(X, Y).rgbGreen + 0.114 * bDIB(X, Y).rgbBlue
        bDIB(X, Y).rgbGreen = bDIB(X, Y).rgbBlue
        bDIB(X, Y).rgbRed = bDIB(X, Y).rgbBlue
  Next Y
Next X
' Setting back the Pixel Array of the DIB Section
Call SetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), bi.bmiHeader, 0&)
' Copying the processed picture into the Picturebox
Call BitBlt(Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, DC, 0, 0, SRCCOPY)
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub

Private Sub Form_Unload(Cancel As Integer)
Call SelectObject(DC, oldDIB)
Call DeleteObject(DIBSection)
Call DeleteDC(DC)
Erase bDIB()
End Sub


     I'm not describing this function because its from the DIB Section article which I suppose the reader has allready read. But to be true this function is also taking 31 milliseconds to complete as the last one did. But this function gives you the scope behind how to map the structure of an organizational Image Processing project. Here we create the DIB Section in the begining and then just do all the things in the button_click subroutine. Now I must stop writing any new function. The fastest function of all functions remain the Grey3 because of many reasons. Firstly not wasting time in creating any new DIBs or DDBs, just directly getting-processing-setting back the pixel array of the picture allready present in the Picturebox. Grey3 completes the operation in 16 milliseconds which is quite fast when talking in terms of VB. But as I said there's no cure for the speed geeks. So get your minds on this topic and try to achieve the results in lesser than 16 milliseconds. And dont forget to send me a copy of your function...

16-07-2006 at 11:16 PM
View Profile Send Email to User Show All Posts | Add Comment
AndreaVB OnLine : Articles and tutorials : Ways of doing Image Processing
Previous Topic (Animations using DIB Section)Next Topic (DIB SECTION)New Topic Post Reply
Surf To:


Not Logged In? Username: Password: Lost your password?
Partners: Download Actual Software | Free Software Download
borderAndreaVB free resources for Visual Basic developersborder

borderAndreaVB Visual Basic and VB.NET source code resources - Copyright © 1999-2010 Andrea Tincaniborder