Convert Array to Image

kcwallace

Centurion
Joined
May 27, 2004
Messages
175
Location
Austin, TX
I have an array of data that I would like to turn into an image (*.bmp,*.jpg, etc.) . Any viewable image is fine.

I have already written a method of converting an individual prixel to grayscale.

THank you
 
If it is 4bytes per pixel, so the array is an array of int32s (4bytes color per pixel) then:

create a new bitmap of the correct dimensions and pixelformat
create a bitmapdata object from the image
get the scan0 (start address) of the bitmapdata
use marshal.copy to transfer the array to the bitmapdata object
unlock the bitmap data object

Visual Basic:
Private Function Int32ArrayToBitmap(ByVal pixels As Integer(), _
    ByVal width As Integer, ByVal height As Integer) as bitmap

        Dim bm1 As New Bitmap(width, height, Imaging.PixelFormat.Format32bppArgb)
        Dim bitmapdata1 As Imaging.BitmapData
        bitmapdata1 = bm1.LockBits(New Rectangle(0, 0, width, height), _
        Imaging.ImageLockMode.WriteOnly, Imaging.PixelFormat.Format32bppArgb)
        Dim scan0 As IntPtr = bitmapdata1.Scan0
        ' copy the array starting at 0 to the bitmapData, copy all pixels (wid*hei)
        Runtime.InteropServices.Marshal.Copy(pixels, 0, scan0, width * height)
        bm1.UnlockBits(bitmapdata1)
        'bm1 now contains the image
        'can save with e.g.
        bm1.Save("Blah.jpg", Imaging.ImageFormat.Jpeg)
        Return bm1
    End Function

 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' Test
        Dim array1(320 * 200) As Integer
        Dim randomgenerator As New Random
        For i As Integer = 0 To (320 * 200) - 1
            Dim randColor As Integer
            randColor = (255 << 24) _
            Or (randomgenerator.Next(0, 256) << 16) _
            Or (randomgenerator.Next(0, 256) << 8) _
            Or randomgenerator.Next(0, 256)
            array1(i) = randColor
        Next
        Me.BackgroundImage = Int32ArrayToBitmap(array1, 320, 200)
    End Sub

If you have 3 bytes per pixel then it is different as each row of pixel data has to end on a 4 byte boundary. So it kind of depends on your array / pixelformat.
 
Last edited:
One More question

I was converting the color to gray by doing the following, but is comes out multi colored

Visual Basic:
                GScale = (ODArr(X, Y) / 6 * 256)
                ODArrN(X, Y) = Color.FromArgb(GScale, GScale, GScale).ToArgb

Do you have any suggestions?
 
You need to find the average of the color channels.
Assuming that this is an ARGB image stored in an array of Int32s (Integer), each Int32 holds information for the Alpha, Red, Green, and Blue channels. You probably want to maintain the Alpha channel, but Red, Green, and Blue channels should all be set to the average of the three.

This is the quickest (to code) way that I can think of.
Visual Basic:
Dim RGB As Int32 = OdArr(X, Y)
Dim Alpha As Integer = RGB >> 24
RGB = ((RGB And 255) + ((RGB >> 8) And 255) + ((RGB >> 16) And 255)) \ 3
ODAnnR(X, Y) = Color.FromArgb(Alpha, RGB, RGB, RGB)
I haven't tested this, though.
 
Last edited:
marble_eater said:
You need to find the average of the color channels.
Assuming that this is an ARGB image stored in an array of Int32s (Integer), each Int32 holds information for the Alpha, Red, Green, and Blue channels. You probably want to maintain the Alpha channel, but Red, Green, and Blue channels should all be set to the average of the three.

This is the quickest (to code) way that I can think of.
Visual Basic:
Dim RGB As Int32 = OdArr(X, Y)
Dim Alpha As Integer = RGB << 24
RGB = ((RGB And 255) + ((RGB << 8) And 255) + ((RGB << 16) And 255)) \ 3
ODAnnR(X, Y) = Color.FromArgb(Alpha, RGB, RGB, RGB)
I haven't tested this, though.

THat did not work, it looked like the attached image.
 

Attachments

Hmm the shifts are going the wrong way:

Dim Alpha As Integer = (RGB >> 24) And &HFF
RGB = ((RGB And 255) + ((RGB >> 8) And 255) + ((RGB >> 16) And 255)) \ 3

but still, you shouldn't have any color, the shifts going the other way just clips the lighter greys.

