Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

Bitmap to byte conversion variance?

Posted on 2006-03-31
17
Medium Priority
?
508 Views
Last Modified: 2010-04-23

Okay, this is a tricky one... or at least tricky to explain.  I have one large bitmap, and a small bitmap that I am trying to compare to a rectangle inside the larger bitmap. I have a pair of pictures that are a perfect match... I iterate through the pixels of the small bitmap and compare the GetPixel color result to that of the pixel within the larger bitmap, and it's a match.

Anyway, I asked for ideas on how to do this quickly, and Sancler had the clever idea of using a ComputeHash on the binary data.  When this works, it is incredibly fast.  But it doesn't always work, and I don't understand why.  My code is below.

What happens is that even before I compute the hash, the byte array that results from the apparently identical bitmaps are different.  In fact, they are WAY different, the byte array returned by one conversion is length 938, and the length of the other byte array is 375.  From bitmaps that apparently are the same... any theories as to what could be different??

(Sancler, hoping you take a crack at this.... if this works I didn't give you enough for the original idea.  :)  )

******


    Dim psmall As Bitmap = My.Resources.ComparePicA
    Dim pbig As Bitmap = My.Resources.MainPic
    Dim psmallcompare As Bitmap = pbig.Clone(New Rectangle(254, 189, 17, 17), pbig.PixelFormat)

    MsgBox(ComparePixels(psmall, psmallcompare))     ' <---- this returns true, indicating that the subrectangle is a match

    If psmall.PixelFormat = psmallcompare.PixelFormat Then
        MsgBox("format is the same")   ' <----- this returns true, which I think means same format
    End If

    Dim ic As System.Drawing.ImageConverter = New System.Drawing.ImageConverter
    Dim btImage1(1) As Byte
    btImage1 = CType(ic.ConvertTo(psmall, btImage1.GetType), Byte())
    Dim btImage2(1) As Byte
    btImage2 = CType(ic.ConvertTo(psmallcompare, btImage2.GetType), Byte())

   ' problem is apparently above this point, since btImage1 and btImage2 are different byte arrays

    Dim shaM As SHA256Managed = New SHA256Managed
    Dim hash1 As Byte() = shaM.ComputeHash(btImage1)
    Dim hash2 As Byte() = shaM.ComputeHash(btImage2)



.....


    Private Function ComparePixels(ByVal b1 As Bitmap, ByVal b2 As Bitmap) As Boolean

        Dim c2 As Color
        Dim c1 As Color

        For x As Integer = 0 To Min(b1.Width, b2.Width) - 1

            For y As Integer = 0 To Min(b1.Height, b2.Height) - 1

                c1 = b1.GetPixel(x, y)
                c2 = b2.GetPixel(x, y)

                If c1 <> c2 Then
                    Return False
                End If

            Next

        Next

        Return True

    End Function
0
Comment
Question by:riceman0
  • 13
  • 4
17 Comments
 

Author Comment

by:riceman0
ID: 16348255

Okay, got a little more info.  The only difference when I put a watch on the two bitmaps, the only difference is the flags parameter (could that be responsible for the big resultant byte array difference?).  The psmall (picture from the resource) flags parameter is 77840, and the psmallcompare (picture cloned from the large resource) flags is zero.  Looks like 77840 totals up to

ImageFlagsReadOnly (65536)
ImageFlagsHasRealPixelSize (8192)
ImageFlagsHasRealDPI (4096)
ImageFlagsColorSpaceRGB (16)

Man... I have no idea what these mean, or how these differences occurred.  Does anybody know how I can resolve these differences, or how to create bitmaps whose binary is compatible?

Thanks.

0
 

Author Comment

by:riceman0
ID: 16348314

Some experiments with bitmap flags, created add'l bitmaps in the following ways:

    Dim psmall As Bitmap = My.Resources.ComparePicA
    Dim pbig As Bitmap = My.Resources.MainPic
    Dim psmallcompare As Bitmap = pbig.Clone(New Rectangle(254, 189, 17, 17), pbig.PixelFormat)
    Dim psmallclone As Bitmap = psmall.Clone(New Rectangle(0, 0, psmall.Width, psmall.Height), psmall.PixelFormat)
    Dim psmallclone2 As Bitmap = psmall.Clone(New Rectangle(1, 1, psmall.Width - 1, psmall.Height - 1), psmall.PixelFormat)

resultant watch flags:

psmall.Flags = 77840      
psmallcompare.Flags = 0
psmallclone.Flags = 77840
pbig.Flags = 77840      
psmall.Flags = 77840      
psmallclone2.Flags = 0

The WEIRDEST thing is that the original source bitmaps have the same flags.  For some reason the clones of subrectangles results in very different flags.  
0
 

Author Comment

by:riceman0
ID: 16348321

It LOOKS like I can get the same flags if I take subrectangle clones of my original resources, instead of using a full-sized clone, or the orignal resource.  This is a big pain, I'll have to re-do all my resources; does anybody out there have any models of why this would be?  Or ideas on ways I can rectify these flags w/o this blind workaround??
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 

Author Comment

by:riceman0
ID: 16348383

Crap.  Using that technique, there is a pair of bitmaps that SHOULD match, have the same flags (because I forced a subrectangle clone), but result in a vertical and horizontal resolution of 96 for one and a resoultion of 96.01199 for the otherone, which apparently throws off the byte conversion.  Crap!!

Anyone, please, any ideas??  So close, and this is by far the fastest way I know to do this...
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 16348454
So, what is SHA256Managed and ComputeHash?
Generate Byte() array from every bitmap using LockBits and Marshal.Copy and compare two arrays.
0
 

Author Comment

by:riceman0
ID: 16349070

I've got that working fine, AlexFM:

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VB_DOT_NET/Q_21786077.html

but it seems way slower than converting the object to a byte array using the ImageConverter technique.  (In fact strangely, as I said in that question, the Marshal.Copy was slower than GetPixel for my size bitmaps.)  The problem now seems to be that the Clone maintains the content but messes with the bitmap format, which throws off the byte compare.

The ComputeHash is just a cute way of comparing the arrays (suggested by Sancler also in the above question).  I have yet to benchmark that vs. a straight array comaprison.  (Actually I might end up using a combination -- check for negative by comparing the first couple of values, else verify positive by comparing the hashes.)
0
 

Author Comment

by:riceman0
ID: 16349079

Oh wait, I hadn't tried marshal.copy, I was using marshal.getbyte.  That might be faster if I don't care about individual pixels, I'll try it.
0
 

Author Comment

by:riceman0
ID: 16349084
And it shouldn't be screwed up by the clone since I'd just be accessing the BitmapData... will definitely try it.
0
 

Author Comment

by:riceman0
ID: 16349171

Actually AlexFM, I don't suppose you have any code that Marshal.Copys the bitmap data, I'm having some trouble:

marshal.Copy mybitmapdata.Scan0 ... something or other
0
 

Author Comment

by:riceman0
ID: 16349180

okay, how come this works:

Blue(x, y) = Marshal.ReadByte(bd.Scan0, (bd.Stride * y) + (4 * x))

but this doesn't work?

Dim m_btimage(1) As Byte
Dim bd As BitmapData
bd = m_bmp.LockBits(New Rectangle(0, 0, m_bmp.Width, m_bmp.Height), ImageLockMode.ReadOnly, m_bmp.PixelFormat)
Marshal.Copy(bd.Scan0, 0, m_btimage, m_bmp.Width * m_bmp.Height)
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 16349650
Dim m_btimage(m_bmp.Width * m_bmp.Height * 3) As Byte

This is right for RGB24 image. For other format array size can be different. This code is right also if BitmapData.Stride is equal to Width*Height*3, which is not always true. If Stride is not equal to line size, every line should be copied separately. I will post more code later.
0
 

Author Comment

by:riceman0
ID: 16349804

Oh, right, size might be wrong too.  What was screwing me up was plugging bd.Scan0 into the first parameter of Marshal.Copy.  Yeah, let me know if you know how to do this, I'll keep trying...
0
 

Author Comment

by:riceman0
ID: 16349884

I've got a couple irons in the fire on this one (hope this isn't starting to become multiple questions), looking for an alternative to clone, which might be too smart and was messing with the bitmap format.  Went back to bitblt, hacked some other working code, and darned if the following SHOULD work to copy a bitmap to another, but doesn't.  Wow, I get stuck a lot.  If anyone can help me make progress on any one of these little issues, it might help me solve the big one.

