Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Reading/Writing Image Data

0.00/5 (No votes)
4 Mar 2021 1  
Fast reading/writing of bitmap data in VB.NET using pointers
The code snippets in this tip will show you how to read/write image data using the .NET Bitmap class in VB.NET.

Background

I was looking for a solution in VB.NET to speed up image read/write operations by using pointers to a bitmap instead of accessing the pixel object.

Using the Code

a) Reading in Bitmap Data in VB.NET Works as Follows

  1. Read in the bitmap data into a bitmap object.
  2. Get the bitmap data and lock its position in memory, so the garbage collection cannot move the data around anymore.
  3. Get a pointer to the first byte of the image data, which is bmpData.Scan0.
  4. Calculate the offset of the pixel from the first byte as (bmpData.Stride * row) + (4 * column).
  5. Read out the bytes corresponding to the individual R, G, B channels of the pixel. Here, the pointer position is calculated for the 32bppArgb format in which 1 bytes is reserved for each channel in the order B, G, R, transparency (alpha channel). In this example, the channel intensities are converted to a grayscale value.   
  6. Unlock the bitmap data.
  7. Do some calculation with the data in the array.
'read in bitmap
fs = New FileStream(m_imgpath, FileMode.Open, FileAccess.Read)
bm = New Bitmap(fs)
fs.Close()

Dim grayval as double
Dim arr(bm.height, bm.width) as Double
Dim pixptr As Integer
'weights for grayscal conversion
Dim wb, wg, wr as double
'chose suitable weights for your application here (display, visual effect, computer vision etc.)
wr = 0.2126 : wg = 0.7125 : wb = 1 - (wr + wg) 

'get bitmap data and lock it in memory
Dim rect As New Rectangle(0, 0, bm.Width, bm.Height)
Dim bmpData As System.Drawing.Imaging.BitmapData = bm.LockBits
    (rect, Drawing.Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format32bppArgb)  

'iterate over the rows and columns and read the data into an array
For i = 0 To bmpData.Height - 1
       For j = 0 To bmpData.Width - 1
               pixptr = (bmpData.Stride * i) + (4 * j)
               grayval = wb*CType(Marshal.ReadByte(bmpData.Scan0, pixptr), Integer)  'B
               grayval += wg*CType(Marshal.ReadByte(bmpData.Scan0, pixptr + 1), Integer)  'G
               grayval += wr*CType(Marshal.ReadByte(bmpData.Scan0, pixptr + 2), Integer)  'R
               arr(i, j) = grayval
         Next 'j
Next 'i 
'Unlock the bitmap data
bm.UnlockBits(bmpData)
'do some calculation with the data...

b) Writing Image Data to a Bitmap Using VB.NET Works in a Similar Way

  1. Create the bitmap to write to.
  2. Get the bitmapdata and lock it in memory.
  3. Get the pointer to the first byte.
  4. Get the offset of the actual pixel to write to.
  5. Get the offset of the pixel's channel.
  6. Normalize the value to the interval [0, 255].
  7. Write the normalized value to the channel byte.
  8. Unlock the bitmap data.
'create a bitmap with required width and height
bm = New Bitmap(Width, Height)
Dim rect As New Rectangle(0, 0, bm.Width, bm.Height)
Dim bmpData As System.Drawing.Imaging.BitmapData = bmPhase.LockBits
    (rect, Drawing.Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format32bppArgb)

maxval = max(arr)
For i = 0 To bm.Height - 1
       For j = 0 To bm.Width - 1
               'convert into value btw 0 and 255, then convert into byte
                byteval = CByte(Math.Abs(arr(i, j)) / maxval * 255)
                pixptr = (bmpData.Stride * i) + (4 * j)
                Marshal.WriteByte(bmpData.Scan0, pixptr, byteval)  'B
                Marshal.WriteByte(bmpData.Scan0, pixptr + 1, byteval)  'G
                Marshal.WriteByte(bmpData.Scan0, pixptr + 2, byteval) 'R
                Marshal.WriteByte(bmpData.Scan0, pixptr + 3, 255) 'alpha value, 
                                                                  'set transparency to zero
        Next
Next

bm.UnlockBits(bmpData)

c) Writing bitmap in C++/CLI

For comparison, I include the same steps for writing image data in C++/CLI. C++ allows the use of pointers. As a result, the code is shorter and faster. Herein, "col" is a struct that contains the RGB values and "RainbowNumberToColor" computes a color coding. I took this function from C# helper (Map numeric values to and from colors in a color gradientC# Helper (csharphelper.com)).

Bitmap^ output = gcnew Bitmap(width, height);
Imaging::BitmapData^ bitmapData1 = output->LockBits(Rectangle(0, 0, width, height),
Imaging::ImageLockMode::ReadOnly, Imaging::PixelFormat::Format32bppArgb);

IntPtr^ pbm = bitmapData1->Scan0;
Byte* imagePointer1 = (Byte*)pbm->ToPointer();
//compute scaling factor for display
//assume there is a function "max" for computing the maximum value
double max = max(array);
double value;

for (i = 0; i < height; i++)
{
    for (j = 0; j < width; j++)
    {
        //map value into range [0,1]
        value =  array[i,j]/max;
        //apply color coding
        col = Utilities::RainbowNumberToColor(value);

        imagePointer1[0] = (Byte)col.B;
        imagePointer1[1] = (Byte)col.G;
        imagePointer1[2] = (Byte)col.R;
        imagePointer1[3] = 255;
        
        //4 bytes per pixel
        imagePointer1 += 4;
    }//loop over j
    //4 bytes per pixel
    imagePointer1 += (bitmapData1->Stride - (bitmapData1->Width * 4));
}//loop over i
output->UnlockBits(bitmapData1);

Points of Interest

VB.NET does not have pointer arithmetic or unsafe code. As a result, I had to use the read/write functions in the namespace "Marshal" instead. This is somewhat slower than direct pointer arithmetics but still much faster than accessing the pixel object. The functions in the namespace Marshal also help to translate code from C# or C++ which uses pointer arithmetics to VB.NET.

History

  • 25th February, 2021: Initial version
  • 27th February, 2021: C++/CLI example added

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here