POV-Ray : Newsgroups : povray.beta-test : File png/ppm gamma issues. v3.8. : File png/ppm gamma issues. v3.8. Server Time
21 Sep 2021 07:48:28 EDT (-0400)
  File png/ppm gamma issues. v3.8.  
From: William F Pokorny
Date: 20 Mar 2021 13:31:20
Message: <60563168$1@news.povray.org>
Recently in responding to a question about greyscale bit depths posted 
by Ingo in povray.general, I happened to notice it was impossible to set 
the grey scale gamma for output pgm/ppm files. We are in v3.8 always 
getting linear output though the bit depth changes of v3.8 appear to work.

The fix for this is to make changes the write function in ppm.cpp to 
look something like:

     // The official Netpbm standard mandates the use of the ITU-R-BT.709
     // transfer function, although it acknowledges the use of linear
     // encoding or the sRGB transfer function as alternative de-facto
     // standards.
     gamma = options.GetTranscodingGammaCurve(BT709GammaCurve::Get());

     // Greyscale or color output
     // TODO - the check for image container type is here to mimic
     // old code; do we still need it?
     if (image->IsGrayscale() || options.grayscale)
     {
         grayscale = true;
         if (plainFormat)
             file->printf("P2\n");
         else
             file->printf("P5\n");
     }
     else
     {
         if (plainFormat)
             file->printf("P3\n");
         else
             file->printf("P6\n");
     }

where the gamma setting is moved out of the else condition and above the 
if/else conditional selecting greyscale or color output. With this 
change the pgm/ppm output matches the documented v3.8 behavior and that 
for png greyscale and color outputs.

---  png gamma issue.
I've mentioned elsewhere POV-Ray makes a pretty good viewer for >8 bit 
channel output files. While using POV-Ray as viewer it generates an 
output file if not suppressed. On a whim - aka asking for trouble - I 
decided to compare the original rendered output file with the viewer 
output file - these should match. They don't.

First looking at the POV-Ray png default color output and immediate 
input as an image_map in a viewer.

The short story is png output is hard coded to srgb unless overridden by 
a File_Gamma setting somewhere. However, this hard coded output doesn't 
trigger the generation of the correct srgb, encoding, information block 
in the png file, but rather a power law 2.2 gamma block. When the 
generated image is read it's done using a gamma correction which doesn't 
match that used to generate the image.

What this means in practice is shown in the attached image top row where 
the differences are multiplied by 50. The differences are small, but 
certainly there.

In addition, looking at the output files generated by the following 
commands and using iinfo to see the _gamma block encoding I see:

---
p380 render.pov +w400 +h400 +d -p +fn8 +ofn8

     oiio:ColorSpace: "GammaCorrected2.2"
     oiio:Gamma: 2.2

---
p380 render.pov +w400 +h400 +d -p +fn8 +ofn8_srgb File_Gamma=srgb

     oiio:ColorSpace: "sRGB"

---
p380 render.pov +w400 +h400 +d -p +fn8 +ofn8_1 File_Gamma=1.0

     oiio:ColorSpace: "linear"
     oiio:Gamma: 1


---
p380 render.pov +w400 +h400 +d -p +fn8 +ofn8_bt709 File_Gamma=bt709

     oiio:ColorSpace: "GammaCorrected1.9"
     oiio:Gamma: 1

Not worked out how to update the color png gamma code as yet. Or looked 
into similar pgm/ppm differences. Suppose a good near term rule would be 
to always specify the output and input gammas you want. When I do this 
the render result and the viewer result output images match as I think 
they should.

Aside: The bottom row of the attached image shows the greyscale 
differences which include the default png gamma out issue above - plus 
apparently more...

Bill P.

---
For those wanting to play at home:

