Skip to content

Instantly share code, notes, and snippets.

@pigeonhill
Last active Jun 16, 2021
Embed
What would you like to do?
LBS (CHDK)
--[[
Landscape Bracketing Script (LBS)
Capture 'Perfect' focus brackets, with focus overlap defined in terms of defocus (CHDK) Circle of Confusion blur
Focus bracketing strategies are: current position to blur-defined infinity (X2INF); min camera focus to blur-defined infinity (Min2INF); H/x to infinity (H/x2INF);...
and Min focus to current focus position.
Overlap, ie bracket to bracket, is definded in terms of the defocus (CoC) blur, eg CoC/2 means focus brackets 'touch' at a blur of CoC/2.
Additional exposure brackets may be taken at each focus step, using various schemes: 1Ev, 2Ev or 3Ev steps using -/+ or -/-- or +/++; one
ISO at 800 or 1600; or one (Zero Noise) exposure at +4Ev, +3Ev or +2Ev, auto from a (ETTL) shadow exposure or a wind bracket.
You can also switch on Infinity Bracketing that either allows you to take an ETTR shot at the infinity focus position or capture a simulated ND bracket set, up to a 5 ND, or both.
Use Bookends to help differentiate bracket set in post (reccomended)
The script will pause and display info about the option you have selected. In X2INF mode you can refocus, with the script recalculating number of brackets...
or telling you the infinity defocus blur if you are positioned beyond the hyperfocal.
Once the script is started you can change shutter speed, but don't change aperture or focal length, as the script will terminate. Compose first!
Press Half Shutter to run the script.
Rev 1.176
June 2021
Garry George
@chdk_version 1.6
@title LBS
@subtitle Focus Stuff
# focus_strat = 0 "Focus logic?" {None X2INF Min2INF H/x2INF Min2X}
# hfrac = 0 "H start" {H H/3 H/5 H/7 H/9}
# overlap = 0 "Overlap at" {CoC 2CoC/3 CoC/2}
# quality = 2 "Infinity quality?" {CoC/2 CoC/3 CoC/4}
@subtitle Exposure Stuff
# delta = 0 "Exposure delta?" {None 1Ev 2Ev 3Ev 800 1600 ZN4 ZN3 ZN2 Auto Wind}
# logic = 0 "Exposure logic?" {0/-/+ 0/-/-- 0/+/++}
# hilit = 20 "% histo HiLit" [5 30]
# sky = 0 "Inf Bracket?" {None Sky ND-Sim Both}
# skyn = 5 "Sky ETTR factor" [5 50]
# NDF = 1 "ND (Ev)" [1 5]
@subtitle Other Stuff
# delay = 3 "Script Delay (s)" [0 5]
# bookends = 1 "Bookends?" {No Yes}
# screen_off = 0 "Screen off?" {No Yes}
--]]
capmode = require("capmode")
props=require'propcase'
bi = get_buildinfo()
if (bi.platform == "m3") then
print("Doesn't run on M3")
sleep(3000)
return
end
if bi.platform == "s95" then
set_exit_key("display")
--elseif bi.platform == "???" then -- add additional camera specific exit keys here
--set_exit_key("display")
else
set_exit_key("right")
end
set_config_value(2151,0) -- switching these two off means we are measuring distance 'from the lens': ...
set_config_value(2152,0) -- ...this is the 'best' we can do without knowing the position of the front principal plane and pupil magnification
function restore() -- called at script termination
exit_alt()
end
function my_restore()
if x_start == -1 then refocus(1000000) else refocus(x_start) end
set_user_tv96(s_start)
set_av96_direct(av)
press("shoot_half")
repeat sleep(10) until get_shooting()
sleep(100)
release("shoot_half")
repeat sleep(10) until (not get_shooting())
set_lcd_display(1)
exit_alt()
sleep(1000)
end
function set_up()
current_focus_mode = get_focus_mode()
current_mode = capmode.get_name()
if capmode.get_name() ~= "M" then
print("Switch to M mode")
sleep(2000)
exit_alt()
return false
end
if current_focus_mode ~= 1 then
print("Switch to MF mode")
sleep(2000)
exit_alt()
return false
end
press("shoot_half")
repeat sleep(10) until get_shooting()
dof = get_dofinfo()
x = dof.focus
x_start = x
fl = fmath.new(dof.focal_length,1000) -- focal length in mm (real)
s = get_prop(props.TV)
s_start = s
av = get_prop(props.AV)
n = fmath.new(av96_to_aperture(av),1000) -- aperture number as a real
release("shoot_half")
repeat sleep(10) until (not get_shooting())
x_start = x
xr = x
base_h = (1000*fl*fl)/(n*dof.coc)
H = base_h + fl
return true
end
function myshoot() -- works inside X_bracket function
local prevcnt=hook_shoot.count()
local rawprevcnt=hook_raw.count()
press'shoot_full_only'
repeat sleep(10) until prevcnt ~= hook_shoot.count()
release'shoot_full_only'
repeat sleep(10) until rawprevcnt ~= hook_raw.count()
end
function snap() -- single image capture
press("shoot_half")
repeat sleep(10) until (get_shooting())
myshoot()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
end
function bookend()
set_tv96_direct(960)
set_av96_direct(get_max_av96())
local ecnt = get_exp_count()
shoot()
set_tv96_direct(s)
set_av96_direct(av)
repeat sleep(10) until (get_exp_count() ~= ecnt)
end
function refocus(xx)
set_focus(xx)
dof = get_dofinfo() -- update info
return dof.focus
end
function X_bracket()
press("shoot_half")
repeat sleep(10) until (get_shooting())
set_tv96_direct(s)
if delta == 0 then
myshoot()
elseif delta < 4 and logic == 0 then
myshoot()
set_tv96_direct(s-96*delta)
myshoot()
set_tv96_direct(s+96*delta)
myshoot()
elseif delta < 4 and logic == 1 then
myshoot()
set_tv96_direct(s+96*delta)
myshoot()
set_tv96_direct(s+2*96*delta)
myshoot()
elseif delta < 4 and logic == 2 then
myshoot()
set_tv96_direct(s-96*delta)
myshoot()
set_tv96_direct(s-2*96*delta)
myshoot()
elseif delta == 4 then
local iso = get_sv96()
myshoot()
set_sv96(sv96_market_to_real(iso_to_sv96(800)))
myshoot()
set_sv96(iso)
elseif delta == 5 then
local iso = get_sv96()
myshoot()
set_sv96(sv96_market_to_real(iso_to_sv96(1600)))
myshoot()
set_sv96(iso)
elseif delta == 6 then
myshoot()
set_tv96_direct(s-96*4)
myshoot()
elseif delta == 7 then
myshoot()
set_tv96_direct(s-96*3)
myshoot()
elseif delta == 8 then
myshoot()
set_tv96_direct(s-96*2)
myshoot()
elseif delta == 9 then
shot_histo_enable(1)
myshoot()
sleep(100)
lower = 1024 - 1024*hilit/100
test=get_histo_range(lower,1024)
step = 0
while test > 0 do
step = step + 1
set_tv96_direct(s + 96 * 2 * step)
myshoot()
sleep(100)
test=get_histo_range(lower,1024)
end
shot_histo_enable(0)
elseif delta == 10 then -- 3Ev ISO uplift from a lowish ISO base
snap()
set_tv96_direct(s+96*3)
local svt = get_sv96()
set_sv96(svt+(3*96))
snap()
set_sv96(svt)
end
set_tv96_direct(s)
release("shoot_half")
repeat sleep(10) until (not get_shooting())
end
function set_ETTR(n)
local ettrval = 10
local histo={}
local percent = 0
local total = 0
histo,total=get_live_histo()
for j = 254,(254-n), -1 do percent = percent + histo[j] end
percent = (1000*percent)/total
press("shoot_half")
repeat sleep(10) until get_shooting()
s = get_tv96()
set_user_tv96(s)
release("shoot_half")
repeat sleep(10) until (not get_shooting())
if percent > ettrval then -- over exposed
i = 0
local s_test = s
repeat
i=i+96
percent = 0
set_user_tv96(s+i)
press("shoot_half")
repeat sleep(10) until get_shooting()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
if get_tv96() == s_test then break end
s_test = get_tv96()
histo,total=get_live_histo()
for j = 254,(254-n), -1 do percent = percent + histo[j] end
percent = (1000*percent)/total
until percent <= ettrval
end
return percent
end
function num_b (xx)
local xc = xx
local HH = H:int()
if xx < 1 then -- at infinity
return 1
end
if focus_strat == 0 then -- no focus bracketing requested
return 1
elseif (3*xc < HH) then
local i = 0
repeat
xc = ((xc*(h-fl))/(fl+h-2*xc)):int()
i = i + 1
until 3*xc >= HH
return (i + 1)
elseif (xx < HH) then
return 2
else
return 1
end
end
-- Main section
if not set_up() then
print("Can't run script")
return -- exit script
end
-- Adjust the overlap h if required
h = base_h -- 'short' hyperfocal, ie H = h + fl
if overlap == 1 then -- adjust h to achieve the requested overlap, ie 'touching' at 2*CoC/3 or CoC/2
h = (h*3)/2
elseif overlap == 2 then
h = 2*h
end
H = h + fl -- 'full' hyperfocal as measured 'from the lens'
x_stop = (H/3):int()
if ((focus_strat == 2) or (focus_strat == 4)) then -- move to minimum focus
x = 10
xr = refocus(x)
x = xr
elseif focus_strat == 3 then
x = (H/(2*hfrac+1)):int()
xr = refocus(x)
x = xr
end
-- Keep all DoF calculations in request-space, to limit build up of image to image CHDK-Canon errors
bracks = num_b(xr)
if focus_strat == 0 then bracks = 1 end
if focus_strat == 4 then
x_stop = x_start
if x_stop > (H/3):int() then
print("Too far away")
return
end
bracks = num_b(xr) - num_b(x_stop)
end
if focus_strat == 0 then
print("@:"..x.."mm")
else
print("At least "..bracks.." focus steps")
print("@:"..x.."mm")
if focus_strat == 4 then
print("Stop@:"..x_stop.."mm")
else
print("H@:"..H:int().."mm")
end
end
print("Press HS to run")
dof = get_dofinfo() -- get current focal length and aperture values
fl_check = dof.focal_length
av_check = dof.aperture
if (focus_strat == 1 or focus_strat == 0) then last_x = 0 else last_x = x end
repeat
dof = get_dofinfo() -- Note the following checks may not work on all cameras: so don't change focal length or aperture ;-)
if fl_check ~= dof.focal_length then
print("FL changed!")
sleep(2000)
return
end
if av_check ~= dof.aperture then
print("Aperture changed!")
sleep(2000)
return
end
if focus_strat == 1 then
if last_x ~= dof.focus then
last_x = dof.focus
if last_x == -1 then
print("@ infinity")
elseif last_x >= H:int() then
if last_x > 0 then
blur = ((dof.coc*H)/last_x):int()
else
blur = 0
end
print("@:"..last_x.."mm/InfBlur="..blur.."um")
else
bracks = num_b(last_x) + 1
print("@:"..last_x.."mm/#steps="..bracks)
end
end
elseif focus_strat == 0 then
if last_x ~= dof.focus then
last_x = dof.focus
if last_x == -1 then
print("@ infinity")
elseif last_x >= H:int() then
if last_x > 0 then
blur = ((dof.coc*H)/last_x):int()
else
blur = 0
end
print("@:"..last_x.."mm/InfBlur="..blur.."um")
else
print("<H@:"..last_x.."mm")
end
end
end
dof = get_dofinfo()
fl_check = dof.focal_length
av_check = dof.aperture
wait_click(500)
if is_key("set") then console_redraw() end
until is_key("shoot_half")
xr = refocus(last_x)
x = xr
t1=os.time()
if screen_off == 1 then set_lcd_display(0) end
if bookends == 1 then bookend() end
t1=(os.time()-t1)
if delay >= t1 then
t1=delay-t1
sleep(t1*1000)
end
if focus_strat == 0 then -- take requested exposues only
X_bracket()
else
im = 0
while (xr < x_stop) and (xr > 0) do -- capture the focus brackets up to the required x_stop
im = im + 1
print(im.."/"..bracks..": "..x.."mm")
X_bracket()
if 2*xr >= H:int() then break end
xrn = ((xr*(h-fl))/(fl+h-2*xr)):int()
if xrn == xr then xrn = xr + 1 end
xr = xrn
x = refocus(xr)
end
if focus_strat ~= 4 then
if xr == -1 then
im=im+1
print(im.."/"..bracks..":INF")
X_bracket()
else -- take an image at H and the infinity blur position
temp = H:int()
temp2 = refocus(temp) -- take shot at the base hyperfocal
im=im+1
print(im.."/"..bracks..": "..temp2.."mm")
X_bracket()
temp2 = (H*(quality+2)):int()
temp2 = refocus(temp2) -- take shoot at the selected infinity blur
im=im+1
print(im.."/"..bracks..": "..temp2.."mm")
X_bracket()
end
end
end
if (sky == 2 or sky == 3) then -- capture an ND sim bracket subset
if bookends == 1 then bookend() end
print("ND brackets")
set_tv96_direct(s_start)
if NDF == 1 then
i = 2
elseif NDF == 2 then
i = 4
elseif NDF == 3 then
i = 8
elseif NDF == 4 then
i = 16
else
i = 32
end
for j = 1, i do
snap()
end
if bookends == 1 then bookend() end
end
if (sky == 1 or sky == 3) and focus_strat ~= 4 then -- Take a sky ETTR shot
print("Sky bracket")
set_ETTR(skyn)
press("shoot_half")
repeat sleep(10) until (get_shooting())
myshoot()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
end
if bookends == 1 then bookend() end
my_restore()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment