Skip to content

Commit

Permalink
ImageStats : Fix handling of infinite values
Browse files Browse the repository at this point in the history
If there are pixels with infinite value, we want that to be represented in the output.
  • Loading branch information
johnhaddon committed Jan 4, 2024
1 parent c27c8c1 commit 88c3e2a
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 7 deletions.
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Fixes
- Stopped failed jobs jumping to the end of the Local Jobs UI.
- Fixed message log update.
- Fixed `Job.statistics()` errors on Windows, ensuring that a `pid` is always returned when available.
- ImageStats : Fixed output of infinite values, which were previously being clamped.

API
---
Expand Down
26 changes: 26 additions & 0 deletions python/GafferImageTest/ImageStatsTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import os
import unittest
import math
import imath

import IECore
Expand Down Expand Up @@ -300,6 +301,31 @@ def testTaskCollaboration( self ) :

self.assertEqual( pm.plugStatistics( stats["__allStats" ] ).computeCount, 1 )

def testInf( self ) :

# Make an image with all `inf` values. We can't do this directly
# with a Constant because the inputs are clamped at float max,
# so we divide an image by zero to get what we want.

constant0 = GafferImage.Constant()
constant1 = GafferImage.Constant()
constant1["color"].setValue( imath.Color4f( 1 ) )

merge = GafferImage.Merge()
merge["in"][0].setInput( constant0["out"] )
merge["in"][1].setInput( constant1["out"] )
merge["operation"].setValue( merge.Operation.Divide )

# Since all values are `inf`, all outputs should be too.

stats = GafferImage.ImageStats()
stats["in"].setInput( merge["out"] )
stats["area"].setValue( stats["in"].format().getDisplayWindow() )

self.assertTrue( math.isinf( stats["max"][0].getValue() ) )
self.assertTrue( math.isinf( stats["min"][0].getValue() ) )
self.assertTrue( math.isinf( stats["average"][0].getValue() ) )

def __assertColour( self, colour1, colour2 ) :
for i in range( 0, 4 ):
self.assertEqual( "%.4f" % colour2[i], "%.4f" % colour1[i] )
Expand Down
23 changes: 16 additions & 7 deletions src/GafferImage/ImageStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,18 @@ ImageStats::ImageStats( const std::string &name )
addChild( new StringVectorDataPlug( "channels", Plug::In, defaultChannelsData ) );

addChild( new Box2iPlug( "area", Gaffer::Plug::In ) );
addChild( new Color4fPlug( "average", Gaffer::Plug::Out, Imath::Color4f( 0, 0, 0, 1 ) ) );
addChild( new Color4fPlug( "min", Gaffer::Plug::Out, Imath::Color4f( 0, 0, 0, 1 ) ) );
addChild( new Color4fPlug( "max", Gaffer::Plug::Out, Imath::Color4f( 0, 0, 0, 1 ) ) );
addChild( new Color4fPlug(
"average", Gaffer::Plug::Out, Imath::Color4f( 0, 0, 0, 1 ),
Imath::Color4f( -std::numeric_limits<float>::infinity() ), Imath::Color4f( std::numeric_limits<float>::infinity() )
) );
addChild(
new Color4fPlug( "min", Gaffer::Plug::Out, Imath::Color4f( 0, 0, 0, 1 ),
Imath::Color4f( -std::numeric_limits<float>::infinity() ), Imath::Color4f( std::numeric_limits<float>::infinity() )
) );
addChild(
new Color4fPlug( "max", Gaffer::Plug::Out, Imath::Color4f( 0, 0, 0, 1 ),
Imath::Color4f( -std::numeric_limits<float>::infinity() ), Imath::Color4f( std::numeric_limits<float>::infinity() )
) );

addChild( new ObjectPlug( "__tileStats", Gaffer::Plug::Out, new IECore::V3dData() ) );
addChild( new ObjectPlug( "__allStats", Gaffer::Plug::Out, new IECore::V3dData() ) );
Expand Down Expand Up @@ -419,8 +428,8 @@ void ImageStats::compute( ValuePlug *output, const Context *context ) const

IECore::ConstFloatVectorDataPtr channelData = flattenedInPlug()->channelDataPlug()->getValue();

float min = std::numeric_limits<float>::max();
float max = std::numeric_limits<float>::lowest();
float min = std::numeric_limits<float>::infinity();
float max = -std::numeric_limits<float>::infinity();
double sum = 0.;

const std::vector<float> &channel = channelData->readable();
Expand All @@ -444,8 +453,8 @@ void ImageStats::compute( ValuePlug *output, const Context *context ) const
static_cast<ObjectPlug *>( output )->setValue( new IECore::V3dData( Imath::V3d( 0 ) ) );
return;
}
float min = std::numeric_limits<float>::max();
float max = std::numeric_limits<float>::lowest();
float min = std::numeric_limits<float>::infinity();
float max = -std::numeric_limits<float>::infinity();
double sum = 0.;

// We traverse in TopToBottom order because floating point precision means that changing
Expand Down

0 comments on commit 88c3e2a

Please sign in to comment.