Opened 12 years ago
Last modified 2 years ago
#774 new enhancement
i.rgb.his, i.his.rgb, d.his support for >8 bit
Reported by: | hamish | Owned by: | |
---|---|---|---|
Priority: | normal | Milestone: | 7.6.2 |
Component: | Raster | Version: | svn-trunk |
Keywords: | i.rgb.his, i.his.rgb, d.his | Cc: | |
CPU: | All | Platform: | All |
Description
Hi,
it would be nice if i.rgb.his, i.his.rgb, d.his modules could have support for >8 bit colors.
attached is a patch which does this for i.rgb.his.
i.his.rgb and d.his are a bit deeper into the max=255 and casting CELLs to unsigned char.
Hamish
Attachments (3)
Change History (28)
Changed 12 years ago by
Attachment: | irgbhis_16bit.diff added |
---|
comment:1 Changed 12 years ago by
for d.his main.c we can probably pass HIS_to_RGB() something like (int)(value_[atcol]/pow(2, bitdepth-8)) to rescale >8bit to 0,255.
opt_bit_depth->options = "8-16";
(we can only set the color to 8-bit per channel, right? so we have to degrade at some point.. might as well be here)
Hamish
comment:2 Changed 12 years ago by
Replying to hamish:
it would be nice if i.rgb.his, i.his.rgb, d.his modules could have support for >8 bit colors.
i.his.rgb and d.his are a bit deeper into the max=255 and casting CELLs to unsigned char.
d.his uses the raster's colour table, so it will work with integer rasters of any bit depth, as well as FP rasters. The use of 8 bits within d.his is due to both GRASS' colour tables and the display architecture using 8-bit intensity values.
I'm not sure that there is much point in changing the display architecture when display hardware which supports more than 8-bit intensity is so rare (I know that some image formats support more than this, but that's not much use if the images are going to be displayed on hardware which only supports 8 bits).
For i.* or r.* modules, you can either use the raster's colour table, or you need an associated scale parameter for each input raster (and possibly an offset parameter as well), or you require that all inputs be FP rasters where 0.0 is black and 1.0 is white.
Using the colour table limits you to 8-bit resolution, but it does at least allow you to use maps with more resolution as inputs.
Changed 12 years ago by
Attachment: | irgbhis_16bit2.diff added |
---|
updated patch for i.rgb.his and i.his.rgb
comment:3 follow-up: 4 Changed 12 years ago by
updated patch attached allowing >8 bit support for i.rgb.his and i.his.rgb. Patch is against latest 6.5svn.
Note round-trip is a little lossy due to hexagonal approximation to the color-cone. TODO: use real cone geometry maths.
These modules write out colr/ tables as min,max of channel. Would it be better to write them out as 0,max_level for the particular bit-depth? e.g. 0,255 for 8-bit and 0,2047 for 11-bit. Then d.rgb and d.his give natural looking results without subtle extra steps.
Hamish
comment:4 Changed 12 years ago by
Replying to hamish:
These modules write out colr/ tables as min,max of channel. Would it be better to write them out as 0,max_level for the particular bit-depth? e.g. 0,255 for 8-bit and 0,2047 for 11-bit. Then d.rgb and d.his give natural looking results without subtle extra steps.
Apart from anything else, assigning black/white to the min/max of the data is definitely wrong.
Beyond that, I suggest:
- changing bit_depth= to max_level=, and reading the maps as DCELL, so that the modules work with both integer and FP data, including integer data where max isn't a power of two.
- making the output FCELL, with values in the range 0.0 to 1.0 (or possibly 0.0 to 360.0 for hue), and using those values for 0% and 100% intensity. Note that d.his uses G_get_raster_row_colors(), so it doesn't care about the actual values, only the corresponding colours.
- replacing both modules with scripts which use r.mapcalc.
- Offering the option of "conical" HSV, i.e. RGB->YUV, H=atan2(U,V).
comment:7 Changed 5 years ago by
Milestone: | 7.0.4 → 7.0.5 |
---|
comment:8 Changed 5 years ago by
I tried irgbhis_16bit2.diff
without reading much the changes it introduces. Going from RGB to HIS and back to RGB gives NULL, for all red, blue and green input maps.
Changed 5 years ago by
Attachment: | i.rgb.his.diff added |
---|
Rewritten, outputs in ranges as defined in the HSL/HSV color space model
comment:9 Changed 5 years ago by
The attached diff works for me, for bitnesses >8-bit. The outputs hue, saturation and intensity are in their expected range, as per the HSL/HSV color space model. There is still an issue on how to instruct the output to be DCELL and not FCELL. Accordingly, I'd like to modify i.his.rgb.
Source for the math of concern are:
- https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
- https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
The current/old implementation is identical to the one documented at ERDAS' field guide. I think, however, it was meant to deal/avoid the case when hue is undefined (this being the case when the max and min values of a band/image are identical) and, perhaps, other issues with floating point math(?).
See also:
comment:10 Changed 5 years ago by
Milestone: | 7.0.5 → 7.3.0 |
---|
comment:12 Changed 5 years ago by
Instead of http://arxiv.org/abs/1107.3348, I wanted to point to https://arxiv.org/pdf/1107.4396.pdf.
comment:13 Changed 5 years ago by
Modified versions for i.rgb.his and i.his.rgb in respective directories at https://trac.osgeo.org/grass/browser/sandbox/alexandris?order=name.
I have reasons to support closing this ticket, after checking and updating all relevant modules or scripts that use them, of course!
A reproducible session, below, to verify that:
- i.rgb.his
- i.his.rgb
Note, however, the modified versions of the modules break the current functionality of i.pansharpen
. This is because the the intensity values, after the modified i.rgb.his, play between 0 and 1. Before, intensity was ranging in [0,255]. The fix should be, at least, conceptually, fairly easy. See also past discussions in #2048.
# Reproducible session
# check range of Landsat bands for BAND in 10 20 30; do echo `echo -e "Landsat band ${BAND}: " && r.info -r lsat7_2002_${BAND}` ;done Landsat band 10: min=42 max=255 # this is blue Landsat band 20: min=28 max=255 # this is green Landsat band 30: min=1 max=255 # this is red # convert (rgb) to his, 8-bit i.rgb.his r=lsat7_2002_30 g=lsat7_2002_20 bl=lsat7_2002_10 h=h8 i=i8 s=s8 # rescale to 16-bit in order to test the `bits` option for BAND in 10 20 30; do r.rescale lsat7_2002_$BAND from=0,255 output=lsat7_2002_${BAND}_16 to=0,65535 ;done Rescale lsat7_2002_10[0,255] to lsat7_2002_10_16[0,65535] Rescale lsat7_2002_20[0,255] to lsat7_2002_20_16[0,65535] Rescale lsat7_2002_30[0,255] to lsat7_2002_30_16[0,65535] # convert again, bits=16 i.rgb.his r=lsat7_2002_30_16 g=lsat7_2002_20_16 bl=lsat7_2002_10_16 h=h16 i=i16 s=s16 bits=16 # check h, i, s output ranges, 8 & 16-bit for DIMENSION in h i s; do echo `echo "${DIMENSION} 8:" && r.info -r ${DIMENSION}8` && echo `echo -e "${DIMENSION} 16:" && r.info -r ${DIMENSION}16` ;done h 8: min=0 max=359.434 h 16: min=0 max=359.434 i 8: min=0.08431373 max=1 i 16: min=0.08431373 max=1 s 8: min=0 max=1 s 16: min=0 max=1 # convert (his) back to rgb i.his.rgb h=h8 i=i8 s=s8 r=r8 g=g8 bl=b8 bits=8 i.his.rgb h=h16 i=i16 s=s16 r=r16 g=g16 bl=b16 bits=16 # check rgb ranges for COLOR in r g b; do echo `echo "${COLOR} 8:" && r.info -r ${COLOR}8` && echo `echo -e "${COLOR} 16:" && r.info -r ${COLOR}16` ;done r 8: min=0 max=255 r 16: min=256 max=65535 g 8: min=28 max=255 g 16: min=7195.999 max=65535 b 8: min=42 max=255 b 16: min=10794 max=65535 # There is some "loss" or "smoothing" after the roundtrip. Note, for red the # minimum is now `1` and not `0`. # Some stats to verify # input 8-bit Landsat 7 bands for BAND in 10 20 30; do echo "lsat7_2002_${BAND} (8-bit): " && r.univar -g --q lsat7_2002_${BAND} && echo -e '\n' ;done lsat7_2002_10 (8-bit): n=250325 null_cells=0 cells=250325 min=42 max=255 range=213 mean=85.473624288425 mean_of_abs=85.473624288425 stddev=20.6960320098858 variance=428.325740954217 coeff_var=24.2133549175924 sum=21396185 lsat7_2002_20 (8-bit): n=250325 null_cells=0 cells=250325 min=28 max=255 range=227 mean=70.9216418655748 mean_of_abs=70.9216418655748 stddev=23.3183338152895 variance=543.744691921275 coeff_var=32.8790101327423 sum=17753460 lsat7_2002_30 (8-bit): n=250325 null_cells=0 cells=250325 min=1 max=255 range=254 mean=66.6989074203535 mean_of_abs=66.6989074203535 stddev=34.7033706761684 variance=1204.32393628754 coeff_var=52.029893769412 sum=16696404 # rescaled to 16-bit Landsat 7 bands for BAND in 10 20 30; do echo "lsat7_2002_${BAND} (16-bit): " && r.univar -g --q lsat7_2002_${BAND}_16 && echo -e '\n' ;done lsat7_2002_10 (16-bit): n=250325 null_cells=0 cells=250325 min=10794 max=65535 range=54741 mean=21966.7214421252 mean_of_abs=21966.7214421252 stddev=5318.88022654064 variance=28290486.8642851 coeff_var=24.2133549175924 sum=5498819545 lsat7_2002_20 (16-bit): n=250325 null_cells=0 cells=250325 min=7196 max=65535 range=58339 mean=18226.8619594527 mean_of_abs=18226.8619594527 stddev=5992.81179052941 variance=35913793.1567083 coeff_var=32.8790101327423 sum=4562639220 lsat7_2002_30 (16-bit): n=250325 null_cells=0 cells=250325 min=257 max=65535 range=65278 mean=17141.6192070309 mean_of_abs=17141.6192070309 stddev=8918.76626377528 variance=79544391.667856 coeff_var=52.029893769412 sum=4290975828 # his, 8-bit for DIMENSION in h i s; do echo "${DIMENSION} (8-bit): " && r.univar -g --q ${DIMENSION}8 && echo -e '\n' ;done h (8-bit): n=250007 null_cells=318 cells=250325 min=0 max=359.433959960938 range=359.433959960938 mean=232.677368810375 mean_of_abs=232.677368810375 stddev=47.0943195962118 variance=2217.87493823014 coeff_var=20.2401805714901 sum=58170970.9441754 i (8-bit): n=250325 null_cells=0 cells=250325 min=0.084313727915287 max=1 range=0.915686272084713 mean=0.294945252267712 mean_of_abs=0.294945252267712 stddev=0.10312914101826 variance=0.0106356197271642 coeff_var=34.9655199483101 sum=73832.1702739149 s (8-bit): n=250325 null_cells=0 cells=250325 min=0 max=1 range=1 mean=0.188441316597818 mean_of_abs=0.188441316597818 stddev=0.105874696347741 variance=0.0112094513267264 coeff_var=56.1844388795609 sum=47171.5725773489 # his, 16-bit for DIMENSION in h i s; do echo "${DIMENSION} (16-bit): " && r.univar -g --q ${DIMENSION}16 && echo -e '\n' ;done h (16-bit): n=250007 null_cells=318 cells=250325 min=0 max=359.433959960938 range=359.433959960938 mean=232.677368810375 mean_of_abs=232.677368810375 stddev=47.0943195962118 variance=2217.87493823014 coeff_var=20.2401805714901 sum=58170970.9441754 i (16-bit): n=250325 null_cells=0 cells=250325 min=0.084313727915287 max=1 range=0.915686272084713 mean=0.294945252267712 mean_of_abs=0.294945252267712 stddev=0.10312914101826 variance=0.0106356197271642 coeff_var=34.9655199483101 sum=73832.1702739149 s (16-bit): n=250325 null_cells=0 cells=250325 min=0 max=1 range=1 mean=0.188441316597818 mean_of_abs=0.188441316597818 stddev=0.105874696347741 variance=0.0112094513267264 coeff_var=56.1844388795609 sum=47171.5725773489 # colors, 8-bit for COLOR in r g b; do echo "${COLOR} (8-bit):" && r.univar -g --q ${COLOR}8 && echo -e '\n' ;done r (8-bit): n=250007 null_cells=318 cells=250325 min=0 max=255 range=255 mean=66.2976036670973 mean_of_abs=66.2976036670973 stddev=34.0979922976592 variance=1162.67307873122 coeff_var=51.4317115726787 sum=16574865 g (8-bit): n=250007 null_cells=318 cells=250325 min=27.9999961853027 max=255 range=227.000003814697 mean=70.6895694237074 mean_of_abs=70.6895694237074 stddev=22.3983713595429 variance=501.687039559992 coeff_var=31.6855393831711 sum=17672887.1829128 b (8-bit): n=250007 null_cells=318 cells=250325 min=42 max=255 range=213 mean=85.2600618087387 mean_of_abs=85.2600618087387 stddev=19.8145003705753 variance=392.614424935528 coeff_var=23.2400727259904 sum=21315612.2726173 # colors, 16-bit for COLOR in r g b; do echo "${COLOR} (16-bit):" && r.univar -g --q ${COLOR}16 && echo -e '\n' ;done r (16-bit): n=250007 null_cells=318 cells=250325 min=256 max=65535 range=65279 mean=17080.4321518997 mean_of_abs=17080.4321518997 stddev=8757.35508357642 variance=76691268.0598418 coeff_var=51.2712734999647 sum=4270227601 g (16-bit): n=250007 null_cells=318 cells=250325 min=7195.9990234375 max=65535 range=58339.0009765625 mean=18167.2193426611 mean_of_abs=18167.2193426611 stddev=5756.38144044411 variance=33135927.2878894 coeff_var=31.6855393875644 sum=4541932006.20068 b (16-bit): n=250007 null_cells=318 cells=250325 min=10794 max=65535 range=54741 mean=21911.8358886264 mean_of_abs=21911.8358886264 stddev=5092.32659532506 variance=25931790.1534549 coeff_var=23.2400727223788 sum=5478112355.00781
comment:16 follow-up: 19 Changed 3 years ago by
Nikos, how confident are you about your version in the sandbox ? Can it replace the version in trunk ?
comment:17 follow-up: 18 Changed 3 years ago by
The statistics in https://trac.osgeo.org/grass/ticket/774#comment:13, show that going from either an 8-bit or 16-bit RGB to HIS and back to RGB, figures are not scrambled. Some rounding applies, as exemplified in the same stats above. Even visually, I confirmed (not documented here) that the old and the new versions perform the same.
It will break i.pansharpen, unless i.pansharpen treats intensity ranging in [0,1].
Nevertheless, another set of tests would be certainly good to have to confirm the correctness of the new version.
comment:18 Changed 3 years ago by
Version: | svn-develbranch6 → svn-trunk |
---|
Replying to Nikos Alexandris:
The statistics in https://trac.osgeo.org/grass/ticket/774#comment:13, show that going from either an 8-bit or 16-bit RGB to HIS and back to RGB, figures are not scrambled. Some rounding applies, as exemplified in the same stats above. Even visually, I confirmed (not documented here) that the old and the new versions perform the same.
This sounds very promising.
It will break i.pansharpen, unless i.pansharpen treats intensity ranging in [0,1].
Would it be hard to add that to i.pansharpen?
Nevertheless, another set of tests would be certainly good to have to confirm the correctness of the new version.
I'd suggest to "svn move" it from sandbox to addons for wider testing with g.extention.
comment:19 follow-up: 20 Changed 3 years ago by
Replying to mlennert:
Nikos, how confident are you about your version in the sandbox ? Can it replace the version in trunk ?
This is a computational danger zone, I think also automatic tests are required. I suggest to add tests to the modules in trunk and use them to test the new modules (whether they are in sandbox or addons).
comment:20 Changed 3 years ago by
Replying to wenzeslaus:
Replying to mlennert:
Nikos, how confident are you about your version in the sandbox ? Can it replace the version in trunk ?
This is a computational danger zone, I think also automatic tests are required. I suggest to add tests to the modules in trunk and use them to test the new modules (whether they are in sandbox or addons).
I fully agree. These tests would be another good exercise for me. If I only can make it to the upcoming code sprint, I will work on these.
Please, see also/don't forget the other two:
comment:21 Changed 3 years ago by
A gunittest
is available at: https://github.com/NikosAlexandris/test_color_space_conversions/blob/master/test_color_space_roundtrips.py
The tests compare the input R, G and B images (Landsat imagery from the nc_spm_08_grass7
test data set) from 6- up to 16-bit (by rescaling the original inputs) with the output R, G and B after a roundtrip from RGB to HIS and back to RGB color spaces.
They confirm, as far as I understand, that the suggested modules
- https://trac.osgeo.org/grass/browser/sandbox/alexandris/i.rgb.his/
- https://trac.osgeo.org/grass/browser/sandbox/alexandris/i.his.rgb/
work with precision levels 0.1 and 0.01. Setting the precision to 0.001, will cause the the test fail for bits=
>= 15.
HIS values are also tested for being inside the expected ranges.
A review of the script would be much appreciated in order to confirm its functionality.
Some points:
- The script tests both the
i.rgb.his
andi.his.rgb
modules. So, where should it be placed?
r.rescale
smartly picks up the max value among any two values given in theto=
parameter. This is a bit confusing and the tests will fail for bitnesses < 6. I think it is meaningful to test for bitnesses all the way down to 2-bit images.
- Currently, the test counts 4 tests. Maybe it would make more sense to count as many as the different precision levels requested, and in addition as many as the different bitnesses tested.
- Would be it better to create dynamically synthetic images instead of using "external" imagery?
Can someone move the test to trunk as suggested by Vaclav?
comment:22 Changed 3 years ago by
Milestone: | 7.4.1 → 7.4.2 |
---|
comment:23 Changed 3 years ago by
Milestone: | 7.4.2 → 7.6.0 |
---|
All enhancement tickets should be assigned to 7.6 milestone.
patch to add 16bit channel support to i.rgb.his