The Problem

Recently, one of our customers gave us a raster that looked absolutely black in the FME Viewer:

User-added image

Closer inspection shows that the pixels have a good variety of values, so why are the contents not visible?

Let's have a closer look at the information window under "Raster Point Info" - we see that we have an image consisting of three 16 bit bands, what defines an RGB48 interpretation. This means that the range of available values for each band goes from 0 to 65535. The actual values on our image do not exceed a few hundred.

User-added image

In other words, this raster contains 8-bit values stored in 16-bit bands.

The completely black display is partly due to how the FME Universal Viewer interprets the data; 0 is black and 65535 is white, so even a value of 256 is not going to register very highly. But, really, this is a problem with the data. We need to set the correct interpretation on the existing values or adjust the values to match the current interpretation.

Solution 1: RasterExpressionEvaluator

One possible solution is to use the RasterExpressionEvaluatorWe can apply the following band list and expression:

RED16;GREEN16;BLUE16
A[0]*200;A[1]*200;A[2]*200

However, this requires some knowledge of this advanced transformer and a few experiments to find an optimal multiplier.

Solution 2: RasterInterpretationCoercer

Another solution is to use the RasterInterpretationCoercer, which may be a better choice for this situation. It contains four conversion options, giving users much better control over the whole process.

Conversion options

The conversion options include:
  • Cast
  • Bounded Cast
  • Scale by data values
  • Scale by data type

If we change the interpretation of our raster from RGB48 to RGB24 using 'Scale by data value' option, we will get the following nice and clear image:

User-added image

Why did I pick this option, and how do these options work?

You can find the explanation how these options work in the transformer's documentation, but honestly speaking, it doesn't look clear enough to me - I don't have C-programming or, more general technical background to understand easily "overflows and roll overs" of the values, so I decided to make a few experiments for myself and show results for those who prefer a good visual explanation.

I will go through all the options and show how they work, and how you can pick the right one. For this purpose, I prepared a simple contour file and made a profile (brown, made with ProfileBuilder) along the red line:
User-added image

Based on contour data, I generated a one band DEM raster and made two tiles. The DEMGenerator makes Real64 rasters, and I am going to try to make it UINT8 - I know that rasters can be really big and want to save on disk space.

Now, before we continue with the conversion modes, let's have a look at the parameters. There are four of them where we pick the conversion mode. Why do we have so many? That's because Workbench has no way of knowing ahead of time what type of rasters will be passed through this transformer - whether it will be a color image (e.g. RGB24, RGBA64, Green16, etc.) or a numeric raster (Real64, Int32, etc.), and our destination can be either color or numeric. All possible conversion combinations are listed as separate parameters (we probably could have just two conversion parameters ("From Color" and "From Numeric") but for backward compatibility we had to have four). Based on our choice in "Destination Interpretation Type" only two of four parameters can be enabled.

User-added image

Let's try to send one of the tiles through the transformers using "Cast" method:

Before    
User-added image
After
    User-added image

Inspection of different areas shows that the image (now UINT8) matches the source DEM (the only difference is that the values were rounded), so it looks like "Cast" is a pretty good option.

Let's have a closer look at the second tile:
Before   
User-added image
After
   User-added image

That's definitely wrong. For better understanding of what is going on let's build a profile of the new DEM (yellow) and compare it with the original profile (brown):
User-added image
The elevation gradually goes up until it reaches 255 meters, which is the maximum value possible for for the type UINT8, and then abruptly it drops down to 0, after that, this pattern repeats again. The new elevation can be expressed with the following formula:
 
elevation % range_for_the_bit_depth = cast_elevation

or, if we take the elevation of the red dot on our UINT8 image:
 
575 % 256 = 63

So, the "Cast" option does not allow us to get a good conversion for the entire dataset.

"Bounded Cast" works nicely for the left tile. For the right tile it makes something different, but it is hardly any better - the top of the hill is simply chopped off (that is, everything above 255 meters) and everything below 0 is set 0:
User-added image

Now we see the limitation of "Cast" and "Bounded Cast". They will work well as long as the values of the raster do not exceed the chosen destination interpretation type. As long as you know for sure that all your data is below the maximum value for the interpretation, these options are a good choice, otherwise, they screw up both pixel values and visual representation.

To avoid this, consider another interpretation type with higher bit depth or another interpretation option - we still have two more to try out.

The option "Scale by Data Value" makes the tiles that look nice and very close to the originals.

Before   
User-added image User-added image
After
 User-added image    User-added image

However, the elevations on both tiles are scaled - expanded on one tile and shrunk on another:
User-added image

The graphical representation of this conversion type could look as follows:

User-added image

The range between minimum and maximum values found in the source is scaled to the full range of available values of the destination type. The tiles in the example have ranges 25 to 200 (left tile) and -45 to 575. Both resulting rasters have values scaled to the range between 0 and 255.

This option won't keep the true representation of the source raster, however, it can be useful for making color images:
User-added image

The image above was made by converting Real64 to Red8, and then two extra bands were added - Green8 set to 100, and Blue8 set to 50. I also added vector contours on top of the image.

The last option, "Scale by Data Type" gives us a simple gray raster with all the pixels having the value of 128.

User-added image

The full range of the source type is scaled to the full range of the destination type. Because the type of Real64 has a range between ~-1.78e+308 and ~1.78e+308, but the actual values vary from -45 to 575, all our destination values are squeezed into a tiny range somewhere very close to 128.

On the other hand, if the values are more or less evenly spread through the entire range, this method can help preserve visual representation:

Before   
User-added image
After
User-added image


To summarize:

The first two options, "Cast" and "Bounded Cast" are better for keeping the original values (or rounded if we go from Real to Integer) under the condition that the destination range is big enough to fit all the values. These options are useful for Numeric to Numeric conversions.

The last two options, "Scale by Data Value" and "Scale by Data Type" help preserve the visual representation and are useful for Numeric-to-Color or Color-to-Color conversions.

As for my tiles, I have to pick another interpretation because I want to keep the correct elevation. INT16 seems to be a reasonable alternative because even if my project covers the entire world, it still won't exceed the maximum and the minimum (in meters) we can find on our planet.

The attached template, RasterInterpretationCoercer.fmwt, shows all the options explained above.

RGBA to RGB Option

The "RGBA to RGB" option tells the transformer what to do if we convert an RGBA image (an image with three color bands and an alpha band responsible for the transparency) into a simple RGB (color) image, that is, if we remove the alpha band.

Here is an RGBA32 image - the portion outside the area of interest is fading gradually into full invisibility:
User-added image

For making the picture visually clearer, I opened it in Paint.Net, a nice free graphic editor. Our own Viewer does not show alpha transparency at this moment (but I hope it will soon).

As you can see, the alpha value within the red circle at the bottom of the image is equal to 114 and the area underneath is semitransparent - we can see through the contents the checkerboard pattern of the background.

If we use "Apply the Alpha Band" option, we still will be able to see the gradual disappearance of the contents, although the image now has the interpretation RGB24 (Paint.Net always shows alpha band, but all the pixels have the value of 255):
User-added image

"Drop Alpha Band" makes a small miracle - it removes the alpha band and reveals what was hidden by the transparency of the alpha pixels:
User-added image

Note how the other bands (Red, Green and Blue) behave in both situations - "apply" option changes them ("alpha influence"), "drop" leaves the bands unchanged ("no alpha influence").

The workspace and the RGBA source image are attached as AlphaHandlingOption.fmwt