Create a gist now

Instantly share code, notes, and snippets.

@pigeonhill /h3s.lua
Last active Mar 20, 2018

Embed
What would you like to do?
Hand Held Helper Script
--Hand Held Helper Script (H3S) Release 1.05
--[[
********************************************************************************************
This script attempts to capture 'the best' bracket sequences when hand holding.
Bracketing choices are Exposure or Focus.
In Focus mode the script creates a two image focus stack via aperture bracketing, and attempts to maintain the same exposure between two limits: the minimum handheld shutter and the maximum ISO.
The exposure bracketing mode is based on ISO shifting as well as time shifting.
The general strategy being to time shift from the highlights at the lowst ISO you can, and ISO shift from the shadows from the lowest Tv you can, at ISO 100. That is try and maximise photon capture, ie for the highest DR.
Note pushing ISO too far is pointless, as once above a critical level, Canon cameras go into ISO invariant zone (varies with camera) and overall DR falls with no increase in (read) noise.
You should also be high enough to be above the 'pattern noise', but not too high.
On a 5D3 a figure of 6400 seems to work well, on an EOSM, say, maybe try 1600 or 3200.
The script is triggered by a 3+ sec half shutter press. The script will give user feedback as requested.
The script's menu allows you to select a 0, 2 or 5 second delay.
To get the best workflow, switch ETTR on and use ETTR to set the base exposure.
Irrespective of using ETTR, the base exposure should always be the exposure set for the highlights.
You can select the min shutter yourself in manual mode, the script uses the ETTR min even if you don't ETTR.
In auto mode (also ensure you select FF or Crop to suit your camera) the script will adjust the min shutter according to your FL.
You can also set an explicit min shutter for Auto mode to account for WA lenses, ie auto 1/FL too low.
The script will take either 3 or 6 brackets, according to the scene and your menu choices.
That is (3 x ISO brackets from the slowest shutter, always) plus [(3 x (highlight) time brackets if possible) or (3 x high ISO 'noise' brackets if requested)]
The 3 high ISO 'noise' brackets can be either at 1/3Ev or 0Ev apart: down from the menu set max ISO
The script can be switched off (disabled) via its menu.
The script will run in LV on non-LV mode: but non-LV mode is quicker at taking brackets.
The exposure brackets need to be post processed, eg through LR HDR Merge, Photomatix or with LR/Enfuse or some other app that handles exposure bracketing, or blended with layer masks.
The focus brackets (only two) are best masked in layers in Photoshop.
The script checks if the lens reports FL and adjusts accordingly. So the script can be used with manual lenses that don't report FL, but ensure you set ETTR min to suit your lens.
The script 'remembers' menu settings at camera close, so you will only need to set them once: only change things as required.
Garry George
March 2018
www.photography.grayheron.net
********************************************************************************************
--]]
require("config")
max_iso = 0
first_pass = false
second_pass = false
reset = false
base_shutter_apex = 0
base_iso_apex = 0
base_aperture_apex = 0
max_iso_apex = 0
count = 0
options0 = {"OFF", "Exposure", "Focus"}
options1 = {"Auto", "Manual"}
options2 = {"0 delay", "2s delay", "5s delay"}
options3 = {"FF Sensor", "Crop Sensor"}
options4 = {"1600", "3200","6400"}
options5 = {"Auto","1/30","1/60"}
options6 = {"None", "Audio","Led", "Both"}
options7 = {"OFF", "Time only", "Time/Noise", "Noise only"}
options8 = {"0Ev", "1/3Ev"}
options9 = {"F/16", "F/22"}
min_shutter_value =0
min_shutter_apex = 0
timer_running = false
time_pressed = 0
run_HHB = false
beeped = false
good_to_go = false
function HH_bracket(arg) -- note this function is called regularly by the shoot_task event handler and does all the main work
if HHB_menu.submenu["Turn Script on and off"].value ~= "OFF" then good_to_go = true else good_to_go = false end -- track the script's on/off state
if good_to_go and timer_running and (not beeped) and (dryos.ms_clock - time_pressed > 3000) then -- give user feedback if requested
if HHB_menu.submenu["User feedback"].value == "Audio" or HHB_menu.submenu["User feedback"].value == "Both" then beep() end
if HHB_menu.submenu["User feedback"].value == "Led" or HHB_menu.submenu["User feedback"].value == "Both" then led_blink() end
beeped = true -- so don't beep again
end
if good_to_go and not run_HHB then -- keep track of min shutter speed
if lens.focal_length == 0 then menu.set("Hand Held Bracketing", "Min Shutter options", "Manual") end -- handle lenses that don't report FL, but ensure ETTR min shutter set correctly
if HHB_menu.submenu["Min Shutter options"].value == "Auto" then -- set ETTR automatically based on focal length and min hand holding shutter set in script's menu
local x = lens.focal_length
if HHB_menu.submenu["Sensor Type"].value == "Crop Sensor" then x = x*1.6 end -- account for crop sensor
x = math.ceil(8*(7+math.ceil(10*math.log(x)/math.log(2))/10))
if x < 96 and HHB_menu.submenu["Your min hand holding limit"].value == "1/30" then
x = 96
elseif x < 104 and HHB_menu.submenu["Your min hand holding limit"].value == "1/60" then
x = 104
end
menu.set("Auto ETTR","Slowest shutter", x) -- set slowest ETTR shutter to FL-informed value, ie 1/FL guidance + your min hand holding limit (1/30 or 1/60)
end
min_shutter_value = menu.get("Auto ETTR","Slowest shutter",1)
min_shutter_value = 1/(2^(min_shutter_value/8 - 7)) -- set min shutter in seconds based on ETTR setting
end
if good_to_go and run_HHB and HHB_menu.submenu["Turn Script on and off"].value == "Focus" then -- capture focus brackets
if HHB_menu.submenu["Delay?"].value == "2s delay" then
msleep(2000) -- inject a 2 sec delay
elseif HHB_menu.submenu["Delay?"].value == "5s delay" then
msleep(5000) -- inject a 5 sec delay
end -- else no delay
base_shutter_apex = camera.shutter.apex
base_iso_apex = camera.iso.apex
base_aperture_apex = camera.aperture.apex
max_iso = tonumber(HHB_menu.submenu["Max ISO"].value)
camera.iso.value = max_iso
max_iso_apex = camera.iso.apex
camera.iso.apex = base_iso_apex
camera.shoot() -- take base image
camera.shutter.value = min_shutter_value -- set to slowest shutter
min_shutter_apex = camera.shutter.apex
local min_aperture_apex = 0
if HHB_menu.submenu["Min aperture"].value == "F/16" then min_aperture_apex = 8 else min_aperture_apex = 9 end
camera.aperture.apex = min_aperture_apex
local delta_ev = min_aperture_apex - base_aperture_apex
local delta_tv = base_shutter_apex - min_shutter_apex
if base_shutter_apex == min_shutter_apex then -- can only use ISO
camera.iso.apex = camera.iso.apex + delta_ev
if camera.iso.apex > max_iso_apex then camera.iso.apex = max_iso_apex end
else -- can use shutter and ISO
camera.shutter.apex = min_shutter_apex
camera.iso.apex = camera.iso.apex + (delta_ev - delta_tv)
if camera.iso.apex > max_iso_apex then camera.iso.apex = max_iso_apex end
end
camera.shoot() -- take the closed down aperture image
camera.shutter.apex = base_shutter_apex -- reset settings to initial state
camera.iso.apex = base_iso_apex
camera.aperture.apex = base_aperture_apex
if timer_running and (not beeped) then -- give user feedback if requested
if HHB_menu.submenu["User feedback"].value == "Audio" or HHB_menu.submenu["User feedback"].value == "Both" then beep() end
if HHB_menu.submenu["User feedback"].value == "Led" or HHB_menu.submenu["User feedback"].value == "Both" then led_blink() end
end
run_HHB = false
return true
end
if good_to_go and run_HHB and HHB_menu.submenu["Turn Script on and off"].value == "Exposure" then -- capture brackets
-- Now manage the three phases
if first_pass then -- do ISO bracketing, 'up' from the min (hand held) shutter, starting at ISO 100, in an attempt to cover the shadow end
first_pass = false -- so don't do first pass again in this bracketing sequence
max_iso = tonumber(HHB_menu.submenu["Max ISO"].value)
second_pass = true -- do a second pass when ready
reset = false -- but don't do a reset yet, ie only after a second pass attempt
base_iso_apex = camera.iso.apex -- get base ISO value in APEX units, ie the ISO set for the highlights
base_shutter_apex = camera.shutter.apex -- get base shutter speed, ie the shutter value used for the highlights in APEX units
menu.set("Shoot", "Advanced Bracket", 0) -- switch advanced bracketing off, just in case it's on'
camera.shutter.value = min_shutter_value -- get ready to ISO bracket from min shutter position
min_shutter_apex = camera.shutter.apex -- get this shutter in APEX units
camera.iso.apex = 5 -- set to ISO 100
if HHB_menu.submenu["Delay?"].value == "2s delay" then
msleep(2000) -- inject a 2 sec delay
elseif HHB_menu.submenu["Delay?"].value == "5s delay" then
msleep(5000) -- inject a 5 sec delay
end -- else no delay
count = dryos.shooting_card.file_number -- get current file counter before we start bracketing
camera.shoot() -- capture the 3 ISO brackets from the min shutter
camera.iso.value = max_iso
camera.shoot()
camera.iso.apex = (camera.iso.apex + 5)/2
camera.shoot()
return true
end
-- Do a time or 'noise' bracket pass, from the highlight shutter time starting position or high ISO level
if second_pass and (dryos.shooting_card.file_number - count == 3) then -- do a second time bracket pass from the highlights, if possible
second_pass = false
camera.shutter.apex = base_shutter_apex -- set shutter to base shutter speed, ie as set for the highlights
camera.iso.apex = base_iso_apex -- set to base ISO, ie as set for the highlights
if base_shutter_apex > min_shutter_apex and (HHB_menu.submenu["2nd Pass Options?"].value == "Time only" or HHB_menu.submenu["2nd Pass Options?"].value == "Time/Noise") then -- can do 3 time brackets from the highlights
camera.shoot()
camera.shutter.apex = min_shutter_apex
camera.shoot()
camera.shutter.apex = camera.shutter.apex + (base_shutter_apex - min_shutter_apex)/2
camera.shoot()
elseif HHB_menu.submenu["2nd Pass Options?"].value == "OFF" then
-- no second pass requested
elseif HHB_menu.submenu["2nd Pass Options?"].value == "Noise only" or HHB_menu.submenu["2nd Pass Options?"].value == "Time/Noise" then -- do some high ISO 'noise' brackets at min shutter
camera.shutter.iso = min_shutter_apex
local delta_ev = 0
if HHB_menu.submenu["Noise Ev delta"].value == "1/3Ev" then delta_ev = 0.33 end
camera.iso.value = max_iso
camera.iso.apex = camera.iso.apex - delta_ev
camera.shoot()
camera.iso.apex = camera.iso.apex - delta_ev
camera.shoot()
camera.iso.apex = camera.iso.apex - delta_ev
camera.shoot()
end
reset = true
return true
end
-- Are we ready to reset?
if reset then -- tidy up
reset = false
camera.shutter.apex = base_shutter_apex -- reset to initial state
camera.iso.apex = base_iso_apex -- reset to initial state
count = 0
menu.set("Shoot", "Advanced Bracket", 0) -- ensure advanced bracketing off so you can ETTR
if timer_running and (not beeped) then -- give user feedback if requested
if HHB_menu.submenu["User feedback"].value == "Audio" or HHB_menu.submenu["User feedback"].value == "Both" then beep() end
if HHB_menu.submenu["User feedback"].value == "Led" or HHB_menu.submenu["User feedback"].value == "Both" then led_blink() end
end
run_HHB = false -- signal end of bracketing sequence to event handler
return true
end
return true -- keep watching out
end
return true
end
function test4key(key)
if key == KEY.HALFSHUTTER and good_to_go and (not timer_running) and (not run_HHB) then -- note assumes the shoot_task runs at least once following camera start
timer_running = true -- start looking for half shutter long press
time_pressed = dryos.ms_clock
beeped = false
return false -- steal key press
elseif key == KEY.UNPRESS_HALFSHUTTER and good_to_go and timer_running and (not run_HHB) then
if dryos.ms_clock - time_pressed > 3000 then -- 3 sec plus half shutter press, so start HH bracketing
run_HHB = true -- OK to run the main script
timer_running = false -- stop looking for a long press
first_pass = true
return false -- steal key press
else -- less than 3 seconds so handle as a normal half shutter unpress
timer_running = false
return true
end
else -- must be a normal key press
return true
end
end
event.keypress = test4key
event.shoot_task = HH_bracket
HHB_menu = menu.new
{
parent = "Shoot",
name = "Hand Held Helper Script",
help = "Start at the highlights for exposure bracketing",
help2 = "Start at widest aperture for focus bracketing",
submenu =
{
{
name = "Turn Script on and off",
help = "Off, Exposure or Focus Brackets",
choices = options0,
},
{
name = "Min Shutter options",
help = "Auto means script sets ETTR min, manual means you do",
help2 = "For manual, set min in ETTR menu",
choices = options1,
},
{
name = "Sensor Type",
help = "Full Frame or Crop 1.6?",
choices = options3,
},
{
name = "Delay?",
help = "Injects the requested delay before bracketing",
help2 = "Switch off Canon delays",
choices = options2,
},
{
name = "Max ISO",
help = "Make sure Canon Auto Max ISO is the same",
choices = options4,
},
{
name = "Min aperture",
help = "Smallest aperture",
choices = options9,
},
{
name = "2nd Pass Options?",
help = "For noise options choose delta Ev",
help2 = "if Time/Noise then do Noise if can't do Time",
choices = options7,
},
{
name = "Noise Ev delta",
help = "Delta Ev ISO bracket",
choices = options8,
},
{
name = "Your min hand holding limit",
help = "Your hand holding shutter speed limit",
help2 = "Useful when in Auto mode to override 1/FL for WA lenses",
choices = options5,
},
{
name = "User feedback",
help = "To help with 3s half shutter and indicate bracket end",
choices = options6,
}
}
}
config.create_from_menu(HHB_menu) -- keep a track of the script's menu state at camera close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment