3

I am developing a program which gets Image from a Camera device. Here is the event how I get the Image.

    private void StreamGrabber_ImageGrabbed(object sender, ImageGrabbedEventArgs e)
    {
        IGrabResult res = e.GrabResult;
        byte[] pixels = res.PixelData as byte[];
        pictureBox1.Image = ByteArrayToBitmap(pixels,res.Width,res.Height, PixelFormat.Format8bppIndexed);

        //ImagePersistence.Save(ImageFileFormat.Jpeg, "tmp/tmp" + i + ".jpg", img);

    }

I can get the Pixel Data Array with the code above. And I can directly save the image with the single line of code.

ImagePersistence.Save(ImageFileFormat.Jpeg, "tmp/tmp" + i + ".jpg", img);

Now my problem starts here. I don't want to save it, I just want to create the Bitmap object from pixel data and simply show in my pictureBox1 object. I have pixel data of image but in any case I couldn't get the image correctly.

Here is ByteArrayToBitmap function;

    public Bitmap ByteArrayToBitmap(byte[] byteIn, int imwidth, int imheight, PixelFormat pixelformat)     
    {
        Bitmap picOut = new Bitmap(imwidth, imheight, pixelformat);  
        BitmapData bmpData = picOut.LockBits(new Rectangle(0, 0, imwidth, imheight), ImageLockMode.WriteOnly, pixelformat);
        IntPtr ptr = bmpData.Scan0;
        Int32 psize = bmpData.Stride * imheight;
        System.Runtime.InteropServices.Marshal.Copy(byteIn, 0, ptr, psize);
        picOut.UnlockBits(bmpData);
        return picOut;      
    }

And here is How image looks like;

GRABBED IMAGE IS HERE

As you can see, image is weird. First I thought if it is the camera's problem. But I have tried it with it's own program, Camera works perfectly. Something going wrong on my code.

I am giving useful informations from debug screen;

The image dimension is 3840x2748.

The size of byte array of pixel data is 10.552.320

I have made that calculation: 3840*2748 = 10.552.320 The image is not grayscale, it is RGB image,

So I Thought that pixel data includes 8-bit-indexed pixel.

I am stuck with this problem. I have tried to give you all useful information about my problem.

How can I get the image and create bitmap object correctly ?

EDIT

    public Bitmap CopyDataToBitmap(int Width, int Height, byte[] data)
    {
        var b = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
        ColorPalette ncp = b.Palette;
        int counter = 0;
        for (int i = 32; i <= 256; i+=32) //for R channel (3 bit)
            for (int j = 32; j <= 256; j+=32)  //for G Channel (3 bit)
                for (int k = 64; k <= 256; k+=64)  //for B Channel (2 bit)
                {
                    ncp.Entries[counter] = Color.FromArgb(255,i-1,j-1,k-1);
                    counter++;
                }
        b.Palette = ncp;

        var BoundsRect = new Rectangle(0, 0, Width, Height);

        BitmapData bmpData = b.LockBits(BoundsRect,
                                        ImageLockMode.WriteOnly,
                                        PixelFormat.Format8bppIndexed);

        IntPtr ptr = bmpData.Scan0;
        int bytes = bmpData.Stride * b.Height;
        var rgbValues = new byte[bytes];
        Marshal.Copy(data, 0, ptr, bytes);

        b.UnlockBits(bmpData);
        Console.WriteLine(b.GetPixel(3648, 1145).ToString());

        return b; 
    }

I have changed algorithm with that function. I am using color palette. But result is still same.

        ColorPalette ncp = b.Palette;
        for (int i = 0; i < 257; i++)
            ncp.Entries[i] = Color.FromArgb(255, i, i, i);
        b.Palette = ncp;

When I use this palette, this time, image becomes Grayscale.

I just want to get clear RGB image.

EDIT 2

My camera's pixel format is BayerBG8. I dont know if it helps.

