Casting an image to a new data type, C#, CVB.NET 13.02.002

I’ve created a small tool that generates a LUT and applies it to an image. The LUT is used to transform an m-bit image into an n-bit image. E.g. a 16-bit image into a 10-bit image, or an 8-bit image into a 12-bit image. For now these images are monochrome. The tool mostly works, except for the following:

The LUT.Apply method seems to return an image that has a data type of either 8-bit, or 16-bit, depending upon the input image data type: E.g. if I input a 10-bit image, it returns me a 16-bit image. If I input an 8-bit image, it returns me an 8-bit image. This causes me two issues.

  1. I’d like to save the output image as an MIO file with the appropriate data type, at the moment, I’m stuck with 8-bit or 16-bit.

  2. This also means I can’t transform an 8-bit image into a 10-bit image, because the LUT.Apply method only returns an 8-bit image if an 8-bit image is inputted.

I can resolve both of these issues if I can cast an image from one data type to another. E.g. I wish to be able to cast an 8-bit image to a 16-bit image without the pixel values changing. Likewise, I wish to cast a 16-bit image to a 10-bit image, but in this 16-bit image, there aren’t any pixel values above 10-bit, so no information should be lost from the cast.

Basically, the point of my question is; how can I cast an image from one data-type to another?

If my post was unclear, let me know and I can clarify.

Many Thanks

EDIT: I should add, the LUT does not perform a simple linear conversion from m-bit to n-bit, so I can’t just multiply the values.

Hi @Pizza_Sub_Pizza,

Im afraid that there will not be an easy ‘casting’ way for what you want to do.
What you can do though is to create a new :cvb:Image with the desired PixelFormat and the use LinearAccess to write the values from your 16bit image into this image.

See:
https://forum.commonvisionblox.com/t/writing-pixel-values-to-an-array/196/7

Cheers
Chris

Hi @Pizza_Sub_Pizza,

@parsd pointed me to an easier way to achieve what you want to do by using the WrappedImage().

See:

using Stemmer.Cvb;
using System;

namespace BitDepthInterpretation
{
  class Program
  {
    static void Main(string[] args)
    {
      using (var sourceImage = new Image(640, 480, 1, PixelDataType.UInt, 16))
      {
        // fill with max 10 bit data
        sourceImage.Initialize(1023);
        // no using here due to the fact that lifetime is bound to sourceImage 
        var wrappedImage = ReinterpreteBitDepth(sourceImage.Planes[0], PixelDataType.UInt, 10);
        // process image
      }
    }

    public static WrappedImage ReinterpreteBitDepth(ImagePlane sourceImagePlane, PixelDataType desiredDataType, int desiredBitDepth)
    {
      if (desiredBitDepth > sourceImagePlane.DataType.BitsPerPixel)
        throw new ArgumentException("Desired bit depth bigger than source bit depth");

      var linearAccess = sourceImagePlane.GetLinearAccess();

      // this is only a view of the source image, thus its lifetime ends with the lifetime of the source image!
      return WrappedImage.FromGreyPixels(linearAccess.BasePtr, 0, sourceImagePlane.Parent.Width, sourceImagePlane.Parent.Height, desiredDataType, desiredBitDepth, linearAccess.XInc.ToInt32(), linearAccess.YInc.ToInt32());
    }
  }
}

hope this helps, I will post an extended version of this to create a MappedImage out of the reinterpreted Images for multiple Planes, so you are not limited to Mono.

Cheers
Chris

1 Like

@Pizza_Sub_Pizza

aaaand the multi planes version:

using Stemmer.Cvb;
using System;
using System.Linq;

namespace BitDepthInterpretationMultiPlanes
{
  class Program
  {
    static void Main(string[] args)
    {
      using (var sourceImage = new Image(640, 480, 3, PixelDataType.UInt, 16))
      {
        // fill with max 10 bit data
        sourceImage.Initialize(1023);

        var reinterpretedImages = sourceImage.Planes.Select(plane => ReinterpreteBitDepth(plane, PixelDataType.UInt, 10)).ToArray();

        // no using here due to the fact that lifetime is bound to sourceImage
        // this does not apply to MappingOption.CopyPixels!
        var mappedImage = MappedImage.FromImages(MappingOption.LinkPixels, reinterpretedImages);
        // process image
      }
    }

    public static WrappedImage ReinterpreteBitDepth(ImagePlane sourceImagePlane, PixelDataType desiredDataType, int desiredBitDepth)
    {
      if (desiredBitDepth > sourceImagePlane.DataType.BitsPerPixel)
        throw new ArgumentException("Desired bit depth bigger than source bit depth");

      var linearAccess = sourceImagePlane.GetLinearAccess();

      // this is only a view of the source image, thus its lifetime ends with the lifetime of the source image!
      return WrappedImage.FromGreyPixels(linearAccess.BasePtr, 0, sourceImagePlane.Parent.Width, sourceImagePlane.Parent.Height, desiredDataType, desiredBitDepth, linearAccess.XInc.ToInt32(), linearAccess.YInc.ToInt32());
    }
  }
}
```cs 

Cheers!