Search code examples
c#bitmapcamerabasler

Can't convert Basler's Pylon IImage.PixelData to C# Bitmap


Code below. At the step where I create the Bitmap, I get the "Parameter is not valid" exception. I'm guessing the grabResult.PixelData is not in a format that C# can convert to an image, but I'm not sure how to bridge that gap. I've confirmed the camera is taking pics in Mono8.

            IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(5000, TimeoutHandling.ThrowException);
            using (grabResult)
            {
                if (grabResult.GrabSucceeded)
                {
                    byte[] buffer = grabResult.PixelData as byte[];
                    using (var ms = new MemoryStream(buffer))
                    {
                        ms.Seek(0, SeekOrigin.Begin);
                        var bmp = new Bitmap(ms);
                    }
                }
            }

Solution

  • There are two general ways to represent images as bytes. Using some type of file-format, like .png, .jpeg etc. These are typically compressed to reduce the size, and includes various types of metadata.

    The other, that basler uses, is raw pixel data. To make any use of this you need metadata, like the image width, height, stride and pixel format to correctly reconstruct the image.

    Luckily, the grab result should contain everything you need. But there is another possible problem with color images, the camera may produce images in some odd color space, and may not even be demosaiced. So you may need to convert it to regular 8 bit RGB.

    The code to do this should look something like:

    var bitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format24bppRgb).
    var bitmapData = bitmap.LockBits(
        new Rectangle(0, 0, target.Width, target.Height),
        ImageLockMode.ReadWrite,
        bitmap.PixelFormat);
    try
    {
        var converter = new PixelDataConverter()
    {
        OutputPixelFormat = PixelType.BGR8packed
    };
        converter.Convert(
            bitmapData.Scan0,
            bitmapData.Stride * bitmapData.Height * 3,
            grabResult.PixelDataPointer,
            grabResult.PayloadSize,
            grabResult.PixelTypeValue,
            grabResult.Width,
            grabResult.Height,
            grabResult.PaddingX,
            grabResult.Orientation
        );
    }
    finally
    {
        target.UnlockBits(bitmapData);
    }
    

    Pay extra attention to the "stride". This is the number of bytes on each row of pixels. This may be larger than the number of pixels multiplied by the number of bytes per pixels due to alignment requirements. Pylon apparently uses "padding" to describe these number of extra bytes. I would expect the converter to take a target stride or padding, but I cannot see any such parameter.

    Also note that the Format24bppRgb and pylons BGR8packed means the same thing due to Byte order issues. This is unfortunately confusing.

    There might also be some .ToBitmap() conversion method provided by the library for convenience. I have not studied the documentation in detail, and we use the raw pixel data anyway.