Generating a BitmapSource image from a SourceData object is easily done using one of two methods: either the data within a single channel is visualized, or several channels within a multichannel SourceData object are combined to generate a color RGB image. In both cases the scaling of the SourceData values to their associated output grayscale values can be adjusted using the OutputWindowing type.
BitmapSource images can be generated from any channel in a SourceData object at any time by invoking one of the image generating methods: GetChannelImage and GetChannelImageArray. Both methods generate images and return either a BitmapSource or a object consisting of pixel values.
The simplest version of SourceData.GetChannelImage takes five arguments - of which only the first two are required - and returns a BitmapSource image:
- GrayBitDepth outputBitDepth specifies the output bit-depth of the generated grayscale image. Possible choices are EightBit, SixteenBit and ThirtyTwoBit.
- channel specifies the data channel to visualize. This is a zero-based index value (the first channel is 0, the second channel is 1, etc).
- GammaEncoding gammaEncoding specifies the type of gamma encoding to apply to the output image. Possible choices are None and sRGB. The default value is GammaEncoding.None, meaning that the data values are linearly scaled to the specified grayscale range.
- OutputWindowing outputWindowing specifies the lower and upper clipping values of the output image. Values that fall within this range are scaled to gray in the output image. All other values assume either black or white, depending on whether the value falls below the lower clipping point or above the upper clipping point. The default value for this argument is null, indicating that the entire data range within the data channel is scaled to gray in the output image.
- Int32Rect regionOfInterest specifies the Cartesian region of interest in the data channel to visualize. The upper left-hand corner of the data is defined as the origin, positive X direction progresses to the right and positive Y direction progresses downward. The default value is to set the region of interest to the entire data channel.
As a first example, assume that a SourceData object called "imageData" has been instantiated and initialized with data values. To generate a linear 8-bit grayscale BitmapSource image from the data contained in the first channel of "imageData" simply do the following:
Alternatively, to apply gamma encoding to the returned BitmapSource image you would do the following:
In some cases it is desirable to retrieve the image as an array of pixel values. This is especially true when using a WriteableBitmap object to efficiently update the image. In these cases you can invoke the version of the method that returns a System.Array object. The returned array type is a function of the GrayBitDepth enumeration specifier and is a one-dimensional array of bytes, unsigned 16-bit integers or 32-bit floating point values with the image scanlines beginning at the data origin and progressing downwards. For example, the following method invocation
will scale the entire data range in the first channel of imageData linearly (no gamma encoding) to an unsigned 16-bit range ( 0 - 65535) and return the results in the form of a one-dimensional System.Array object called "pixels". This System.Array object can then be used to efficiently update a WriteableBitmap image.
Generating a grayscale image of a subset of the full data range is a very common operation. This is typically referred to as "windowing" and is especially helpful when data contains noise and other outlier values that suppress the rendering of the data of interest. In PrecisionImage.NET this is accomplished using the OutputWindowing class. The OutputWindowing class is used to specify the lower and upper clipping levels to use when depicting the data within a given data channel. An OutputWindowing object can contain the clipping values for any number of channels.
For example, if the first channel of "imageData" contains values that span the range of 0.0 - 1.0 and it is desirable to generate a grayscale image based only on the values within the subset range of 0.25 - 0.5, you would first create an OutputWindowing object and associate the clipping values with the channel of interest as follows:
The above window specification forces the grayscale mapping to within the range of 0.25 to 0.5 (inclusive). Source data values that fall below or above this range are rendered as either black or white, respectively. You can then generate a BitmapSource image (or retrieve a System.Array using the alternative form of the method) using this windowed range by passing this object to the GetChannelImage method:
Keep in mind that any combination of function arguments can be used when invoking these methods. As an example, assuming that "imageData" has 1024 rows and 2048 columns and you wish to generate an image based on a centered quadrant of the data with the previous windowing range, you would do the following:
Lastly, there are several static methods defined in the OutputWindowing class that return windowing objects initialized to default ranges for common image bit-depths: GetDefault8BitOutputWindows (MinClip = 0, MaxClip = 255), GetDefault16BitOutputWindows (MinClip = 0, MaxClip = 65535) and GetDefaultNormalizedOutputWindows (MinClip = 0, MaxClip = 1). The GetAutoContrastWindow method can be used to retrieve an OutputWindowing object with automatically computed clipping values to suppress the effect of data outliers on the grayscale image mapping.
Image generation is not limited to single channel grayscale types. Multiple data channels within a SourceData object can be combined to generate RGB color images. To generate color images the GetBitmapSource method is used in conjunction with a PixelFormat property and independently assigned red, green and blue data channels. Optionally, multichannel OutputWindowing objects can also be used to control the output scaling of each data channel independently. The SourceData.GetBitmapSource method has the following definition:
These method arguments have the same meaning and are used in the same way as described in the previous "single channel image generation" section of the software guide. All three arguments are optional.
If a SourceData object has been instantiated using a BitmapSource image, then the PixelFormat of the initializing BitmapSource image is internally associated with the new SourceData object by default. To change this PixelFormat, or if you wish to associate a PixelFormat with a SourceData object that was instantiated as an empty object (i.e. without a BitmapSource argument) you can do so by using the AssignOutputPixelFormat method. For example, to associate a PixelFormat type of Rgb48 with the "imageData" object you would do the following:
You can then invoke the SourceData.GetBitmapSource method to retrieve the color image:
This will return a 16-bit per channel RGB BitmapSource of the entire data set (i.e. no region of interest) with each color channel scaled linearly (i.e. no gamma encoding) to the associated data channel's full range ( i.e. no output windowing) for each of the RGB channels.
During instantiation, the data channels of a SourceData object are internally assigned the same order as the red/green/blue/alpha channels of the initializing BitmapSource argument. If a SourceData object is instantiated without a BitmapSource argument, or if a single-channel BitmapSource argument is passed to the SourceData constructor, then the red/green/blue indices are all assigned to the first data channel in the SourceData object by default (channel 0). Also, in this case, the alpha channel index is assigned to a non-existent data channel (channel -1) to indicate no initial alpha channel association. This behavior can be easily overridden if desired. The RedChannelIndex, GreenChannelIndex, BlueChannelIndex and AlphaChannelIndex properties are used to get/set the color channel indices of a SourceData object when generating a BitmapSource image. These properties do not modify the underlying data, they only associate data with a given color channel during image generation. These properties are not mutually exclusive i.e. the same data can be associated with more than one color channel. When generating an alpha-containing multichannel image via the GetBitmapSource method, an optional fixed-alpha value can be specified which is applied to the entire image (in other words, the alpha blending would be spatially uniform). If no value is specified and the AlphaChannelIndex points to an existing data channel when invoking the method , the data pointed to by AlphaChannelIndex is used to source the alpha channel values. To dissociate any data from the alpha channel, set the AlphaChannelIndex property to -1, or assign a value of 1.0 to the fixedAlphaValue argument of the GetBitmapSource method.
As a first example, consider the case where a SourceData object called "imageData" has been instantiated based upon an 8-bit grayscale image ("bitmapSource") as follows:
The above generates a single-channel SourceData object containing the values of the original image normalized with the gamma encoding removed. Since this was instantiated using a single-channel source image, the values for the red/green/blue indices are all initialized to the first (and only) data channel, that is, channel 0. The alpha index is initialized to a value of -1 indicating no data association. A grayscale visualization of the data in channel 0 can be generated as follows:
The above code generates an 8-bit grayscale BitmapSource of the data in channel 0 with the gamma encoding re-applied (via the GammaEncoding.sRGB specifier). However, if the output pixel format associated with the "imageData" was changed from the original Gray8 to Bgra32 using the AssignOutputPixelFormat method and a BitmapSource image was generated as follows:
the resulting image would be a Bgra32-type image identical in appearance since the red/green/blue channels all have the same data channel index value of 0. Furthermore, since no fixed alpha value was specified when invoking the method, the image is generated with a default alpha of 1.0 (full opacity). Alternatively, if the method had been invoked as follows:
the image would be generated with a uniform alpha value of 0.5. Finally, if the method were to be invoked like this:
the image generated would have a spatially-varying alpha channel based on the data contained in channel 0.
GetBitmapSource takes an optional OutputWindowing argument for independently adjusting the windowing of the color channels. To utilize this feature the OutputWindowing object must contain the lower and upper clipping specifications for the given channel. For example, assume "imageData" was initialized with a BitmapSource object of type Rgb48. To generate an output BitmapSource with the red channel windowed from 0.25 - 0.75 and the blue channel windowed from 0.0 - 0.5, you would create the following OutputWindowing object for use in the function call:
The windowed color image is then generated by passing "dataWindows" to the GetBitmapSource method:
This returns a BitmapSource object of type Rgb48. The values in the first channel (channel 0) that fall between 0.25 and 0.75 are linearly assigned (i.e. no gamma encoding) to the red channel of the image. Since "dataWindows" contains no output windowing specification for the second channel (channel 1), the full range of the data values in that channel are linearly assigned to the green channel. Lastly, all values in the third channel (channel 2) that fall between 0.0 and 0.5 are linearly assigned to the blue channel.