//-------- render.pov
#version 3.8; // Simple scene to test file gamma behavior.
global_settings { assumed_gamma 1 }
#declare Grey50 = srgb <0.5,0.5,0.5>;
background { color Grey50 }
#declare Camera00 = camera {
     perspective
     location <3,3,-3.001>
     sky y
     angle 35
     right x*(image_width/image_height)
     look_at <0,0,0>
}
#declare White = srgb <1,1,1>;
#declare Light00 = light_source { <50,150,-250>, White }
#declare Red = srgb <1,0,0>;
#declare CylinderX = cylinder { -1*x, 1*x, 0.01 pigment { Red } }
#declare Green = srgb <0,1,0>;
#declare CylinderY = cylinder { -1*y, 1*y, 0.01 pigment { Green } }
#declare Blue = srgb <0,0,1>;
#declare CylinderZ = cylinder { -1*z, 1*z, 0.01 pigment { Blue } }
#include "functions.inc"
#declare Fn00 = function (x,y,z) { f_sphere(x,y/3,z,0.1) }
#declare Xfrm00 = transform {
     rotate z*30.0
     translate <0.5,0.0,0.0>
}
#declare FnXfrm00 = function { transform {Xfrm00 inverse} }
#declare Fn00x = function (x,y,z) {
     Fn00(FnXfrm00(x,y,z).x,FnXfrm00(x,y,z).y,FnXfrm00(x,y,z).z)
}
#declare Xfrm01 = transform {
     rotate z*60.0
     scale <1,3,1>
}
#declare FnXfrm01 = function { transform {Xfrm01 inverse} }
#declare Fn00xx = function (x,y,z) {
     Fn00x(FnXfrm01(x,y,z).x,FnXfrm01(x,y,z).y,FnXfrm01(x,y,z).z)
}
#declare Iso99 = isosurface {
     function { Fn00xx(x,y,z) }
     contained_by { box { -2.0,2.0 } }
     threshold 0
     accuracy 0.0005
     max_gradient 1.1
     pigment { color Green }
}

//--- scene ---
     camera { Camera00 }
     light_source { Light00 }
     object { CylinderX }
     object { CylinderY }
     object { CylinderZ }
     object { Iso99 }


//-------- viewer.pov
// p380 viewer.pov +w400 +h400 +d +p +fn8
// Replace three fn8.png strings below with whatever.
#version 3.8;
global_settings { assumed_gamma 1 }
#declare Grey50 = srgb <0.5,0.5,0.5>;
background { color Grey50 }
#declare VarOrthoMult =
     1.0/max(image_width/image_height,image_height/image_width);
#declare Camera01z = camera {
     orthographic
     location <0,0,-2>
     direction z
     right VarOrthoMult*x*max(1,image_width/image_height)
     up VarOrthoMult*y*max(1,image_height/image_width)
}
#macro ImageMap00(_HF)
   #if (_HF=0)
     image_map { "fn8.png"
   #else
      "fn8.png"
   #end
   #if (_HF=0)
      map_type 0
      once
    //interpolate 2 // No interpolation
     }
   #end
#end
#declare ImageMap00_P = pigment { image_map { "fn8.png" gamma 1 } }
#declare ImageMap00_Range = max_extent(ImageMap00_P);
#declare ImageMap00_NrmScale =
     <min(1,ImageMap00_Range.x/ImageMap00_Range.y),
      min(1,ImageMap00_Range.y/ImageMap00_Range.x),1>;
#declare Pigment00 = pigment {
     ImageMap00(0)
     translate <-0.5,-0.5,0>
     scale ImageMap00_NrmScale
}
#declare Fin00 = finish {
     ambient 0.0
     diffuse 0
     emission srgbft <1,1,1,0,0>
}
#declare Txtr00 = texture { pigment { Pigment00 } finish { Fin00 } }
#declare Plane00 = plane { z, 0 }
#declare Obj00 = object { Plane00 texture { Txtr00 } }

//--- scene ---
     camera { Camera01z }
     object { Obj00 }


Post a reply to this message


Attachments:
Download 'png_gamma_story.png' (33 KB)

Preview of image 'png_gamma_story.png'
png_gamma_story.png


 

Copyright 2003-2021 Persistence of Vision Raytracer Pty. Ltd.