Image Enhancement Using Palettes | Lab Report |
Current Address: Perform on the fly, flicker free gray-scale image enhancement |
||
Purpose
The purpose of this program is to demonstrate how to use a
palette with a pf8bit bitmap to achieve brightness and contrast enhancement as well as achieving binary and full spectrum thresholding on the fly and in a flicker free manner. Using a pair of scroll bars, the user can adjust the contents of a 256 gray scale palette. The program
quickly reassigns the palette to the bitmap giving the appearance of on the fly image processing.
Background
While developing a medical image processing application, I was
interested in providing flicker free, on the fly contrast and brightness
enhancement. Such images can have pixel values ranging from -1000 to 20000. My
original solution was to create a lookup table that mapped the image values
stored in a Width * Height
buffer array down to a 256 gray scale pf8bit Bitmap. This can be done by
calculating the coefficients in the equation
x' = C * x + B ; 0 <= x' <= 255
where x' is the modified pixel value, x is the original pixel value, C is the contrast, and B is the brightness. Suppose we have an image and we find the Minimum and Maximum pixel values. C and B are
C = 255 / (Max - Min)
B = -C * Min
It is easy to show that if x = Max, x' = 255 and similarly when x = Min, x' = 0. The Lookup table is a one dimensional integer array having length Max – Min, with values defined such that
x' = Lookup( x - Min ) = C * x + B ; Min <= x <= Max
Then the image Buffer can be mapped to the display Bitmap using
Bitmap( i , j ) = Lookup[ Buffer( i , j ) - Min ] ; 0 <= i <= W , 0 <= j <= H
Now consider the case of full spectrum thresholding, which can be used to segment regions of an image having similar intensity values. A simple modification to the linear mapping function is accomplished by replacing x with f(x):
x' = C * f(x) + B
where f(x) is a range checking function between the desired limits Lower to Upper:
f(x) = x ; L <= x <= U , otherwise f(x) = Min
In order to achieve binary thresholding, change B to 0, C to 1 and f(x) to
f(x) = 255 ; L <= x <= U , otherwise f(x) = 0
In addition to thresholding, several non-linear mapping functions are provided in the program as alternatives to the original linear transformation described above. In the following definitions, assume that the values of x are already scaled between 0 and 255 (i.e. Min = 0, Max = 255). The Gaussian map is defined such that C = 255, B = 0 and
f(x) = Exp[ ( c - x )/(2 * s * s) ] ; 0 <= x , c <= 255 , 0 <= f(x) <= 1 , 0 < s < 255/2
Here, Exp is the exponential function, c is the center of the symmetric Gaussian curve, and s is a measure of the width of the curve. The Cosine map is defined with
f(x) = Cos[ Pi * ( x - c )/s ] ; c - s/2 < x < c + s/2 , otherwise f(x) = 0
0 <= c <= 255 , 0 < s <= 2 * 255
Again, c is the center of the symmetric positive half of a cosine curve, and s is a measure of the width of the curve (C = 255, B = 0). The Triangle map is a piecewise linear mapping function defined over two ranges of x as
f(x) = 2 * ( x - c - s/2 )/s ; c - s/2 < x <= c
f(x) = 2 * ( c + s/2 - x )/s ; c < x < c + s/2 , otherwise f(x) = 0
0 <= c <= 255 , 0 < s <= 2 * 255
with analogous definitions for c and s (C = 255, B = 0). The preceding definitions are provided as a demonstration of mapping in general and do not necessarily produce useful or practical results for a particular image. However, they can provide aesthetically interesting effects. Try toggling back and forth between the standard brightness/contrast transformation and the other functions set at their initial, default positions. In particular, observe the image appearance and note that the Triangle map gives the same results as the brightness/contrast operation. Change the location of the 'center' slider to position 0. This produces an inverted or negative image.
In any event, every time we change C or B for a given f(x), the process involves a two-step procedure of calculating a lookup table and then mapping the image values to a bitmap. This is time consuming, since we must loop through Max - Min + W *H addresses. Thus, for large, broad spectrum images, it can become difficult to achieve image modifications in apparent real time.
Materials and Equipment
Software Requirements
Windows 95/98/2000/NT
Borland C++ Builder 4 (to recompile)
PalettePrj.EXEHardware Requirements
800 by 600 display in 256 colors, 16, 24 or 32 bit color mode
Procedure
Discussion
A solution to image modifications in apparent real time is to use a pf8bit
bitmap for display purposes
and a logical color palette (LOGPALETTE) structure (solution from Patrick M.
Martin, posted 7/17/00 on borland.public.cppbuilder.graphics). This structure
contains the number and an array of PALETTEENTRY structures which define the
color and usage of each entry in the logical palette (ref. Win32 Programmer's
Reference). The PALETTEENTRY structures contain 4 Byte fields, the most
important of which are peRed, peBlue, and peGreen. Define a 256 gray scale
palette and assign it to a temporary bitmap:
Graphics::Bitmap* aBitmap = new Graphics::Bitmap(); aBitmap->PixelFormat = pf8bit; aBitmap->Width = 10; aBitmap->Height = 256; LOGPALETTE* pal = NULL; try { pal = (LOGPALETTE*) malloc( sizeof(LOGPALETTE) + \ sizeof(PALETTEENTRY) * 256); pal->palVersion = 0x300; pal->palNumEntries = 256; for(short i = 0 ; i < 256 ; i++) { pal->palPalEntry[i].peRed = (Byte) i; pal->palPalEntry[i].peGreen = pal->palPalEntry[i].peRed; pal->palPalEntry[i].peBlue = pal->palPalEntry[i].peRed; } HPALETTE hpal = CreatePalette(pal); if(hpal) { aBitmap?>Palette = hpal; //save a backup of the palette for later GetDIBColorTable(aBitmap->Canvas->Handle, 0, 256, OldPalette); } } __finally { delete pal; } |
It is a simple matter to retrieve or set the palette structure array in a pf8bit Bitmap with the Windows API functions: GetDIBColorTable(Bitmap->Canvas->Handle, 0, 256, OldPalette) and SetDIBColorTable(Bitmap->Canvas->Handle, 0, 256, NewPalette). OldPalette and NewPalette are 256 element arrays of RBGQUAD structures (similar to a PALETTEENTRY structure). Now, create a gray scale bar using a TImage component which displays the contents of the current palette:
Byte* ptr; for(short y = 0; y < 256; y++) { ptr = (Byte*)aBitmap->ScanLine[y]; for(int x = 0; x < aBitmap->Width; x++) ptr[x] = y; } GrayScale->Picture->Assign(aBitmap); delete aBitmap; |
Whenever B or C are modified, as with a scroll bar, create a new color palette:
void __fastcall TForm1::ScrollBarChange(TObject *Sender) { short i = 256; while(i--){ short Val = (short)(i*Contrast+Brightness); NewPalette[i].rgbBlue = (Byte)(Val > 255 ? 255 : (Val < 0 ? 0 : Val)); NewPalette[i].rgbGreen = NewPalette[i].rgbBlue; NewPalette[i].rgbRed = NewPalette[i].rgbBlue; } SetDIBColorTable(GrayScale->Picture->Bitmap->Canvas->Handle, 0, 256, NewPalette); GrayScale->Invalidate(); //repaint the image immediately } |
Now there are only 256 addresses to calculate. Note that x represents the default gray scale palette value, i, while x' is the new palette value, Val. Flicker is easily removed from the image by setting the form's ControlStyle in its constructor with ControlStyle << csOpaque.
The attached zipped project shows how to read in a general color bitmap and display it as a 256 gray scale image. The coding of the various effects is handled in a modified version of the last code example.
Conclusions
Modifications to gray scale images can be achieved on the fly
without flicker by changing color table entries, assigning them to the handle of
a bitmap's canvas, and then invalidating the image. The technique can easily be
extended to 8 bit color bitmaps by modifying individual color planes
independently.
References
Discussions on thresholding and image transformations in general can
be found in:
Ramesh Jain, Rangachar Kasturi, Brian G. Schunck, Machine
Vision, McGraw-Hill, 1995.
Additionally, implementation details and code examples can be found in:
Douglas A. Lyon, Image
Processing in Java, Prentice Hall PTR, 1999
Keywords
256 gray scale, pf8bit Bitmap, Palettes, Brightness, Contrast, Thresholding,
GetDIBColorTable, SetDIBColorTable, ScanLine, PixelFormat, LOGPALETTE,
PALETTEENTRY, RBGQUAD, Builder
Download
Borland C++ Builder source and EXE (230 KB):
ImageEnhancementUsingPalettes.ZIP
Copyright (C) 2000 by Dean
Inglis. All Rights Reserved.
Reproduced here with permission.
Updated 18 Feb 2002
since 29 Aug 2000