def ImageBufWeight(weight, inputBuffer, gamma=0.75, clip=0.05, lut=None): ''' Apply a bell / triangular weight function to an Image Buffer ''' (channelType, width, height, channels, orientation, metadata, inputSpec) = ImageAttributes(inputBuffer) temp = ImageBufMakeConstant(width, height, channels, oiio.HALF) grey05 = ImageBufMakeConstant(width, height, channels, oiio.HALF, tuple([0.5] * channels)) if lut: ImageBufAlgo.add(temp, temp, inputBuffer) if 1 in lut: ImageBufAlgo.clamp(temp, temp, tuple([0.5] * channels), tuple([1.0] * channels)) if 2 in lut: ImageBufAlgo.clamp(temp, temp, tuple([0.0] * channels), tuple([0.5] * channels)) #print( "\tLUT application : %s" % result ) ImageBufAlgo.absdiff(temp, grey05, temp) else: ImageBufAlgo.absdiff(temp, grey05, inputBuffer) ImageBufAlgo.sub(temp, grey05, temp) ImageBufAlgo.div(temp, temp, 0.5) ImageBufAlgo.sub(temp, temp, clip) ImageBufAlgo.mul(temp, temp, 1.0 / (1.0 - clip)) ImageBufAlgo.clamp(temp, temp, tuple([0.0] * channels), tuple([1.0] * channels)) ImageBufAlgo.pow(weight, temp, gamma)
def ImageBufWeight(weight, inputBuffer, gamma=0.75, clip=0.05, lut=None): ''' Apply a bell / triangular weight function to an Image Buffer ''' (channelType, width, height, channels, orientation, metadata, inputSpec) = ImageAttributes(inputBuffer) temp = ImageBufMakeConstant(width, height, channels, oiio.HALF ) grey05 = ImageBufMakeConstant(width, height, channels, oiio.HALF, tuple([0.5]*channels) ) if lut: ImageBufAlgo.add(temp, temp, inputBuffer) if 1 in lut: ImageBufAlgo.clamp(temp, temp, tuple([0.5]*channels), tuple([1.0]*channels)) if 2 in lut: ImageBufAlgo.clamp(temp, temp, tuple([0.0]*channels), tuple([0.5]*channels)) #print( "\tLUT application : %s" % result ) ImageBufAlgo.absdiff(temp, grey05, temp) else: ImageBufAlgo.absdiff(temp, grey05, inputBuffer) ImageBufAlgo.sub(temp, grey05, temp) ImageBufAlgo.div(temp, temp, 0.5) ImageBufAlgo.sub(temp, temp, clip) ImageBufAlgo.mul(temp, temp, 1.0/(1.0-clip)) ImageBufAlgo.clamp(temp, temp, tuple([0.0]*channels), tuple([1.0]*channels)) ImageBufAlgo.pow(weight, temp, gamma)
# mul b = ImageBuf() ImageBufAlgo.mul(b, gray128, 1.5) write(b, "cmul1.exr") b = ImageBuf() ImageBufAlgo.mul(b, gray128, (1.5, 1, 0.5)) write(b, "cmul2.exr") # FIXME -- image multiplication; it's not in testsuite/oiiotool either # b = ImageBuf() # ImageBufAlgo.mul (b, make_constimage(64,64,3,oiio.HALF,(.1,.2,.3)), # make_constimage(64,64,3,oiio.HALF,(.1,.1,.1),20,20)) # write (b, "mul.exr") # div b = ImageBuf() ImageBufAlgo.div(b, gray64, make_constimage(64, 64, 3, oiio.HALF, (2.0, 1, 0.5))) write(b, "div.exr", oiio.HALF) b = ImageBuf() ImageBufAlgo.div(b, gray64, 2.0) write(b, "divc1.exr", oiio.HALF) b = ImageBuf() ImageBufAlgo.div(b, gray64, (2.0, 1, 0.5)) write(b, "divc2.exr", oiio.HALF) # pow b = ImageBuf() ImageBufAlgo.pow(b, gray128, 2) write(b, "cpow1.exr") b = ImageBuf() ImageBufAlgo.pow(b, gray128, (2, 2, 1)) write(b, "cpow2.exr")
write (b, "cmul2.exr") b = ImageBuf() ImageBufAlgo.mul (b, make_constimage(64,64,3,oiio.HALF,(.5,.5,.5)), make_constimage(64,64,3,oiio.HALF,(1.5,1,0.5))) write (b, "mul.exr", oiio.HALF) # mad b = ImageBuf() ImageBufAlgo.mad (b, make_constimage(64,64,3,oiio.HALF,(.5,.5,.5)), make_constimage(64,64,3,oiio.HALF,(1.5,1,0.5)), make_constimage(64,64,3,oiio.HALF,(0.1,0.1,0.1))) write (b, "mad.exr", oiio.HALF) # div b = ImageBuf() ImageBufAlgo.div (b, gray64, make_constimage (64, 64, 3, oiio.HALF, (2.0,1,0.5))) write (b, "div.exr", oiio.HALF) b = ImageBuf() ImageBufAlgo.div (b, gray64, 2.0) write (b, "divc1.exr", oiio.HALF) b = ImageBuf() ImageBufAlgo.div (b, gray64, (2.0,1,0.5)) write (b, "divc2.exr", oiio.HALF) # invert a = ImageBuf ("../oiiotool/tahoe-small.tif") b = ImageBuf() ImageBufAlgo.invert (b, a) write (b, "invert.tif", oiio.UINT8) # pow
def mkhdr(outputPath, inputPaths, responseLUTPaths, baseExposureIndex, writeIntermediate=False, outputGamut=1): ''' Create an HDR image from a series of individual exposures If the images are non-linear, a series of response LUTs can be used to linearize the data ''' global temp_dirs # Create buffers for inputs inputBuffers = [] inputAttributes = [] # Read images for inputPath in inputPaths: print("Reading input image : %s" % inputPath) # Read inputBufferRaw = loadImageBuffer(inputPath, outputGamut=outputGamut) # Reset the orientation ImageBufReorient(inputBufferRaw, inputBufferRaw.orientation) # Get attributes (channelType, width, height, channels, orientation, metadata, inputSpec) = ImageAttributes(inputBufferRaw) # Cast to half by adding with a const half buffer. inputBufferHalf = ImageBufMakeConstant(width, height, channels, oiio.HALF) ImageBufAlgo.add(inputBufferHalf, inputBufferHalf, inputBufferRaw) # Get exposure-specific information exposure = getExposureInformation(metadata) print("\tChannel Type : %s" % (channelType)) print("\tWidth : %s" % (width)) print("\tHeight : %s" % (height)) print("\tChannels : %s" % (channels)) print("\tOrientation : %s" % (orientation)) print("\tMetadata # : %s" % (len(metadata))) print("\tExposure : %s" % (exposure)) # Store pixels and image attributes inputBuffers.append(inputBufferHalf) inputAttributes.append((channelType, width, height, channels, orientation, metadata, exposure, inputSpec)) # Get the base exposure information # All other images will be scaled to match this exposure if baseExposureIndex >= 0: baseExposureIndex = max(0, min(len(inputPaths) - 1, baseExposureIndex)) else: multithreaded = True if multithreaded: threads = cpu_count() baseExposureIndex = findBaseExposureIndexMultithreaded( inputPaths, width, height, channels, threads) else: baseExposureIndex = findBaseExposureIndexSerial( inputBuffers, width, height, channels) baseExposureInfo = inputAttributes[baseExposureIndex][6] baseInputspec = inputAttributes[baseExposureIndex][7] print("") print("Base exposure index : %d" % baseExposureIndex) print("Base exposure info : %s" % baseExposureInfo) # Find the lowest and highest exposures exposureAdjustments = [ getExposureAdjustment(x[6], baseExposureInfo) for x in inputAttributes ] minExposureOffsetIndex = exposureAdjustments.index( min(exposureAdjustments)) maxExposureOffsetIndex = exposureAdjustments.index( max(exposureAdjustments)) print("Max exposure index : %d" % minExposureOffsetIndex) print("Min exposure index : %d" % maxExposureOffsetIndex) print("\nBegin processing\n") # Two buffers needed for algorithm imageSum = ImageBufMakeConstant(width, height, channels, oiio.HALF, inputSpec=baseInputspec) weightSum = ImageBufMakeConstant(width, height, channels, oiio.HALF) # Re-used intermediate buffers color = ImageBufMakeConstant(width, height, channels, oiio.HALF) weight = ImageBufMakeConstant(width, height, channels, oiio.HALF) weightedColor = ImageBufMakeConstant(width, height, channels, oiio.HALF) # Process images for inputIndex in range(len(inputPaths)): inputPathComponents = (os.path.splitext(inputPaths[inputIndex])[0], ".exr") intermediate = 0 ImageBufAlgo.zero(color) ImageBufAlgo.zero(weight) ImageBufAlgo.zero(weightedColor) print("Processing input image : %s" % inputPaths[inputIndex]) inputBuffer = inputBuffers[inputIndex] # Copy the input buffer data ImageBufAlgo.add(color, color, inputBuffer) if writeIntermediate: intermediatePath = "%s_int%d.float_buffer%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(color, intermediatePath) # Linearize using LUTs if responseLUTPaths: for responseLUTPath in responseLUTPaths: print("\tApplying LUT %s" % responseLUTPath) ImageBufAlgo.ociofiletransform( color, color, os.path.abspath(responseLUTPath)) if writeIntermediate: intermediatePath = "%s_int%d.linearized%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(color, intermediatePath) # Get exposure offset inputExposureInfo = inputAttributes[inputIndex][6] exposureAdjustment = getExposureAdjustment(inputExposureInfo, baseExposureInfo) exposureScale = pow(2, exposureAdjustment) print("\tScaling by %s stops (%s mul)" % (exposureAdjustment, exposureScale)) # Re-expose input ImageBufAlgo.mul(color, color, exposureScale) if writeIntermediate: intermediatePath = "%s_int%d.exposure_adjust%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(color, intermediatePath) print("\tComputing image weight") # Weight lut = [] if inputIndex == minExposureOffsetIndex: lut.append(1) if inputIndex == maxExposureOffsetIndex: lut.append(2) if lut: print("\tUsing LUT %s in weighting calculation" % lut) weightIntermediate = intermediate if writeIntermediate else 0 ImageBufWeight(weight, inputBuffer, lut=lut) if writeIntermediate: intermediatePath = "%s_int%d.weight%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(weight, intermediatePath) print("\tMultiply by weight") # Multiply color by weight ImageBufAlgo.mul(weightedColor, weight, color) if writeIntermediate: intermediatePath = "%s_int%d.color_x_weight%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(weightedColor, intermediatePath) print("\tAdd values into sum") # Sum weighted color and weight ImageBufAlgo.add(imageSum, imageSum, weightedColor) ImageBufAlgo.add(weightSum, weightSum, weight) if writeIntermediate: intermediatePath = "%s_int%d.color_x_weight_sum%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(imageSum, intermediatePath) intermediatePath = "%s_int%d.weight_sum%s" % ( inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(weightSum, intermediatePath) # Divid out weights print("Dividing out weights") ImageBufAlgo.div(imageSum, imageSum, weightSum) # Write to disk print("Writing result : %s" % outputPath) ImageBufWrite(imageSum, outputPath) # Clean up temp folders for temp_dir in temp_dirs: #print( "Removing : %s" % temp_dir ) shutil.rmtree(temp_dir)
def mkhdr(outputPath, inputPaths, responseLUTPaths, baseExposureIndex, writeIntermediate = False, outputGamut = 1, compression = None, compressionQuality = 0, rawSaturationPoint = -1.0, alignImages = False, dcrawVariant = None): ''' Create an HDR image from a series of individual exposures If the images are non-linear, a series of response LUTs can be used to linearize the data ''' global temp_dirs # Set up capture of old_stdout, old_stderr = sys.stdout, sys.stderr redirected_stdout = sys.stdout = Logger() redirected_stderr = sys.stderr = Logger() # Create buffers for inputs inputBuffers = [] inputAttributes = [] # Read images for inputPath in inputPaths: print( "Reading input image : %s" % inputPath ) # Read inputBufferRaw = loadImageBuffer( inputPath, outputGamut=outputGamut, rawSaturationPoint=rawSaturationPoint, dcrawVariant=dcrawVariant ) # Reset the orientation print( "\tRaw Orientation : %d" % inputBufferRaw.orientation) ImageBufReorient(inputBufferRaw, inputBufferRaw.orientation) # Get attributes (channelType, width, height, channels, orientation, metadata, inputSpec) = ImageAttributes(inputBufferRaw) # Cast to half by adding with a const half buffer. inputBufferHalf = ImageBufMakeConstant(width, height, channels, oiio.HALF) ImageBufAlgo.add(inputBufferHalf, inputBufferHalf, inputBufferRaw) # Get exposure-specific information exposure = getExposureInformation(metadata) print( "\tChannel Type : %s" % (channelType) ) print( "\tWidth : %s" % (width) ) print( "\tHeight : %s" % (height) ) print( "\tChannels : %s" % (channels) ) print( "\tOrientation : %s" % (orientation) ) print( "\tExposure : %s" % (exposure) ) print( "\tMetadata # : %s" % (len(metadata)) ) # Store pixels and image attributes inputBuffers.append( inputBufferHalf ) inputAttributes.append( (channelType, width, height, channels, orientation, metadata, exposure, inputSpec) ) # Get the base exposure information # All other images will be scaled to match this exposure if baseExposureIndex >= 0: baseExposureIndex = max(0, min(len(inputPaths)-1, baseExposureIndex)) else: multithreaded = True if multithreaded: threads = cpu_count() baseExposureIndex = findBaseExposureIndexMultithreaded(inputPaths, width, height, channels, threads) else: baseExposureIndex = findBaseExposureIndexSerial(inputBuffers, width, height, channels) baseExposureMetadata = inputAttributes[baseExposureIndex][5] baseExposureInfo = inputAttributes[baseExposureIndex][6] baseInputspec = inputAttributes[baseExposureIndex][7] print( "" ) print( "Base exposure index : %d" % baseExposureIndex ) print( "Base exposure info : %s" % baseExposureInfo ) # Find the lowest and highest exposures exposureAdjustments = [getExposureAdjustment(x[6], baseExposureInfo) for x in inputAttributes] minExposureOffsetIndex = exposureAdjustments.index(min(exposureAdjustments)) maxExposureOffsetIndex = exposureAdjustments.index(max(exposureAdjustments)) print( "Max exposure index : %d" % minExposureOffsetIndex ) print( "Min exposure index : %d" % maxExposureOffsetIndex ) print( "\nBegin processing\n" ) # Two buffers needed for algorithm imageSum = ImageBufMakeConstant(width, height, channels, oiio.HALF, inputSpec=baseInputspec) weightSum = ImageBufMakeConstant(width, height, channels, oiio.HALF) # Re-used intermediate buffers color = ImageBufMakeConstant(width, height, channels, oiio.HALF) weight = ImageBufMakeConstant(width, height, channels, oiio.HALF) weightedColor = ImageBufMakeConstant(width, height, channels, oiio.HALF) # Process images for inputIndex in range(len(inputPaths)): inputPathComponents = (os.path.splitext( inputPaths[inputIndex] )[0], ".exr") intermediate = 0 ImageBufAlgo.zero( color ) ImageBufAlgo.zero( weight ) ImageBufAlgo.zero( weightedColor ) print( "Processing input image : %s" % inputPaths[inputIndex] ) inputBuffer = inputBuffers[inputIndex] # Copy the input buffer data ImageBufAlgo.add(color, color, inputBuffer) if writeIntermediate: intermediatePath = "%s_int%d.float_buffer%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(color, intermediatePath) # Find the image alignment matrix to align this exposure with the base exposure if alignImages: try: if inputIndex != baseExposureIndex: if cv2: print( "\tAligning image %d to base exposure %d " % (inputIndex, baseExposureIndex) ) warpMatrix = find2dAlignmentMatrix(inputBuffer, inputBuffers[baseExposureIndex]) # reformat for OIIO's warp w = map(float, list(warpMatrix.reshape(1,-1)[0])) warpTuple = (w[0], w[1], 0.0, w[3], w[4], 0.0, w[2], w[5], 1.0) print( warpTuple ) warped = ImageBuf() result = ImageBufAlgo.warp(warped, color, warpTuple) if result: print( "\tImage alignment warp succeeded." ) if writeIntermediate: intermediatePath = "%s_int%d.warped%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(warped, intermediatePath) color = warped else: print( "\tImage alignment warp failed." ) if writeIntermediate: intermediate += 1 else: print( "\tSkipping image alignment. OpenCV not defined" ) if writeIntermediate: intermediate += 1 else: print( "\tSkipping alignment of base exposure to itself") if writeIntermediate: intermediate += 1 except: print( "Exception in image alignment" ) print( '-'*60 ) traceback.print_exc() print( '-'*60 ) # Weight print( "\tComputing image weight" ) lut = [] if inputIndex == minExposureOffsetIndex: lut.append(1) if inputIndex == maxExposureOffsetIndex: lut.append(2) if lut: print( "\tUsing LUT %s in weighting calculation" % lut ) ImageBufWeight(weight, color, lut=lut) if writeIntermediate: intermediatePath = "%s_int%d.weight%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(weight, intermediatePath) # Linearize using LUTs if responseLUTPaths: for responseLUTPath in responseLUTPaths: print( "\tApplying LUT %s" % responseLUTPath ) ImageBufAlgo.ociofiletransform(color, color, os.path.abspath(responseLUTPath) ) if writeIntermediate: intermediatePath = "%s_int%d.linearized%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(color, intermediatePath) # Get exposure offset inputExposureInfo = inputAttributes[inputIndex][6] exposureAdjustment = getExposureAdjustment(inputExposureInfo, baseExposureInfo) exposureScale = pow(2, exposureAdjustment) # Re-expose input print( "\tScaling by %s stops (%s mul)" % (exposureAdjustment, exposureScale) ) ImageBufAlgo.mul(color, color, exposureScale) if writeIntermediate: intermediatePath = "%s_int%d.exposure_adjust%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(color, intermediatePath) # Multiply color by weight print( "\tMultiply by weight" ) ImageBufAlgo.mul(weightedColor, weight, color) if writeIntermediate: intermediatePath = "%s_int%d.color_x_weight%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(weightedColor, intermediatePath) print( "\tAdd values into sum" ) # Sum weighted color and weight ImageBufAlgo.add(imageSum, imageSum, weightedColor) ImageBufAlgo.add(weightSum, weightSum, weight) if writeIntermediate: intermediatePath = "%s_int%d.color_x_weight_sum%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(imageSum, intermediatePath) intermediatePath = "%s_int%d.weight_sum%s" % (inputPathComponents[0], intermediate, inputPathComponents[1]) intermediate += 1 ImageBufWrite(weightSum, intermediatePath) # Divid out weights print( "Dividing out weights" ) ImageBufAlgo.div(imageSum, imageSum, weightSum) # Write to disk print( "Writing result : %s" % outputPath ) # Restore regular streams sys.stdout, sys.stderr = old_stdout, old_stderr additionalAttributes = {} additionalAttributes['inputPaths'] = " ".join(inputPaths) additionalAttributes['stdout'] = "".join(redirected_stdout.log) additionalAttributes['stderr'] = "".join(redirected_stderr.log) ImageBufWrite(imageSum, outputPath, compression=compression, compressionQuality=compressionQuality, metadata=baseExposureMetadata, additionalAttributes=additionalAttributes) # Clean up temp folders for temp_dir in temp_dirs: #print( "Removing : %s" % temp_dir ) shutil.rmtree(temp_dir) for temp_dir in temp_dirs: temp_dirs.remove(temp_dir)