Hexagon Geospatial
MENU

Who rated this article

Learn more about our products, find answers, get the latest updates, and connect with other Hexagon Geospatial product users, or get support from our professional service team.
Showing results for 
Search instead for 
Do you mean 

Stretch Image through Saved Lookup Table (LUT)

by Technical Evangelist ‎11-20-2015 10:27 AM - edited ‎04-14-2016 08:47 AM (5,061 Views)

Download model

Description:

When an image is displayed in an ERDAS IMAGINE View, it is generally stretched through a Lookup Table (LUT) to map DN values to screen brightness values in order to maximize the ability for the observer to visually interpret the data. By default this is applied automatically based on Preferences to map through a Standard Deviation or Percentage clipping LUT. However, sometimes this default stretch is insufficient and the LUT is manually manipulated using the contrast tools to produce a better visual display. In order for the data to be redisplayed at a later date with the same appearance, this LUT can be saved into the image's header (by clicking the Save icon on the Quick Access toolbar, for example). Then, next time the image is displayed, the saved LUT will be used instead of the Preference-driven statistical defaults.

 

Sometimes it is necessary to physically stretch imagery to an 8bit range (for example to "style" 16bit image data for use in applications which don't support 16bit data) using specific LUTs. This Model stretches three bands of an input image through a Lookup Table that has previously been saved into the header of that image. It produces a three band RGB image stretched into the 8bit (0 to 255) range with a "No Stretch" LUT saved into its header to force display to look like the original image would have displayed through the saved LUT. The model could be easily modified to stretch a single Panchromatic band (or to stretch other numbers of bands).

 

Note that the Model will fail if any of the input bands do not have a saved LUT (Contrast attribute).

 

 SavedLUTStretch-v15-0-4.gmdx
 SavedLUTStretch.PNG

 

         

Input parameters:

Raster In: Input image which has had a LUT saved into the header.

Red Band: Input band number to send to the Red component of the desired RGB result. E.g. 1. A single value can be entered and the ERDAS IMAGINE dialog will automatically convert it to the expected Range List (e.g. 1 will become 1:1)

Green Band: Input band number to send to the Red component of the desired RGB result. E.g. 2

Blue Band: Input band number to send to the Red component of the desired RGB result. E.g. 3

Raster Out: Output 8bit image with DN values stretched through the LUT saved in Raster In.

Skip Factor: Statistics approximation factor. A value of 4 will increase processing speed, but may result in an output "NoStretch" LUT which does not encompass the entire stretched image data range. A value of 1 will result in slower processing, but guarantees the full range is encapsulated by the created LUT.

 

 SavedLUTStretch_dialog.PNG

   

Why Does It Do That?

In drafting this Article it struck me that it may not be at all obvious why the Model is constructed in the way it is (especially for such a seemingly simple task). So this section goes into detail on the thinking behind the way the Model is built. Hopefully this will give you ideas on how to build your own Models and how to best modify this one to your particular requirements. Let the Team know if you like this level of detail.

 

The first step in the Model is to split the processing into the specific bands to be stretched. This is achieved using

 the Band Selection operator and is performed because operators such as Attribute Lookup (which is used to convert each DN value to its associated Contrast Table value) only operate on a single band at a time. This could be simplified in ERDAS IMAGINE 2016 using the Iterator operator to repeat the same process on different inputs, but in ERDAS IMAGINE 2015 it means we need to branch into three near-identical branches of the Model to individually process each of the three bands to be stretched.

 

As mentioned above, the Attribute Lookup operator deals with converting the input DN values into corresponding floating point contrast values as defined by the saved LUT in the input image's header. A contrast table converts to floating point values between 0 and 1, so to achieve the desired unsigned 8bit output data type the raster stream is then Multiplied by 255 and fed through a Round operator.

 

At this stage of the Model (just under half way from left to right in the Model screenshot above) the LUT stretching is pretty much complete and proceeds down to the Stack Layers operator to put the individual processing branches back together into a three band image. The majority of the remaining branches to the right of the Round operators deal with constructing a "No Stretch" Lookup Table to save into the header of the output stretched image file.

 

Constructing the LUT for the output image is more complex than it sounds. You'd think that, because the output is stretched to an 8bit range, you simply create a LUT with 256 bins and a linearly mapped value (from 0 to 1) for the contrast. In many cases that would suffice. However, sometimes the stretch that was saved into the original input image's header didn't map to the full 8bit, 0 - 255, data range. Consider a 2 Standard Deviation stretch constructed for data with skewed histograms. Sometimes this can result in a LUT that maps "off the end" of the data because - (or +) n SDs is a value below the minimum data value, so the stretch maps the minimum image data value to a brightness value greater than 0. Once stretched through the LUT the resulting output minimum value is also greater than 0 (say, 23). Due to the manner in which ERDAS IMAGINE bins the attribute information for the data (and due to the way this has varied over time and also with different image data formats), more complex processing has to occur in the Model to determine exactly how to construct the "No Stretch" LUT for the output image.

 

This is achieved by calculating Statistics for the stretched data values in order to determine the minimum and maximum data values. Since statistics calculation is a time consuming process, a Skip Factor can be specified by the user. A Skip Factor of 4, for example, would sample every fourth column and row to accumulate statistics, thereby increasing processing efficiency. However, by sampling, some accuracy might be lost (in the LUT, not in the data itself). Consider, for example, a stretched band that has a range of 23 to 253. Only a smattering of pixels might have the minimum value of 23 and the sampling might therefore not sample any pixels with value 23. This might therefore result in the Statistics operator returning a Minimum value of 24 rather than the true minimum of 23. If this is a concern, enter a Skip Factor of 1 to ensure that all pixels are considered when accumulating the statistics.

 

With the Minimum and Maximum pixel values known (approximately, if a Skip Factor other than 1 was used), the Model proceeds to calculate the data range of each band (Max - Min + 1). This is used in the Dynamic Table Input (Series) operator to construct a table with as many rows as the range of the stretched band, for example 253 - 23 + 1 results in a table of 231 rows.

 

The same operator also populates this table with values that will ultimately become the Contrast values in the output image's header. The starting value (for the first row in the table) is calculated as the Minimum divided by the output data type (8bit) maximum, e.g. 23 / 255 or 0.0902 (remember - Contrast is a floating point value between 0 and 1, but we aren't starting from 0).

 

