Skip to content

Commit

Permalink
Additional updates to handle namespaces properly in LUT3D, ASCCDL and…
Browse files Browse the repository at this point in the history
… Info

Update to handle Inf and NaN values in LUT1D lookups
Turned off cachiing of IndexMaps values. Was very, very slow.
Updates to handle OpenImageIO versions greater than 1.8, less than 2.0
  • Loading branch information
hpd committed Dec 15, 2018
1 parent 028b909 commit 47ba8be
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 28 deletions.
9 changes: 4 additions & 5 deletions python/aces/clf/ASCCDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,18 @@ def write(self, tree):

def readChild(self, element):
elementType = self.getElementType(element.tag)
if elementType == 'SOPNode':
if elementType.lower() == 'sopnode':
for child in element:
childType = child.tag
childType = self.getElementType(child.tag)
if childType == 'Slope':
self._values['slope'] = map(float, child.text.split())
elif childType == 'Offset':
self._values['offset'] = map(float, child.text.split())
elif childType == 'Power':
self._values['power'] = map(float, child.text.split())
elif elementType == 'SatNode':
elif elementType.lower() == 'satnode':
for child in element:
childType = child.tag
childType = self.getElementType(child.tag)
if childType == 'Saturation':
self._values['saturation'] = float(child.text)
return None
Expand Down Expand Up @@ -202,7 +202,6 @@ def process(self, values, stride=0, verbose=False):
outValue[i] = clamp( outValue[i] )

luma = 0.2126 * outValue[0] + 0.7152 * outValue[1] + 0.0722 * outValue[2]
print( luma )

for i in range(3):
outSat = luma + (1.0/saturation) * (outValue[i] - luma)
Expand Down
18 changes: 12 additions & 6 deletions python/aces/clf/Array.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,14 +488,20 @@ def lookup1DLinear(self, position, channel):
dimensions = self._dimensions

index = position*(dimensions[0]-1)
indexLow = int(math.floor(index))
indexHigh = int(math.ceil(index))
interp = index - indexLow

value1 = self.lookup1D(indexLow, channel)
value2 = self.lookup1D(indexHigh, channel)
# NaNs, Infs
if np.isnan(index) or np.isinf(index):
result = index
# Normal values
else:
indexLow = int(math.floor(index))
indexHigh = int(math.ceil(index))
interp = index - indexLow

value1 = self.lookup1D(indexLow, channel)
value2 = self.lookup1D(indexHigh, channel)

result = (1-interp)*value1 + interp*value2
result = (1-interp)*value1 + interp*value2

return result
# lookup1DLinear
Expand Down
2 changes: 1 addition & 1 deletion python/aces/clf/IndexMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
class IndexMap:
"A Common LUT Format IndexMap element"

def __init__(self, dimension=[], values=[], elementType='IndexMap', useCachedProcess=True):
def __init__(self, dimension=[], values=[], elementType='IndexMap', useCachedProcess=False):
"%s - Initialize the standard class variables" % elementType
self._dimension = dimension
self._values = values
Expand Down
9 changes: 8 additions & 1 deletion python/aces/clf/Info.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,17 @@ def write(self, tree):
return element
# write

def getElementType(self, tag):
# ..find('}') allows us to strip out namespaces
elementType = tag[tag.find('}')+1:]
elementType = elementType.replace('-', '')
elementType = elementType.replace('_', '')
return elementType

def read(self, element):
# Read child elements
for child in element:
elementType = child.tag
elementType = self.getElementType(child.tag)

if elementType == 'AppRelease':
self._children.append( Comment(child.text, 'AppRelease') )
Expand Down
6 changes: 3 additions & 3 deletions python/aces/clf/LUT3D.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def readChild(self, element):
child.setValuesAreIntegers(integers)

self._array = child
elif element.tag == 'IndexMap':
elif elementType == 'IndexMap':
child = IndexMap()
child.read(element)
self._indexMaps.append( child )
Expand Down Expand Up @@ -156,12 +156,12 @@ def process(self, values, stride=0, verbose=False):

# Run each channel through the index map, or base normalization
for i in range(min(3, len(value))):
# Run through single Index Map then normalize
# Run through per-channel Index Map then normalize
if len(self._indexMaps) > 1:
outValue[i] = self._indexMaps[i].process(outValue[i])
outValue[i] /= float(dimensions[i]-1)

# Run through per-channel Index Map then normalize
# Run through single Index Map then normalize
elif len(self._indexMaps) > 0:
outValue[i] = self._indexMaps[0].process(outValue[i])
outValue[i] /= float(dimensions[i]-1)
Expand Down
40 changes: 28 additions & 12 deletions python/aces/filterImageWithCLF.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,13 @@ def halfsToFloat(half1, half2):
# 'floats' read from OpenImageIO.
# Each 4 byte 'float' is the binary equivalent of two packed 2-byte half-floats.
def oiioFloatPixelsToNPHalfArray(width, height, channels, oiioFloats):
# Read float pixels into a numpy half-float pixel array
npPixels = np.frombuffer(np.getbuffer(np.float32(oiioFloats)), dtype=np.float16)

