NOTE: | Some of the members in the BITMAPHANDLE structure mentioned below are intended for internal use, including pLUT / pLUT16, PaintHighBit and PaintLowBit. These members are used when window-leveling. More information can be found in the LTKRN.H header file, which describes the entire structure. |
12-/16-bit bitmaps can use a Look-Up Table (LUT) for displaying and/or image processing the image. The LUT is optional (it can be NULL). If the LUT is not null, it can be used for display or for display and image processing. The LUT is created by window leveling (L_ApplyLinearVOILUT, L_WindowLevel) or by loading a DICOM or TIFF file with window-leveling information.
There are 3 cases:
1. No LUT
2. LUT exists and is used for display only
3. LUT exists and is used for display and image processing
The LUT is stored in the BITMAPHANDLE using the following fields (the fields are shown in bold):
pLUT
Pointer to an array of L_RGBQUAD values containing the corresponding color for each LUT entry. Usually, the L_RGBQUAD entries contain grayscale colors. But sometimes, the L_RGBQUAD entries contain non-grayscale colors. The number of entries is determined by Flags.UseLUT, LowBit, HighBit, PaintLowBit and PaintHighBit entries in the BITMAPHANDLE structure. If pLUT is NULL and pLUT16 is NULL, then there is no LUT (case 'a' above).
pLUT16
Just like pLUT, except that the array contains L_RGBQUAD16 values and each color value is 16-bit. (16-bit values provide extra precision compared to 8-bit values).
Flags.UseLUT
If TRUE, the LUT is used for display and image processing (case 'c' above). In this case, the number of LUT entries is determined by LowBit and HighBit:
LUT Length = 1 << (HighBit – LowBit + 1)
If FALSE, the LUT is used only for display. In this case, the number of LUT entries is determined by PaintLowBit and PaintHighBit:
LUT Length = 1 << (PaintHighBit – PaintLowBit + 1)
pLUT and pLUT16 are synchronized (that is, they contain the same information). If pLUT == NULL and pLUT16 != NULL, then there is a LUT. Conversely, if pLUT != NULL and pLUT16 == NULL, there is a LUT. If pLUT != NULL and pLUT16 != NULL, there is a LUT and both arrays will point to the same colors and have the same length.
PaintLowBit/PaintHighBit can be different than LowBit/HighBit, but usually they are the same.
Here are the 3 cases again:
1. 'No LUT': pLUT == NULL
2. 'LUT used for display only':pLUT != NULL and Flags.UseLUT == FALSE (or 0)
3. 'LUT used for display and image processing': pLUT != NULL and Flags.UseLUT == TRUE (or 1)
In cases a) and b), image processing will be performed on image data.
In case b), image processing is performed and there is a LUT. The image can look completely different after image processing, so it is best to regenerate the LUT to make sure you see all the details in the new bitmap. Regenerate the LUT by doing something like this:
L_GetMinMaxVal(pBitmap, MinVal, MaxVal);
L_ApplyLinearVOILUT(pBitmap, (MinVal + MaxVal / 2), (MaxVal – MinVal) / 2, 0);
In case c), the image processing would typically take the LUT colors into account. There are several types of image processing functions and the output will vary:
1. The image processing does not change the size, and the output of each pixel depends only on the color of the original pixel (for example, when inverting the colors or changing the brightness). In this case, the image processing will be performed on the LUT colors only and the data will be unchanged
2. The image processing changes the size of the image. In this case, the image processing will be performed on the image data and the LUT entries will be taken into account if appropriate.
3. The image processing does not change the size of the image but the output of a pixel depends on the surrounding pixel colors (for example, the average or other spatial filters). In this case, each pixel is remapped through the LUT to an intermediary 24/48-bit bitmap and the image processing is performed on the intermediary bitmap. After that, for each pixel in the intermediary bitmap the closest value in the LUT is found and that value's index will be the new pixel data.
Some image processing performs well when the LUT is used for image processing, while others perform better if the LUT is ignored. You can choose whichever mode is more appropriate for your application. Here is how you can get to each mode:
1. 'No LUT': L_WindowLevel(pBitmap, pBitmap->LowBit, pBitmap->HighBit, NULL, 0, WINDOWLEVEL_PAINT)
2. 'LUT used for display only': L_WindowLevel(pBitmap, lowbit, highbit, pLUT, LUTLength, WINDOWLEVEL_PAINT)
3. 'LUT used for display and image processing': L_WindowLevel(…, WINDOWLEVEL_PAINT_AND_PROCESSING) or L_ApplyLinearVOILUT.
You can also switch between b) and c) by switching the pBitmap->Flags.UseLUT flag between 0 and 1. But when doing so, make sure PaintLowBit == LowBit and PaintHighBit == HighBit. The above L_WindowLevel calls can also be replaced with L_WindowLevelExt function calls in order to use 16-bit entries for the LUT ().
See Also
Raster .NET | C API | C++ Class Library | JavaScript HTML5
Document .NET | C API | C++ Class Library | JavaScript HTML5
Medical .NET | C API | C++ Class Library | JavaScript HTML5
Medical Web Viewer .NET