Solved

Change each pixels rgb values


I'm in the process of doing a histogram match for two images. So far, I've been able to do some statistics on the levels of grey, histogram the values, and in an external python script, determine new values per value of grey. i.e the reference value of 1 might be value 10 in the target image.

 

 

Can the Raster expression evaluator or another transformer conduct a test to read the the value of a pixel and reassign it a new value. For example if i'm looking at the red channel, IF pixelvalue == 1 ? pixelvalue = 20 : (IF pixelvalue == 2) ? pixelvalue = 30 etc etc...

 

 

Maybe this needs to involve pallettes?

 

 

 

 
icon

Best answer by takashi 6 July 2015, 16:57

View original

16 replies

Userlevel 3
Badge +17
Hi,

 

 

This article might help you.

 

Using Conditions with the RasterExpressionEvaluator Transformer (https://knowledge.safe.com/articles/Samples_and_Demos/Using-Conditions-with-the-RasterExpressionEvaluator-Transformer)

 

 

Takashi
Hi,

 

 

Thanks for the lead. I've encountered another issue. The error is INTERPRETATION_LIST has 1 entries , whereas the EXPRESSION_LIST has 546 entries.

 

What I tried to do is set the interpretation to auto, the I have my if statements all into the expression. e.g

 

@if (A[0] == 0, 3,3);

 

@if (A[0] == 1, 4,4);

 

@if (A[0] == 2, 5,5);

 

@if (A[0] == 3, 6,6);

 

@if (A[0] == 4, 7,7);

 

@if (A[0] == 5, 8,8);

 

@if (A[0] == 6, 9,9);

 

@if (A[0] == 7, 10,10);

 

@if (A[0] == 8, 11,11);

 

@if (A[0] == 9, 12,12);

 

 

Is there a way to multiline the expression?

 

Thanks.
Userlevel 3
Badge +17
In the past (maybe FME 2012 and earlier), the RasterExpressionEvaluator had only one field for entering expressions, and semicolons were used to separate multiple expressions for each band.

 

But the interface has been changed. Currently you have to enter expressions seperately for each row associated to a band, if your raster has multiple bands. When you used semicolons in an expression, such an error occurs.

 

 

I guess you are going to calculate a value for one band.

 

i.e.

 

If A[0] == 0 Then 3

 

Else If A[0] == 1 Then 4

 

Else If A[0] == 2 Then 5 ... and so on.

 

 

If my understanding is correct, you can use a nested "if" statement.

 

e.g.

 

@if (A[0] == 0, 3,

 

@if (A[0] == 1, 4,

 

@if (A[0] == 2, 5,

 

@if (A[0] == 3, 6,

 

@if (A[0] == 4, 7,

 

@if (A[0] == 5, 8,

 

@if (A[0] == 6, 9,

 

@if (A[0] == 7, 10,

 

@if (A[0] == 8, 11, 12)))))))))

 

 

However, the error says your expression is going to calculate values for 546 entries. It means that you have 546 conditions in fact.

 

Although it may be possible theoretically to write a nested "if" statement for such many conditions, it would be too complicated and also troublesome.

 

 

There could be more simple way. For example, if the requirement is to just add 3 to the original pixel value, this expression would be enough.

 

A[0] + 3

 

 

If you explain the requirement concretely, we could consider a workaround.
Thanks again.

 

 

What i've tried was using an older raster expression evaluator that had the text box input for interpretations and was able to copy paste the correct number of matching interpretations to the expressions. i.e 546 interpretations, 546 evaluations.

 

The problem with that was, after processing every pixel contained 546 bands.

 

 

Just to describe what I have in further detail. The histogram matching has the source image and target image broken up into R,G,B and the percentage of occuring decimal values 0-255 across both images is calculated. Comparing these percentage values, I can determine, for example, when my target image has a Red8 value of 50, this is equivalent to source image Red8 30. So i've created a mapping of new RGB values for my target image. [30,50] 

 

 

So with the mapping I need to change the target image's rgb values according to the new mapping

 

 

[6, 7, 8, 9, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 29, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 71, 72, 73, 74, 75, 75, 76, 77, 77, 78, 79, 79, 80, 81, 81, 82, 83, 83, 84, 84, 85, 85, 86, 87, 87, 88, 88, 89, 89, 90, 91, 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 117, 118, 120, 121, 122, 123, 125, 126, 127, 128, 130, 133, 135, 136, 138, 140, 142, 144, 147, 148, 149, 151, 152, 152, 154, 156, 156, 157, 157, 158, 160, 161, 161, 161, 161, 162, 162, 162] 

 

 

The array above represents the mapping of a red channel. The index of the array would describe the decimal value. For example, Decimal 0 should be 6 instead. Decimal 1 should be 7.

 

 

Summary, I need a way to map new rgb values to a raster and write the new raster image out.

 

 

I'm now considering converting image to point cloud, change the red green blue component of the points, and then rasterize the point cloud.
Userlevel 3
Badge +17
The older RasterExpressionEvaluator generates 546 bands if the expression has 546 entries which are separated with semicolons. Even if you use the old transformer, you have to set a nested "if" statement to map a band value.

 

 

I'm still unclear the mapping rule.

 

The array that you posted contains 182 elements. The requirement is to change a pixel value like this?

 

If A[0] == 0 Then 6

 

Else If A[0] == 1Then 7

 

Else If A[0] == 2 Then 8

 

...

 

Else If A[0] == 180 Then 162

 

Else If A[0] == 181 Then 162

 

Else ? (how to map the value > 181?)

 

I haven't thought of how the last value would be mapped. I might manual input the last value, so A[0] == 182, 162,162) , something along those lines. 

 

 

I'll give it a try. I have a python script to help me generate the entire expression statement which makes it easier.

 

 

 
Userlevel 3
Badge +17
I agree that it would be easy to generate the expression using a script.
Ok, so the nested expression works but I've now hit some type of limit with how many nestings it can support. I've reach about 130 nests, but it will not do the full 182 let alone 546, it simple says "error running translation" . I can work around this by dividing the script into two per channel, but ultimately costs me time. 
Badge +3
extracting a colormap and processing it and reinserting the colormap seems a more efficient way, rather then making a huge (nested) expressionlist.

 

That would make the attributemapping accesible, and you could use your map (as a txt or csv file).

 

 

 

There are examples of this roaming somewhere in this forum.
I've had a look for colormap but mostly lead to some of your own posts Gio. I need some pointers on the colourmap and what transformers I should be working with.

 

 

How do I reinsert a colourmap. Is this part of transforming into palettes instead of bands?

 

 

Thanks.
Userlevel 3
Badge +17
I tried the palette approach.

 

Firstly, create 3 pallete attributes with 256 entries for each band. Use "GRAY8" as the value interpretation for every pallete. These pallete attributes will be used as value mapping table for each band. See also the help doc of the RasterPaletteExtractor to learn about the format of pallete attribute.

 

e.g.

 

-----

 

GRAY8

 

0 6

 

1 7

 

2 8

 

3 9

 

4 10

 

...

 

254 ???

 

255 ???

 

-----

 

 

Divide the data flow into 3 streams and add these transformers seperately for each band.

 

(1) RasterSelector - select 2 bands

 

Band and Pallete List: 1;2 ("0;2" for green band, "0;1" for blue band)

 

(2) RasterBandRemover - remove selected bands. i.e. keep one band

 

(3) RasterSelector - select the first band

 

Band and Pallete List: 0

 

(4) RasterBandInterpretationCorercer

 

Destination Interpretation Type: UInt8

 

(5) RasterPaletteAdder

 

Palette Attribute: <specify the palette attribute for the band>

 

(6) RasterPaletteResolver

 

(7) RasterBandInterpretationCoercer - restore the original band interpretation

 

Destination Interpretation Type: Red8 ("Green8" for green band, "Blue8" for blue band)

 

(8) AttributeCreator

 

_order = 0 (1 for green band, 2 for blue band)

 

 

Send the 3 rasters to a Sorter to sort by "_order" ascending.

 

Finally, add a RasterBandCombiner to re-create an RGB raster.

 

 

There could be smarter way...
Userlevel 3
Badge +17
This is the data flow for testing.

 

Userlevel 3
Badge +17
oops, first 3 transformers (RasterSelector, RasterBandRemover and RasterSelector_2) can be replaced with RasterSelector + RasterBandKeeper.

 

-----

 

RasterSelector

 

- Band And Pallete List: 0 for Red / 1 for Green / 2 for Blue

 

RasterBandKeeper
Userlevel 3
Badge +17
This data flow produced the same result as the previous one.

 

Hi Takashi

 

 

The results from your workflow is amazing. The workbench with nested expressions method averages 5.5 minutes to compute whereas your workflow takes less than 30 seconds. That's a significant improvement will greatly reduce total processing time. Thank you very much.
Userlevel 3
Badge +17
That's great. I was able to upgrade my own skill about raster manipulations too :)

Reply