def findAverageWeightFromPath(inputPath, width, height, channels): ''' Find the average weight of an image, specified by it's file path ''' weight = ImageBufMakeConstant(width, height, channels, oiio.HALF) temp = ImageBufMakeConstant(1, 1, channels, oiio.HALF) try: print("\tReading image : %s" % inputPath) inputBufferRaw = ImageBuf(inputPath) # Cast to half by adding with a const half buffer. inputBufferHalf = ImageBufMakeConstant(width, height, channels, oiio.HALF) ImageBufAlgo.add(inputBufferHalf, inputBufferHalf, inputBufferRaw) print("\tComputing Weight") ImageBufWeight(weight, inputBufferHalf) # Compute the average weight by resizing to 1x1 print("\tResizing") # Not using multithreading here, as this function will be called within # Python's multhreading framework ImageBufAlgo.resize(temp, weight, filtername='box') # Get the average weight value weight = temp.getpixel(0, 0) #print( "\tWeight : %s" % str(weight) ) averageWeight = sum(map(float, weight)) / channels except Exception, e: print("Exception in findAverageWeightFromPath") print(repr(e))
def writeTexture(scrBuffer, outFile): global errorFlag try: config = ImageSpec() # config.attribute("maketx:highlightcomp", 1) config.attribute("maketx:filtername", "lanczos3") # config.attribute("maketx:opaquedetect", 1) config.attribute("maketx:oiio options", 1) scrBuffer.set_write_tiles(tileSize, tileSize) ImageBufAlgo.make_texture(oiio.MakeTxTexture, scrBuffer, outFile, config) errorFlag = 0 except Exception as e: errorFlag = 1 tqdm.write( prefix + Fore.RED + "Error on conversion. Maybe wrong/corrupt texture file or resolution too high." )
def findAverageWeightFromPath(inputPath, width, height, channels): ''' Find the average weight of an image, specified by it's file path ''' weight = ImageBufMakeConstant(width, height, channels, oiio.HALF) temp = ImageBufMakeConstant(1, 1, channels, oiio.HALF) try: print( "\tReading image : %s" % inputPath ) inputBufferRaw = ImageBuf( inputPath ) # Cast to half by adding with a const half buffer. inputBufferHalf = ImageBufMakeConstant(width, height, channels, oiio.HALF) ImageBufAlgo.add(inputBufferHalf, inputBufferHalf, inputBufferRaw) print( "\tComputing Weight" ) ImageBufWeight(weight, inputBufferHalf) # Compute the average weight by resizing to 1x1 print( "\tResizing" ) # Not using multithreading here, as this function will be called within # Python's multhreading framework ImageBufAlgo.resize(temp, weight, filtername='box') # Get the average weight value weight = temp.getpixel(0,0) #print( "\tWeight : %s" % str(weight) ) averageWeight = sum(map(float, weight))/channels except Exception, e: print( "Exception in findAverageWeightFromPath" ) print( repr(e) )
def __init__(self, image_a, image_b): self.debug = False self.fail_threshold = 0.1 self.warn_threshold = 0.01 self.image_a_buffer = ImageBuf() self.image_b_buffer = ImageBuf() # remove alpha channel from input images ImageBufAlgo.channels( self.image_a_buffer, ImageBuf(image_a), ('R', 'G', 'B') ) ImageBufAlgo.channels( self.image_b_buffer, ImageBuf(image_b), ('R', 'G', 'B'), ) # protected self._image_a_location = image_a self._image_b_location = image_b self._file_ext = os.path.splitext(image_a)[-1] self._compare_results = CompareResults()
def _open(self, source, size=3): erode = ImageBuf(source.spec()) ImageBufAlgo.erode(erode, source, size, size) dilate = ImageBuf(source.spec()) ImageBufAlgo.dilate(dilate, erode, size, size) return dilate
def resizeHDR(scrBuffer, width, height): srcSpec = scrBuffer.spec() resizedBuffer = ImageBuf( ImageSpec(width, height, srcSpec.nchannels, srcSpec.format)) ImageBufAlgo.resize(resizedBuffer, scrBuffer, filtername=resizeFilter) threadResult.put(resizedBuffer) return resizedBuffer
def blurImage(srcBuffer): K = ImageBuf() ImageBufAlgo.make_kernel(K, blurFilter, blurAmountX, blurAmountY) Blurred = ImageBuf() ImageBufAlgo.convolve(Blurred, srcBuffer, K) threadResult.put(Blurred) return Blurred
def _dilate(self, source): dilate = ImageBuf(source.spec()) ImageBufAlgo.dilate( dilate, source, 4, 4, ) return dilate
def _median(self, source, size=5): size = int(size) median = ImageBuf(source.spec()) ImageBufAlgo.median_filter( median, source, size, size ) return median
def compare_image_pixels(self, result_image, correct_result_image, threshold): self.fail_test_if_no_oiio() """ Given a file to find it results, compare it against the correct result. Returns None if the input is not an image. Returns oiio.CompareResults if results can be compared. """ compresults = oiio.CompareResults() ImageBufAlgo.compare(result_image, correct_result_image, threshold, threshold, compresults) return compresults
def create_diff_buffer(self): """Create a difference image buffer from image_a and image_b Returns: ImageBuf: new difference image buffer """ diff_buffer = ImageBuf(self.image_a_buffer.spec()) ImageBufAlgo.sub(diff_buffer, self.image_a_buffer, self.image_b_buffer) ImageBufAlgo.abs(diff_buffer, diff_buffer) return diff_buffer
def findAverageWeight(imageBuffer, width, height, channels): ''' Get the average value of the weighted image ''' weight = ImageBufMakeConstant(width, height, channels, oiio.HALF) temp = ImageBufMakeConstant(1, 1, channels, oiio.HALF) print("\tComputing Weight") ImageBufWeight(weight, imageBuffer) # Compute the average weight by resizing to 1x1 print("\tResizing") # The nthreads argument doesn't seem to have much effect ImageBufAlgo.resize(temp, weight, nthreads=cpu_count(), filtername='box') # Get the average weight value averageWeight = sum(map(float, temp.getpixel(0, 0))) / channels return averageWeight
def findAverageWeight(imageBuffer, width, height, channels): ''' Get the average value of the weighted image ''' weight = ImageBufMakeConstant(width, height, channels, oiio.HALF) temp = ImageBufMakeConstant(1, 1, channels, oiio.HALF) print( "\tComputing Weight" ) ImageBufWeight(weight, imageBuffer) # Compute the average weight by resizing to 1x1 print( "\tResizing" ) # The nthreads argument doesn't seem to have much effect ImageBufAlgo.resize(temp, weight, nthreads=cpu_count(), filtername='box') # Get the average weight value averageWeight = sum(map(float, temp.getpixel(0,0)))/channels return averageWeight
def ImageBufReorient(imageBuf, orientation): ''' Resets the orientation of the image ''' ''' Orientation 6 and 8 seem to be reversed in OIIO, at least for Canon cameras... This needs to be investigated further. ''' if orientation == 6: imageBuf.specmod().attribute("Orientation", 1) ImageBufAlgo.rotate270(imageBuf, imageBuf) ImageBufAlgo.reorient(imageBuf, imageBuf) elif orientation == 8: imageBuf.specmod().attribute("Orientation", 1) ImageBufAlgo.rotate90(imageBuf, imageBuf) ImageBufAlgo.reorient(imageBuf, imageBuf) else: ImageBufAlgo.reorient(imageBuf, imageBuf)
def ImageBufReorient(imageBuf, orientation): ''' Resets the orientation of the image ''' ''' Orientation 6 and 8 seem to be reversed in OIIO, at least for Canon cameras... This needs to be investigated further. ''' if orientation == 6: imageBuf.specmod().attribute ("Orientation", 1) ImageBufAlgo.rotate90(imageBuf, imageBuf) ImageBufAlgo.reorient (imageBuf, imageBuf) elif orientation == 8: imageBuf.specmod().attribute ("Orientation", 1) ImageBufAlgo.rotate270(imageBuf, imageBuf) ImageBufAlgo.reorient (imageBuf, imageBuf) else: ImageBufAlgo.reorient (imageBuf, imageBuf)
def _blur(self, source, size=1.0): """Apply gaussian blur to given image Args: source (ImageBuf): Image buffer which to blur size (float): Blur size Return: ImageBuf: Blurred image """ source = self._open(source) kernel = ImageBuf(source.spec()) ImageBufAlgo.make_kernel( kernel, "gaussian", size, size ) blurred = ImageBuf(source.spec()) ImageBufAlgo.convolve(blurred, source, kernel) return blurred
def main(): options, args = parseOptions() tile_x = options.tile_x tile_y = options.tile_y frame = options.frame output = options.output filemask = options.filemask tile_files = [] tiles_lost = [] for i in xrange(0, (tile_x * tile_y)): filepath = filemask % (i, frame) if not os.path.exists(filepath): tiles_lost += [filepath] continue tile_files += [filepath] if len(tile_files) != (tile_x * tile_y): raise Exception("Tile not found: %s" % tiles_lost) #TODO: merge metadata from tiles spec = ImageBuf(str(tile_files[0])).spec() spec_e = ImageSpec(spec.full_width, spec.full_height, spec.nchannels, spec.format) extended = ImageBuf(spec_e) for filename in tile_files: img = ImageBuf(filename) ImageBufAlgo.paste(extended, img.xbegin, img.ybegin, img.zbegin, 0, img, nthreads=4) extended.write(output)
if not image.has_error: image.set_write_format(format) image.write(filename) if image.has_error: print "Error writing", filename, ":", image.geterror() ###################################################################### # main test starts here try: # Some handy images to work with gridname = "../../../../../oiio-images/grid.tif" grid = ImageBuf(gridname) checker = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.checker(checker, 8, 8, 8, (0, 0, 0), (1, 1, 1)) gray128 = make_constimage(128, 128, 3, oiio.HALF, (0.5, 0.5, 0.5)) gray64 = make_constimage(64, 64, 3, oiio.HALF, (0.5, 0.5, 0.5)) # black b = ImageBuf(ImageSpec(320, 240, 3, oiio.UINT8)) ImageBufAlgo.zero(b) write(b, "black.tif") # fill (including use of ROI) b = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.fill(b, (1, 0.5, 0.5)) ImageBufAlgo.fill(b, (0, 1, 0), oiio.ROI(100, 180, 100, 180)) write(b, "filled.tif") # checker
def compare(self, diff_image_location=None, blur=10, raise_exception=True): """Compare the two given images Args: diff_image_location (str): file path for difference image. Written only if there are failures blur (float): image blur to apply before comparing """ if not diff_image_location: diff_image_location = os.path.dirname(self._image_a_location) self.blur_images(blur) ImageBufAlgo.compare( self.image_a_buffer, self.image_b_buffer, self.fail_threshold, self.warn_threshold, self._compare_results, ) diff_buffer = self.create_diff_buffer() if self.debug: self.image_a_buffer.write( '{}/{}_debug{}'.format( diff_image_location, os.path.basename(self._image_a_location), self._file_ext, ) ) self.image_b_buffer.write( '{}/{}_debug{}'.format( diff_image_location, os.path.basename(self._image_b_location), self._file_ext, ) ) if self._compare_results.nfail > 0: ImageBufAlgo.color_map(diff_buffer, diff_buffer, -1, 'inferno') remap_buffer = ImageBuf() multiplier = 5 ImageBufAlgo.mul( remap_buffer, diff_buffer, (multiplier, multiplier, multiplier, 1.0), ) ImageBufAlgo.add(remap_buffer, self.image_a_buffer, remap_buffer) msg = report_msg.format( failures=self._compare_results.nfail, warn=self._compare_results.nwarn, meanerror=self._compare_results.meanerror, rmserror=self._compare_results.rms_error, psnr=self._compare_results.PSNR ) remap_buffer.write( '{}/{}-{}_diff{}'.format( diff_image_location, os.path.basename(self._image_a_location), os.path.basename(self._image_b_location), self._file_ext, ) ) self.image_a_buffer.write( '{}/{}_debug{}'.format( diff_image_location, '1_a', self._file_ext, ) ) self.image_b_buffer.write( '{}/{}_debug{}'.format( diff_image_location, '1_b', self._file_ext, ) ) if raise_exception: raise ImageDifferenceError(msg) else: print(msg)
if not image.has_error : image.write (filename, format) if image.has_error : print ("Error writing", filename, ":", image.geterror()) ###################################################################### # main test starts here try: # Some handy images to work with gridname = os.path.join(OIIO_TESTSUITE_IMAGEDIR, "grid.tif") grid = ImageBuf (gridname) checker = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.checker (checker, 8, 8, 8, (0,0,0), (1,1,1)) gray128 = make_constimage (128, 128, 3, oiio.HALF, (0.5,0.5,0.5)) gray64 = make_constimage (64, 64, 3, oiio.HALF, (0.5,0.5,0.5)) tahoetiny = ImageBuf("../oiiotool/src/tahoe-tiny.tif") # black # b = ImageBuf (ImageSpec(320,240,3,oiio.UINT8)) b = ImageBufAlgo.zero (roi=oiio.ROI(0,320,0,240,0,1,0,3)) write (b, "black.tif", oiio.UINT8) # fill (including use of ROI) b = ImageBuf (ImageSpec(256,256,3,oiio.UINT8)); ImageBufAlgo.fill (b, (1,0.5,0.5)) ImageBufAlgo.fill (b, (0,1,0), oiio.ROI(100,180,100,180)) write (b, "filled.tif", oiio.UINT8)
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 convertColor(srcBuffer, fromColor="linear", toColor="sRGB"): Dst = ImageBuf() ImageBufAlgo.colorconvert(Dst, srcBuffer, fromColor, toColor) threadResult.put(Dst) return Dst
print("[", end="") for c in range(spec.nchannels): print(fmt.format(p[c]), end=" ") print("] ", end="") print("") ###################################################################### # main test starts here try: # Some handy images to work with gridname = os.path.join(OIIO_TESTSUITE_IMAGEDIR, "grid.tif") grid = ImageBuf(gridname) checker = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.checker(checker, 8, 8, 8, (0, 0, 0), (1, 1, 1)) gray128 = make_constimage(128, 128, 3, oiio.HALF, (0.5, 0.5, 0.5)) gray64 = make_constimage(64, 64, 3, oiio.HALF, (0.5, 0.5, 0.5)) tahoetiny = ImageBuf(OIIO_TESTSUITE_ROOT + "/oiiotool/src/tahoe-tiny.tif") # black # b = ImageBuf (ImageSpec(320,240,3,oiio.UINT8)) b = ImageBufAlgo.zero(roi=oiio.ROI(0, 320, 0, 240, 0, 1, 0, 3)) write(b, "black.tif", oiio.UINT8) # fill (including use of ROI) b = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.fill(b, (1, 0.5, 0.5)) ImageBufAlgo.fill(b, (0, 1, 0), oiio.ROI(100, 180, 100, 180)) write(b, "filled.tif", oiio.UINT8)
def premultiply_image(cls, img_pixels: np.array) -> np.array: """ Premultiply a numpy image with itself """ a = cls.np_to_imagebuf(img_pixels) ImageBufAlgo.premult(a, a) return a.get_pixels(a.spec().format, a.spec().roi_full)
image.set_write_format (format) image.write (filename) if image.has_error : print "Error writing", filename, ":", image.geterror() ###################################################################### # main test starts here try: # Some handy images to work with gridname = "../../../../../oiio-images/grid.tif" grid = ImageBuf (gridname) checker = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.checker (checker, 8, 8, 8, (0,0,0), (1,1,1)) gray128 = make_constimage (128, 128, 3, oiio.HALF, (0.5,0.5,0.5)) gray64 = make_constimage (64, 64, 3, oiio.HALF, (0.5,0.5,0.5)) # black b = ImageBuf (ImageSpec(320,240,3,oiio.UINT8)) ImageBufAlgo.zero (b) write (b, "black.tif") # fill (including use of ROI) b = ImageBuf (ImageSpec(256,256,3,oiio.UINT8)); ImageBufAlgo.fill (b, (1,0.5,0.5)) ImageBufAlgo.fill (b, (0,1,0), oiio.ROI(100,180,100,180)) write (b, "filled.tif") # checker
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)
if not image.has_error: image.set_write_format(format) image.write(filename) if image.has_error: print "Error writing", filename, ":", image.geterror() ###################################################################### # main test starts here try: # Some handy images to work with gridname = "../../../../../oiio-images/grid.tif" grid = ImageBuf(gridname) checker = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.checker(checker, 8, 8, 8, (0, 0, 0), (1, 1, 1)) gray128 = make_constimage(128, 128, 3, oiio.HALF, (0.5, 0.5, 0.5)) # black b = ImageBuf(ImageSpec(320, 240, 3, oiio.UINT8)) ImageBufAlgo.zero(b) write(b, "black.tif") # fill (including use of ROI) b = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8)) ImageBufAlgo.fill(b, (1, 0.5, 0.5)) ImageBufAlgo.fill(b, (0, 1, 0), oiio.ROI(100, 180, 100, 180)) write(b, "filled.tif") # checker b = ImageBuf(ImageSpec(256, 256, 3, oiio.UINT8))
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)