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.
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.
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.
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 Image with the desired PixelFormat and the use LinearAccess to write the values from your 16bit image into this image.
@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.
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!