An increment is calculated as 1 / 255 to count up into each subsequent row of the table from that starting value.

 

In this example we therefore end up with a Table of 231 rows and populated with values starting at 0.0902 and proceeding linearly up to 0.9922 in the 231st row.

 

This happens three times, once in each branch of the Model for the separate bands, thereby constructing three tables. These are assembled by the Create Column(s) operator into layer-specific attribute columns called Contrast and the Attach Attributes operator subsequently attaches these to the 3-band raster stream produced by the Stack Layers operator.

 

Thus, no matter if the stretched DN values of the output don't cover the entire 0 to 255 8bit range, a LUT is saved into the header of the output image which will directly map the DN value to screen brightness value and thereby emulate the appearance of the original input image (displayed through its own LUT).

 

But we're not quite done. The input imagery might have had a NoData value defined, or might have ignored a background value of 0 in its original statistics (and therefore in its original LUT), so the last couple of steps of the Model address this. If the input imagery had a NoData defined, that information will be carried through the Model processing as a NoData Mask and, because NoData is present in the process, Spatial Modeler will attempt to preserve NoData in the output . However, on outputting an unsigned 8bit image (probably in IMG format) there's a dilemma since there are only 256 values which are candidates for value-based NoData. By default 0 is going to be assigned for value-based NoData and pixels that fell under the NoData Mask will be assigned to that value. However, the LUT through which the data was stretched may also have stretched valid (non-NoData) pixels to a value of 0. These pixels would also become NoData in the output, which is generally an undesired outcome.

 

Consequently, the penultimate two Operators in the Model apply a test to see if a pixel Equals 0, and if true the Either/Or operator writes out a 1, or outputs the input value otherwise. I.e. 0s get converted to 1s and all other values stay the same.

 

The Raster Output then creates the output file containing the stretched data (with a LUT written into the header to cause the data to display, by default, with "No Stretch") and applies the NoData Mask. Applying the NoData Mask ensures that any pixels that were originally NoData, but which might have gotten stretched to 1s, get set back to NoData, while valid pixel locations keep their new values.

 

If NoData or background 0 retention aren't applicable to your data, you can remove, or modify the behavior of, the Equals and Either/Or operators.

Who rated this article