Furkan KESKIN
  • 168
  • 3
  • 16
  • GDI+ uses BGR order. Maybe you just need to swap blue and red? – Dan Byström Jun 25 '18 at 18:17
  • Not sure but looks like the color is inverted. – Vinod Maurya Jun 25 '18 at 18:30
  • @DanByström I don't know how to do it. I have only pixel data. – Furkan KESKIN Jun 25 '18 at 18:37
  • 8-bit bitmap has a color palettet. The palette size should be 256 * 4. But then again, it's unusual that a high resolution camera would use 8-bit bitmap. Maybe it's 24-bit `size = (width * 3 + padding) * height` - show what the real image is supposed to look like. – Barmak Shemirani Jun 25 '18 at 21:00
  • @BarmakShemirani I have a color palette with size of 256. After your comment, I tried to extend it to 4*256=1024, It throwed ArgumentException and exception says, palette cannot bigger than 255 and cannot less than 0. Camera's own library shows image correctly with same data with IGrabResult.Display(); code. But I couldn't. I am really really stuck in here. – Furkan KESKIN Jun 25 '18 at 22:00
  • It's 256 color. Each color is usually 4 bytes, so the size in bytes is 1024. I don't see anything relating to palette/color-table in the code you have shown. – Barmak Shemirani Jun 25 '18 at 22:43
  • @BarmakShemirani I have edited the code with my palette. – Furkan KESKIN Jun 26 '18 at 08:46

1 Answers1

1

8-bit bitmap only uses 256 colors, it's not useful in any modern camera. The colors have to be supplied by whoever made the image. Each pixel is one byte (or 8-bits), it's a value from 0 to 256, it refers not to a color, but to an index in the color table.

for (int i = 0; i < 256; i++)//change to 256
    ncp.Entries[i] = Color.FromArgb(255, i, i, i);

This basically makes a random color table. The odds that it's the right color table is 1/(256*256*256)


BayerBG8 is raw format, it has nothing to do with 8-bit bitmap. See description

Each pixel is represented with 4 color detectors. There are only 3 colors for each pixel, so they put an extra Green detector.

The first row is blue green blue green ...
The next row is green red green red ...

BGBGBGBG...
GRGRGRGR...
BGBGBGBG...
GRGRGRGR...

You have to take a 2x2 square and get an average color, representing 1 pixel:

piexel 1: BG    piexel 2: BG ...
          GR              GR ...

Starting from 3840x2748, you end up with 1920x1374 pixels.

Each color can be represented with 3 bytes. Now you have:

BGR BGR BGR BGR BGR BGR //RGB is stored backward as BGR

That's a total of 5760 * 1374 bytes. The example below uses a simple approximation. There are two green values for each pixel, we take an average of the two green values.

int src_width = 3840;
int src_height = 2748;

int dst_width = src_width / 2;
int dst_height = src_height / 2;

byte[] destination = new byte[dst_width * dst_height * 3];
int pos = 0;
for(int row = 0; row < src_height; row += 2)
{
    for(int col = 0; col < src_width; col += 2)
    {
        int i = row * src_width + col;
        //blue from this line
        destination[pos++] = source[i];

        //green average of two greens from this line and next line:
        destination[pos++] = (source[i + 1] + source[i + src_width])/2;

        //red from the next line
        destination[pos++] = source[i + src_width + 1];
    }
}

Use destination with Format24bppRgb, size 1920x1374

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • 1
    Finally I get the RGB image thank you so so much. When I save images from its own program, it saves with 3840x2748 dimension. I have googled and found that https://www.baslerweb.com/en/sales-support/knowledge-base/frequently-asked-questions/bayer-to-rgb-and-missed-pixel-value-at-last-row-and-line/15245/ I think we are losing half of pixels by your algorithm. What do you suggest? – Furkan KESKIN Jun 27 '18 at 00:53
  • I updated the algorithm to use all the data from raw image. I don't know what the final image looks like. It probably needs additional color correction, anti-aliasing, etc. which you can do in .net – Barmak Shemirani Jun 27 '18 at 02:24
  • @FurkanKESKIN Yeah, Bayer works with a sliding window, not just with 2x2 blocks. It moves, both in X and Y, by a single byte, meaning your final result is only one pixel less than the original amount of bytes. I answered a similar question before that also turned out to be about Bayer; you can check the answer [here](https://stackoverflow.com/a/50038800/395685). – Nyerguds Jun 28 '18 at 13:02