Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Replacing Pixels in BMP 1

Status
Not open for further replies.

Error7

Programmer
Jul 5, 2002
656
0
16
GB
I've been away from the forum for a while because I've been busy on other hobbies but now I have started a new project which is causing me a small problem.

I have a bmp in which I want to replace all pixels that have a value lower or higher than a known range of values, with a common colour. i.e. a head and shoulders shot of somebody on a multi coloured background. I find the lowest and highest values of the skin tones then replace everything else.

I've been quite succesful so far by scanning each x, y co-ordinate and replacing each pixel as I go, but not surprisingly, this method is painfully slow.

Does anybody know of a more efficient way of doing this?

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Didn't there used to be an edit thread option? I can't find it now. I should add that I did a search and came up with a similar question in thread222-91495 but that's not quite the same thing.

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Its nice to see you back bigal. It would be better if you show your code so that we could see what you are doing and what do you want.

As far as manipulation of pixels in a bitmap is concerned, it would be really fast if you directly access the bits in the memory which make up the image.

Manipulating pixels using GetPixel/SetPixelV and Point/PSet is very slow. However, if you directly access the bits of your bitmap and manipulate them directly, you get very high speed results. There are several GDI functions which allow you to directly manipulate the bitmap bits without interacting the bitmap object or the associated device context.

Have a look at GetDIBits, SetDIBits, CreateDIBSection and GetObject functions.

Moreover, you can also map the 2-dimensional bitmap bits in the memory to a 2-dimensional array and access the bitmap bits as if they were actually inside the array you have declared. This requires you to play with SAFEARRAY structure which makes your array point directly to the bitmap bits instead of consuming its own space in memory.

The advantage is that you acsess the pixels of your bitmap just like a two dimensional array instead of computing complicated offsets for each pixel from the beginning of data.

I have also written a program which implements some basic graphic filters like smoothing, sharpening, embossing, diffusing, edge detection and grayscale conversion. In that program I used both techniques and the resulting performance was outstanding.
 
Hi Hypetia

Sounds like you're well experienced in what I'm trying to do.

GetPixel/SetPixel and Point/PSet I can understand and are what I am currently using in my code, but GetDIBits and SetDIBits are beyond my capabilities. Can you offer any pointers to head me in the right direction.

Thanks a million.

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
bigalbigal, play around with the example at the bottom of this page.