Thanks all.

    Public Function Copy(ByVal r As Rectangle) As Bitmap

        Dim newbmp As New Bitmap(r.Width, r.Height, m_bmp.PixelFormat)

        Dim newgraphic As Graphics = Graphics.FromImage(m_bmp)
        Dim oldgraphic As Graphics = Graphics.FromImage(newbmp)

        Dim newhdc As IntPtr = newgraphic.GetHdc
        Dim oldhdc As IntPtr = oldgraphic.GetHdc

        Dim i As Integer = BitBlt(newhdc, 0, 0, r.Width, r.Height, oldhdc, r.X, r.Y, SRCCOPY)  '<-- i returns 1, meaning success

        newgraphic.ReleaseHdc(newhdc)
        oldgraphic.ReleaseHdc(oldhdc)

        newgraphic.Dispose()
        oldgraphic.Dispose()
0
 
LVL 48

Accepted Solution

by:
AlexFM earned 2000 total points
ID: 16350028
This is tested code. You need first function.

    ' Create Byte array from RGB24 bitmap
    Private Function BitmapToBytesRGB24(ByVal bmp As Bitmap) As Byte()
        If bmp.PixelFormat <> PixelFormat.Format24bppRgb Then
            Return Nothing
        End If

        Dim i As Integer
        Dim length As Integer = bmp.Width * bmp.Height * 3

        Dim bytes(length) As Byte

        Dim data As BitmapData
        data = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), _
            ImageLockMode.ReadOnly, _
            bmp.PixelFormat)

        If (data.Stride = bmp.Width * 3) Then
            Marshal.Copy(data.Scan0, bytes, 0, length)
        Else
            For i = 0 To bmp.Height - 1
                Dim p As IntPtr = New IntPtr(data.Scan0.ToInt32() + data.Stride * i)
                Marshal.Copy(p, bytes, i * bmp.Width * 3, bmp.Width * 3)
            Next
        End If

        bmp.UnlockBits(data)

        Return bytes
    End Function

    ' Create RGB24 bitmap from Byte array
    Private Function BitsToBitmapRGB24(ByVal bytes() As Byte, ByVal width As Integer, ByVal height As Integer)
        If (bytes.GetLength(0) < width * height * 3) Then
            Return Nothing
        End If

        Dim bmp As Bitmap
        Dim i As Integer

        bmp = New Bitmap(width, height, PixelFormat.Format24bppRgb)

        Dim data As BitmapData
        data = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), _
            ImageLockMode.WriteOnly, _
            bmp.PixelFormat)

        If (data.Stride = width * 3) Then
            Marshal.Copy(bytes, 0, data.Scan0, width * height * 3)
        Else
            For i = 0 To bmp.Height - 1
                Dim p As IntPtr = New IntPtr(data.Scan0.ToInt32() + data.Stride * i)
                Marshal.Copy(bytes, i * bmp.Width * 3, p, bmp.Width * 3)
            Next
        End If

        bmp.UnlockBits(data)

        Return bmp
    End Function
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 16350029
Addition:

Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
0
 

Author Comment

by:riceman0
ID: 16350569

Awesome, thank you.  This whole thing has halved my compare time.  

I'm going to figure out what was wrong with my BitBlt copy in another question, because I'd still like compare the current approach

Clone->LockBits->Marshal.Copy->ComputeHash, compare hash

to

BitBlt->ImageConverter to byte array->ComputeHash, compare hash

but right now I'm still way ahead.  Thanks!
0
 

Author Comment

by:riceman0
ID: 16351187

Actually... it's a good bit faster without the hash, just parsing through the byte array.  So the real magic bullet was just being able to convert to a byte array.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

It’s quite interesting for me as I worked with Excel using vb.net for some time. Here are some topics which I know want to share with others whom this might help. First of all if you are working with Excel then you need to Download the Following …
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
Despite its rising prevalence in the business world, "the cloud" is still misunderstood. Some companies still believe common misconceptions about lack of security in cloud solutions and many misuses of cloud storage options still occur every day. …
Suggested Courses

580 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question