I did it this way to figure out what was going on:
Visual Basic:
    Private Sub ToGreyscale(ByRef pixels As Integer())
        For i As Integer = 0 To pixels.Length - 1

            Dim r, g, b As Byte
            r = (pixels(i) >> 16) And &HFF
            g = (pixels(i) >> 8) And &HFF
            b = pixels(i) And &HFF

            Dim grey As Integer = CInt((0.299 * r) + (0.587 * g) + (0.114 * b))  
            ' Or you could average:
            ' Dim average As Integer = CInt(R) + CInt(G) + CInt(B)
            ' average = average \ 3 ' integer division

            pixels(i) = (255 << 24) _
            Or (grey << 16) _
            Or (grey << 8) _
            Or grey
        Next
    End Sub

The method to get the grey from r, g and b uses an algorithm that emphasises colors that we don't see too well in b&w. I didn't use color.fromargb as it is slower and we are looping through lots of pixels.

Anyway, the color must be coming from somewhere...

What is in your array? Ints or bytes? How does one array item relate to the color of one pixel?
 
jo0ls said:
The method to get the grey from r, g and b uses an algorithm that emphasises colors that we don't see too well in b&w. I didn't use color.fromargb as it is slower and we are looping through lots of pixels.
Um... what? Your average RGB to grayscale conversion is (R + G + B) / 3, i.e. each channel becomes the average of the three. If you are dealing with a 32-bpp image in the form of a 32-bpp array then I had the right idea. I think I just used the wrong operator and the wrong byte order. Check this one out though:
Visual Basic:
'This structure is a union used to access color components and make
'pixels grayscale
<StructLayout(LayoutKind.Explicit)> _
Public Structure ColorConv
    '32-bit color value
    <FieldOffset(0)> _
    Dim ARGB As Integer
 
    'Component access
    <FieldOffset(3)> _
    Dim A As Byte
    <FieldOffset(2)> _
    Dim R As Byte
    <FieldOffset(1)> _
    Dim G As Byte
    <FieldOffset(0)> _
    Dim B As Byte
 
    'Make color gray
    Public Sub MakeGray()
        Dim Gray As Byte = CByte((CInt(R) + G + B) \ 3)
        R = Gray
        G = Gray
        B = Gray
    End Sub
 
    Public Shared Sub SlowMethodToMakeAnImageGrayScale(ByVal Image As System.Drawing.Bitmap)
        Dim ColorConverter As ColorConv
 
        'Loop through pixels, loading them, making them gray, then storing them
        For I As Integer = 0 To Image.Width - 1
            For J As Integer = 0 To Image.Height - 1
                ColorConverter.ARGB = Image.GetPixel(I, J).ToArgb()
                ColorConverter.MakeGray()
                Image.SetPixel(I, J, Color.FromArgb(ColorConverter.ARGB))
            Next
        Next
    End Sub
  
    'This is approximately 2000 times faster
    Public Shared Sub MakeGrayFast(ByVal Image As Bitmap)
        Dim BmpData As Imaging.BitmapData = Image.LockBits(New Rectangle(0, 0, Image.Width, Image.Height), Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format32bppArgb)
        Dim RawData As Integer() = New Integer(((BmpData.Stride \ 4) * Image.Height) - 1) {}
        System.Runtime.InteropServices.Marshal.Copy(BmpData.Scan0, _
          RawData, 0, RawData.Length)
 
        Dim ColorProcessor As ColorConv
        For X As Integer = 0 To RawData.Length - 1
            ColorProcessor.ARGB = RawData(X)
            ColorProcessor.MakeGray()
            RawData(X) = ColorProcessor.ARGB
        Next
 
        System.Runtime.InteropServices.Marshal.Copy(RawData, 0, _
          BmpData.Scan0, RawData.Length)
        Image.UnlockBits(BmpData)
    End Sub
End Structure
dog.jpg
 
Last edited:
I meant this one:
Dim grey As Integer = CInt((0.299 * r) + (0.587 * g) + (0.114 * b))

and I meant that color.fromargb is slower than shoving bits around.
 
Thank you for your help.

I cannot seem to make it work. I keep getting the attached image. The image is a blue shade of what I am trying to create. I am assuming that I am making an error in converting the underlying data into image data. THe underlying data starts out as a 40 x 1024 array of voltages. I then convert the voltages to Optical Density. Next I upsize the image to 1000x 1024 by interpolating the data inbetween the known datapoints. Up until this point I am confident that the caclulations are correct.

I then convert the Optical Density to image data by using the following conversion.

Visual Basic:
    Private Function Norm(ByVal dblVal As Double, ByVal intNormFact As Double) As Double
        Return Math.Round(dblVal / intNormFact * 255)
    End Function

dblVal = the Optical Density from the 40*1024 image array;
intNormFact = the maximum value that dblVal can be.
and I multiply by 255 to convert to a shade of gray because that is the range of inputs allowed into the rgb inputs to define a color.

Am I doing it wrong?
 

Attachments

Back
Top