View SourceEvision Example - High Dynamic Range Imaging
Mix.install([{:evision,"~> 0.2"},{:kino,"~> 0.7"},{:req,"~> 0.5"}],system_env:[# optional, defaults to `true`# set `EVISION_PREFER_PRECOMPILED` to `false`# if you prefer `:evision` to be compiled from source# note that to compile from source, you may need at least 1GB RAM{"EVISION_PREFER_PRECOMPILED",true},# optional, defaults to `true`# set `EVISION_ENABLE_CONTRIB` to `false`# if you don't need modules from `opencv_contrib`{"EVISION_ENABLE_CONTRIB",true},# optional, defaults to `false`# set `EVISION_ENABLE_CUDA` to `true`# if you wish to use CUDA related functions# note that `EVISION_ENABLE_CONTRIB` also has to be `true`# because cuda related modules come from the `opencv_contrib` repo{"EVISION_ENABLE_CUDA",false},# required when# - `EVISION_ENABLE_CUDA` is `true`# - and `EVISION_PREFER_PRECOMPILED` is `true`## set `EVISION_CUDA_VERSION` to the version that matches# your local CUDA runtime version## current available versions are# - 118# - 121{"EVISION_CUDA_VERSION","118"},# require for Windows users when# - `EVISION_ENABLE_CUDA` is `true`# set `EVISION_CUDA_RUNTIME_DIR` to the directory that contains# CUDA runtime libraries{"EVISION_CUDA_RUNTIME_DIR","C:/PATH/TO/CUDA/RUNTIME"}])
:ok
Define Some Helper Functions
defmoduleHelperdodefdownload!(url,save_as,overwrite?\\false)dounlessFile.exists?(save_as)doReq.get!(url,http_errors::raise,into:File.stream!(save_as),cache:notoverwrite?)end:okend@doc"""
This function chunks binary data by every requested `chunk_size`
To make it more general, this function allows the length of the last chunk
to be less than the request `chunk_size`.
For example, if you have a 7-byte binary data, and you'd like to chunk it by every
4 bytes, then this function will return two chunks with the first gives you the
byte 0 to 3, and the second one gives byte 4 to 6.
"""defchunk_binary(binary,chunk_size)whenis_binary(binary)dototal_bytes=byte_size(binary)full_chunks=div(total_bytes,chunk_size)chunks=iffull_chunks>0dofori<-0..(full_chunks-1),reduce:[]doacc->[:binary.part(binary,chunk_size*i,chunk_size)|acc]endelse[]endremaining=rem(total_bytes,chunk_size)chunks=ifremaining>0do[:binary.part(binary,chunk_size*full_chunks,remaining)|chunks]elsechunksendEnum.reverse(chunks)endend
aliasEvision,as:Cv# change to the file's directory# or somewhere you have write permissionFile.cd!(__DIR__)# create a directory for storing the test imagesFile.mkdir_p!("photo_hdr_test")exposure_filenames=0..15|>Enum.map(&Integer.to_string(&1))|>Enum.map(&String.pad_leading(&1,2,"0"))|>Enum.map(&("memorial"<>&1<>".png"))exposure_file_urls=exposure_filenames|>Enum.map(&("https://raw.githubusercontent.com/opencv/opencv_extra/4.x/testdata/cv/hdr/exposures/"<>&1))exposure_file_save_paths=exposure_filenames|>Enum.map(&Path.join([__DIR__,"photo_hdr_test",&1]))exposure_file_urls|>Enum.zip(exposure_file_save_paths)|>Enum.map(fn{url,save_as}->Helper.download!(url,save_as)end)|>Enum.all?(&(:ok=&1))
true
Download list.txt that Stores the Exposure Sequences
# Firstly we load input images and exposure times from user-defined folder.# The folder should contain images and list.txt - file that contains file names and inverse exposure times.exposure_sequences=list_txt_file|>File.read!()|>String.split("\n")|>Enum.reject(&(String.length(&1)==0))|>Enum.map(&String.split(&1," "))|>Enum.map(&List.to_tuple(&1))|>Enum.map(fn{image_filename,times}->mat=Cv.imread(Path.join([__DIR__,"photo_hdr_test",image_filename])){val,""}=Float.parse(times){mat,1/val}end)images=exposure_sequences|>Enum.map(&elem(&1,0))# `times` HAS to be float32, otherwise OpenCV will crashtimes=exposure_sequences|>Enum.map(&elem(&1,1))|>Enum.into(<<>>,fnd-><<d::float-size(32)-little>>end)|>Cv.Mat.from_binary_by_shape({:f,32},{1,Enum.count(images)})
# It is necessary to know camera response function (CRF) for a lot of HDR construction algorithms.# We use one of the calibration algorithms to estimate inverse CRF for all 256 pixel values.calibrate=Cv.createCalibrateDebevec()response=Cv.CalibrateDebevec.process(calibrate,images,times)
# We use Debevec's weighting scheme to construct HDR image# using response calculated in the previous item.merge_debevec=Cv.createMergeDebevec()hdr=Cv.MergeDebevec.process(merge_debevec,images,times,response:response)
# Since we want to see our results on common LDR display we have to map our HDR image to 8-bit range# preserving most details.# It is the main goal of tonemapping methods.# We use tonemapper with bilateral filtering and set 2.2 as the value for gamma correction.tonemap=Cv.createTonemap(gamma:2.2)ldr=Cv.Tonemap.process(tonemap,hdr)
# There is an alternative way to merge our exposures in case when we don't need HDR image.# This process is called exposure fusion and produces LDR image that doesn't require gamma correction.# It also doesn't use exposure values of the photographs.merge_mertens=Cv.createMergeMertens()fusion=Cv.MergeMertens.process(merge_mertens,images)