Two strings walk into a bar. The first string says to the bartender: 'Bartender, I'll have a beer. u.5n$x5t?*&4ru!2[sACC~ErJ'. The second string says: 'Pardon my friend, he isn't NULL terminated'.
 
Do a google search on 'mc1097.pdf'. This will take you to a superb article from VBPJ archives. This article explains manipulating SAFEARRAYs and VB pointers in detail.

A section in the article also explains how to directly manipulate bitmap bits for image processing using arrays pointing bitmap data as I explained above.
 
Thank you kindly gents. I'll get stuck into it this weekend.

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Of course, it is possible to do this at almost blitting speeds...
 
...with only a few lines of code...and no need to play around with those DIB things, or with SafeArrays, or even with bits or pixels...
 
ExtFloodFill?

Two strings walk into a bar. The first string says to the bartender: 'Bartender, I'll have a beer. u.5n$x5t?*&4ru!2[sACC~ErJ'. The second string says: 'Pardon my friend, he isn't NULL terminated'.
 
Goodness, no; you'd still have to do your own per-pixel scanning of the image to ensure that all the pixels were being changed, plus it is a relatively expensive operation...
 
So how much longer are you going to keep us in suspense?? ;)
 
MaskBlt?

Two strings walk into a bar. The first string says to the bartender: 'Bartender, I'll have a beer. u.5n$x5t?*&4ru!2[sACC~ErJ'. The second string says: 'Pardon my friend, he isn't NULL terminated'.
 
ColorMatrix?

Two strings walk into a bar. The first string says to the bartender: 'Bartender, I'll have a beer. u.5n$x5t?*&4ru!2[sACC~ErJ'. The second string says: 'Pardon my friend, he isn't NULL terminated'.
 
No - RemapTable. I use ColorMatrix for tinting, or modifying hue and saturation. One of the first GDI+ examples that I posted in this forum demonstrated using ColorMatrix to quickly create the grayscale version of a bitmap, but that thread seems to have been deleted.

More recently I illustrated how GDI+ can be used to save images in formats other than BMP (e.g. the ever ellusive jpg format) without reverting to a 3rd party DLL or OCX. That was in thread222-760856, and for those that want to see the following example in action I suggest you have a quick look at that thread since it contains links to the GDI+ type library that I use (basically you cannot use GDI+ from VB without it).

OK, for this example you'll need to add a reference to the GDI+ type library, then drop a couple of picture boxes onto a form, and a command button. Picture1 should contain a picture with some colour (or colours) that you want to replace. Then drop in this code...
Code:
[COLOR=blue]Private Sub Command3_Click()
    Dim GpInput As GdiplusStartupInput
    Dim Token As Long
    Dim gdiImage As Long
    Dim gdiImageAttribs As Long
    Dim gdiGraphics As Long
    Dim myColorMap(1) As ColorMap

    ' You'd build up an array of all the colours you want to
    ' replace
    myColorMap(0).oldColor = White
    myColorMap(0).newColor = YellowGreen
    myColorMap(1).oldColor = Blue
    myColorMap(1).newColor = Red ' ARGB(255, 0, 0) ' ARGB not illustrated in this example, so commented out
    
    
    GpInput.GdiplusVersion = 1
    If GdiplusStartup(Token, GpInput) <> Ok Then
        MsgBox "Error loading GDI+!", vbCritical
    Else
        GdipCreateFromHDC Picture2.hDC, gdiGraphics
        GdipCreateBitmapFromHBITMAP Picture1.Picture.Handle, Picture1.Picture.hPal, gdiImage
        GdipCreateImageAttributes gdiImageAttribs
        GdipSetImageAttributesRemapTable gdiImageAttribs, ColorAdjustTypeBitmap, 1, 2, myColorMap(0)
        GdipDrawImageRectRect gdiGraphics, gdiImage, 0, 0, 300, 300, 0, 0, 300, 300, UnitPixel, gdiImageAttribs
        GdiplusShutdown Token
    End If
    
End Sub
[/color]
And that's it
 
I like it but how would you go about changing a range of colors, using DIB things seems easier in that since but I am sure you'll show me why that's not the case:) Have a star.

Two strings walk into a bar. The first string says to the bartender: 'Bartender, I'll have a beer. u.5n$x5t?*&4ru!2[sACC~ErJ'. The second string says: 'Pardon my friend, he isn't NULL terminated'.
 
You just need to generate the necessary ColorMap array at the point I put my comment in the code. Here's a (simple) example that will replace all pure grays (those where R, G and B are the same) with red.
Code:
[COLOR=blue]Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Private Sub Command3_Click()
    Dim GpInput As GdiplusStartupInput
    Dim Token As Long
    Dim gdiImage As Long
    Dim gdiImageAttribs As Long
    Dim gdiGraphics As Long
    Dim myColorMap(255) As ColorMap
    Dim lp As Long
    
    
    ' You'd build up an array of all the colours you want to
    ' replace
    For lp = 0 To 255
        myColorMap(lp).oldColor = ARGB(CByte(lp), CByte(lp), CByte(lp))
        myColorMap(lp).newColor = ARGB(255, 0, 0)
    Next
    
    GpInput.GdiplusVersion = 1
    If GdiplusStartup(Token, GpInput) <> Ok Then
        MsgBox "Error loading GDI+!", vbCritical
    Else
        GdipCreateFromHDC Picture2.hDC, gdiGraphics
        GdipCreateBitmapFromHBITMAP Picture1.Picture.Handle, Picture1.Picture.hPal, gdiImage
        GdipCreateImageAttributes gdiImageAttribs
        GdipSetImageAttributesRemapTable gdiImageAttribs, ColorAdjustTypeBitmap, 1, 255, myColorMap(0)
        GdipDrawImageRectRect gdiGraphics, gdiImage, 0, 0, 300, 300, 0, 0, 300, 300, UnitPixel, gdiImageAttribs
        GdiplusShutdown Token
    End If

End Sub
Private Function ARGB(R As Byte, G As Byte, B As Byte, Optional Alpha As Byte = 255)
    Dim myARGB As RGBQUAD
    Dim Result As Long
    
    myARGB.rgbBlue = B
    myARGB.rgbGreen = G
    myARGB.rgbRed = R
    myARGB.rgbReserved = Alpha
    
    CopyMemory Result, myARGB, 4
    ARGB = Result
End Function
[/color]


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top