POV-Ray : Newsgroups : povray.beta-test : File png/ppm gamma issues v3.8. Part 1. Server Time
29 Mar 2024 05:57:40 EDT (-0400)
  File png/ppm gamma issues v3.8. Part 1. (Message 1 to 1 of 1)  
From: William F Pokorny
Subject: File png/ppm gamma issues v3.8. Part 1.
Date: 20 Mar 2021 13:22:16
Message: <60562f48$1@news.povray.org>
// 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.

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 two POV-Ray generated images 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.

What this means in practice is shown in the attached image top row where 
the differences are multiplied by 50. The differences are mostly 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.9

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 gamma you want. When I do this, 
the render result and the viewer result images match - as they should.

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

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 below with render.png or 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-2023 Persistence of Vision Raytracer Pty. Ltd.