if oiio.VERSION < 10800:
# Read float pixels into a numpy half-float pixel array
npPixels = np.frombuffer(np.getbuffer(np.float32(oiioFloats)), dtype=np.float16)
else:
# Convert uint16 values into a numpy half-float pixel array
npPixels = np.frombuffer(np.getbuffer(np.uint16(oiioFloats)), dtype=np.float16)

return npPixels
# oiioFloatPixelsToNPHalfArray
Expand All @@ -95,8 +100,12 @@ def oiioFloatPixelsToNPHalfArray(width, height, channels, oiioFloats):
# is a numpy array of half-float pixels.
# Each 4 byte 'float' is the binary equivalent of two packed 2-byte half-floats.
def npHalfArrayToOIIOFloatPixels(width, height, channels, npPixels):
# Read half-float pixels into a numpy float pixel array
oiioFloatsArray = np.frombuffer(np.getbuffer(np.float16(npPixels)), dtype=np.float32)
if oiio.VERSION < 10800:
# Read half-float pixels into a numpy float pixel array
oiioFloatsArray = np.frombuffer(np.getbuffer(np.float16(npPixels)), dtype=np.float32)
else:
# Read half-float pixels into a numpy float pixel array
oiioFloatsArray = np.frombuffer(np.getbuffer(np.float16(npPixels)), dtype=np.uint16)

return oiioFloatsArray
# npHalfArrayToOIIOFloatPixels
Expand Down Expand Up @@ -136,11 +145,14 @@ def readPixelArray(inputPath,
width = inputImageSpec.width
height = inputImageSpec.height
channels = inputImageSpec.nchannels
channelnames = inputImageSpec.channelnames
metadata = inputImageSpec.extra_attribs
metanames = [attr.name for attr in metadata]

bitShift = 0

print( "Loading image - spects : %d x %d - %d channels, %s bit depth, %s" % (width, height, channels, type, channelnames))

# Handle automatic bit-depth conversions
if bitDepthOverride:
if bitDepthOverride == "auto":
Expand Down Expand Up @@ -176,15 +188,15 @@ def readPixelArray(inputPath,
for i in range(len(sourceData)):
sourceData[i] = sourceData[i] >> bitShift

# OIIO doesn't return half-float values directly, so this will
# convert from the packed representation to half-floats
# OIIO versions < 1.8 didn't return half-float values directly
# so this will convert from the packed representation to half-floats
if type == oiio.HALF:
print( "Unpacking half-float values" )
sourceData = oiioFloatPixelsToNPHalfArray(width, height, channels, sourceData)
else:
(sourceData, bitDepth, width, height, channels, metadata) = (None, clf.bitDepths["UINT10"], 0, 0, 0, None)
(sourceData, bitDepth, width, height, channels, metadata) = (None, clf.bitDepths["UINT10"], 0, 0, 0, None, None)

return (sourceData, bitDepth, width, height, channels, metadata)
return (sourceData, bitDepth, width, height, channels, metadata, channelnames)
# readPixelArray

#
Expand All @@ -197,6 +209,7 @@ def writePixelArray(outputPath,
height,
channels,
metadata,
channelnames,
compression=None,
compressionQuality=0):
print( "Writing image - path : %s" % outputPath)
Expand Down Expand Up @@ -251,6 +264,7 @@ def writePixelArray(outputPath,
outputSpec.width = width
outputSpec.height = height
outputSpec.nchannels = channels
outputSpec.channelnames = channelnames

# Mapping between bit depth and 'oiio:BitsPerSample' metadata value
bitDepthValue = {
Expand Down Expand Up @@ -389,7 +403,7 @@ def filterRow_stride(row,

# Process values
#print( "Processing %04d, %04d : %s" % (i, j, ovalue))
pvalue = processList.process(pvalue, stride=channels)
pvalue = processList.process(pvalue, stride=channels, verbose=verbose)

# Reset values if output image and CLF output bit depths don't match
if OutRange:
Expand Down Expand Up @@ -477,7 +491,7 @@ def filterImageWithCLF(inputPath,
#
t0 = timeit.default_timer()

pixels, inBitDepth, width, height, channels, metadata = readPixelArray(inputPath)
pixels, inBitDepth, width, height, channels, metadata, channelnames = readPixelArray(inputPath)
if pixels == None:
print( "\nImage %s could not be opened. Filtering aborted.\n" % inputPath )
return
Expand Down Expand Up @@ -571,7 +585,9 @@ def filterImageWithCLF(inputPath,
else:
print( "Filtering image - single threaded" )

for j in range(height):
#for j in range(height):
j = 5
if True:
# Using filterRow_stride instead of filterRow_pixel
# Processing a full row is ~10% faster than processing individual pixels
filterRow_stride(j,
Expand All @@ -590,7 +606,7 @@ def filterImageWithCLF(inputPath,
t0 = timeit.default_timer()

writePixelArray(outputPath, processedPixels, outBitDepth, width, height, channels, metadata,
compression, compressionQuality)
channelnames, compression, compressionQuality)

t1 = timeit.default_timer()
elapsed = t1 - t0
Expand Down

0 comments on commit 47ba8be

Please sign in to comment.