1

I have a single array containing the image data acquired from a camera. The array contains only the 8 bit data relating to each pixel. currently I perform the following to display the image on the picturebox:

    Dim bm As New Bitmap(CInt(width), CInt(height))
    Dim bufcnt As Integer = 0
    Dim mrow As Integer
    Dim mcol As Integer
    For mrow = 0 To height - 1
        For mcol = 0 To width - 1
            bm.SetPixel(mcol, mrow, Color.FromArgb(imageBuffer(bufcnt), imageBuffer(bufcnt), imageBuffer(bufcnt)))
            bufcnt = bufcnt + 1
        Next mcol
    Next mrow
    PCBIMG.Image = bm

However, on larger images this does take some time, is there a more efficient way of doing this that allows for realtime display of the image?

Rob Hulme
  • 11
  • 2

1 Answers1

2

Here is an example that uses Marshal.Copy to move the entire bitmap at once. The bitmap format must be Format8bppIndexed so that you can move the raw data directly into the bitmap object rather than convert each pixel to 24bpp manually. The 8bpp indexed format can actually display any 8-bit subset of 24-bit colours so you need to define a colour palette for the image. I'm assuming you want greyscale, so the palette is just a 1:1 map.

For this you'll need the following imports :

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

Then :

Function ConvertBytesToBitmap(ByVal inBytes() As Byte, _
                              ByVal imgWidth As Integer, _ 
                              ByVal imgHeight As Integer) As Bitmap

    Dim b As New Bitmap(imgWidth, imgHeight, PixelFormat.Format8bppIndexed)

    ' Define a greyscale colour palette
    Dim ncp As ColorPalette = b.Palette
    Dim i As Integer
    For i = 0 To 255
        ncp.Entries(i) = Color.FromArgb(255, i, i, i)
    Next
    b.Palette = ncp

    ' Copy the bytes to the bitmap's data region
    Dim BoundsRect As New Rectangle(0, 0, imgWidth, imgHeight)
    Dim bmpData As BitmapData = b.LockBits(BoundsRect, _
                                           ImageLockMode.WriteOnly, _
                                           b.PixelFormat)

    ' Bitmap.Scan0 returns a pointer to the image data array
    ' The data format is a 1D array, row-major format
    Dim ptr As IntPtr = bmpData.Scan0

    Dim numberOfBytes As Integer = bmpData.Stride * b.Height

    Marshal.Copy(inBytes, 0, ptr, numberOfBytes)
    b.UnlockBits(bmpData)
    Return b

End Function

To show an example of usage, I've chosen a 400x200 pixel image and have filled it with a horizontal gradient :

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim i As Integer
    Dim j As Integer
    ' Create example image buffer for 400x200px image
    Dim imgBuffer(79999) As Byte

    ' Fill example image buffer with horizontal gradient
    For i = 0 To 199
        For j = 0 To 399
            imgBuffer(j + i * 400) = 255 * j / 400
        Next
    Next

    PictureBox1.Image = ConvertBytesToBitmap(imgBuffer, 400, 200)
End Sub

The function above is not fully optimized for your purposes, of course. You could skip re-creating the greyscale palette each time and save a copy, either passing it in as an argument or building it into a class, etc. This could save a bit of time. Still, this should be several thousands of times faster than the code you are using, even if you create the colour palette each time.

J...
  • 30,968
  • 6
  • 66
  • 143