Esempio n. 1
0
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)
Esempio n. 2
0
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)