Bitmap to byte conversion variance?


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
riceman0Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

riceman0Author Commented:

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
riceman0Author Commented:

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
riceman0Author Commented:

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
Cloud Class® Course: CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

riceman0Author Commented:

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
AlexFMCommented:
So, what is SHA256Managed and ComputeHash?
Generate Byte() array from every bitmap using LockBits and Marshal.Copy and compare two arrays.
0
riceman0Author Commented:

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
riceman0Author Commented:

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
riceman0Author Commented:
And it shouldn't be screwed up by the clone since I'd just be accessing the BitmapData... will definitely try it.
0
riceman0Author Commented:

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
riceman0Author Commented:

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
AlexFMCommented:
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
riceman0Author Commented:

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
riceman0Author Commented:

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
AlexFMCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AlexFMCommented:
Addition:

Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
0
riceman0Author Commented:

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
riceman0Author Commented:

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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.