Skip to content

Instantly share code, notes, and snippets.

@pigeonhill
Last active Jul 10, 2022
Embed
What would you like to do?
M3 Focus & Exposure Bracketing
--[[
@title LBS-M
@chdk_version 1.6
@subtitle Bracketing Options
#mode = 0 "Focus?" {Off X2INF Min2INF Manual Min2X SupRes}
#bracket = 0 "Exposure?" {Off 1(4Ev) 1(3Ev) 1(2Ev) 2(2Ev) 3(2Ev) ISO AutoX>H Wind3Ev Wind4Ev HandH AutoS>H}
#sky = 0 "Sky?" {Off 2Ev 3Ev 4Ev 5Ev 6Ev ETTR}
#ndf = 0 "ND?" [-5 30]
@subtitle Bracketing Settings
#infinity = 3 "Infinity Focus (xH)" [2 4]
#overlap = 15 "Overlap Blur (um)" [5 30]
#pmag = 1 "Assumed Pupil Mag" [1 6]
#hhmin = 30 "HH Min Tv (1/x s)" [10 100]
#isoval = 0 "X Bracketing Value" {+1/200 +2/400 +3/800 +4/1600 +5/3200}
#srpix = 0 "SupRes Pixel Shift?" {Off -+ ++}
@subtitle Other Settings
#dofs = 0 "DoF Display" {Blur DoFs Both}
#diff = 0 "Diffraction Aware?" {Off On}
#traf = 0 "Traffic Lights" {Off On}
#evmode = 0 "Exposure Help" {Off Survey ETTR}
#sleep_time = 0 "Delay (s)" [0 10]
#bookends = 1 "Bookends?" {Off On}
#histoon = 0 "CHDK Histogram" {Off On}
#screen_off = 0 "Screen?" {Off On}
#log = 0 "Log?" {Off On Reset}
#help = 0 "Console" {Off On}
#lens = 0 "Lens Name?" {Off On}
#usefd = 0 "Focus distance" {lower Upper Harmonic}
#ettrpl = 1 "low ETTR 1/10 %" [1 10]
#ettrpu = 1 "high ETTR %" [1 10]
#hlw = 1 "Over X Warning Bins" [1 10]
#bp = 0 "Black Level Sensitivity" {Normal Medium High}
#limit = 0 "ETTR limit" [0 10]
Notes:
* This script only runs on the M3, M10 or M100 with the XIMR version of CHDK and the camera should be in M mode. But note, not tested on the M100.
* Go to https://drive.google.com/drive/folders/1drk1xi6kMMIeF5xSqXgnpjGCffF8CVkd?usp=sharing for XIMR builds
* Focus functionality and time based ND bracketing needs a registered EF-M lens. Non registered EF-M lenses can only be used with certain exposure bracketing.
* The focus bracketing works best with wide angle lenses, as tele lenses will usually mean too many focus brackets ;-)
* Set the CHDK ALT button to anything other than m-FN (M3) or VIDEO (M10/100), as these buttons are used as a second shutter button to start bracketing
* Register your lens below in the lens_info() function. Note only EF-M lenses will allow full focus functionality. Non EF-M lenses may 'play up' ;-)
* This script is not designed for macro focus stacking; it is intended for non-macro, deep focus photography only.
* If you reset/delete the log, you need to set the log menu after.
* When paused, at the assess point, you can refocus and change focal length and aperture to see the impact on bracketing.
* Use the M-Fn button on the M3 to initiate bracketing, or the VIDEO button on the M10/M100. Note you must have selected a focus bracketing option.
* If traffic light (TL) feedback is enabled, once an image is captured, the left hand TL shows the near DoF's focus overlap state, relative to the last image captured...
...and the right TL shows the far DoF's overlap state relative to the last image captured. Yellow = same focus; green = a positive overlap; red = a negative overlap (a gap).
* Changing focal length or aperture will reset the traffic lights.
* The CHDK histogram can be toggled on and off by pressing the MENU button
* Auto ETTR by pressing the wheel right button.
* You can dynamically change the shutter delay by pressing and holding the up button.
* If auto exposure bracketing is selected, first set the exposure for the shadows; the script will auto bracket by the requested Ev step until the ETTR exposure is reached
* Exposure survey/help provides two options. The first provides Ev feed back on the current exposure relative to the last captured image. The second provides...
* ...1/3 stop feedback in the highlight area: green = <x/10% of the histogram is in that 1/3 stop; Yellow = over x/10% and less than y%; and red = over y%...
* ...where x and y are selected in the script's menu. Note the top 1/3 stop is divided into two 1/6 stops plus a white bar will appear if the highlight end...
* ...of the histogram is not zero. You can set how many bins this represents, ie 1-10.
* Use the "Assumed Pupil Mag" menu item to refine focus overlap insurance. Use 1 for a telephoto lens...
* ...for a retrofocus lens simply guess the pupil mag if you don't know it (exit/entrance ration), but round up, ie slightly over estimate for DoF insurance
* All UI presented distances, ie focus and DoFs, are relative to the sensor plane.
* Note: If the bar disappears, just do a half shutter press.
Release 1.11773
photography.grayheron.net
June 2022
--]]
require "drawings"
props=require'propcase'
capmode = require("capmode")
bi = get_buildinfo()
current_focus_mode = get_focus_mode()
current_mode = capmode.get_name()
if current_focus_mode ~= 1 then set_mf(1) end
set_exit_key("down")
if histoon == 0 then set_config_value(1060,0) else set_config_value(1060,1) end
inf = 81000 -- infinity trap
dirty = true
set_console_autoredraw(1)
set_console_layout(0,0,45,3)
tg = 242
non_m = false
tv_str = {"32", "25.4","20","16", "12.7", "10","8", "6.3","5","4","3.2", "2.5","2",
"1.6", "1.3", "1", "0.8", "0.6", "0.5", "0.4", "0.3", "1/4", "1/5", "1/6", "1/8", "1/10", "1/13",
"1/15", "1/20", "1/25", "1/30", "1/40", "1/50", "1/60", "1/80", "1/100", "1/125", "1/160", "1/200",
"1/250", "1/320", "1/400", "1/500", "1/640","1/800", "1/1000", "1/1250", "1/1600","1/2000","1/2500",
"1/3200","1/4000", "1/5000", "1/6400", "1/8000", "1/10000", "1/12500", "1/16000", "1/20000", "1/25000",
"1/32000", "1/40000"}
-- Check if OK to use
if not ((bi.platform == "m3") or (bi.platform == "m10") or (bi.platform == "m100")) then
print("Doesn't run on this Cam")
sleep(3000)
return
end
if get_gui_screen_width() ~= 360 then
print("Need an XIMR build")
sleep(3000)
return
end
if capmode.get_name() ~= "M" then
print("Put camera in M mode")
sleep(3000)
return
end
if log == 2 then
print_screen(1)
print("Log reset")
print("Change log menu")
sleep(3000)
print_screen(0)
return
end
function units(xx)
local xr =""
if xx:int() > 5000 then
xr=(xx/1000):tostr(1).."m"
elseif xx:int() > 1000 then
xr=(xx/10):tostr(0).."cm"
else
xr=(xx):tostr(0).."mm"
end
return xr
end
function update_DoFs(xx) -- update DoF related stuff
h = (1000*F*F)/(n*overlap) -- short hyperfocal (mm)
H = h + F -- 'full' hyperfocal (mm), as measured from the lens front principal, ie NOT the sensor
local q = xx-t
u = (((q*(q-4*F)):sqrt() + q)/2):int() -- focus distance in mm from front principal
if xx < inf then
ndof = (F*F)/(u-F)
ndof = ndof+(h*(F-h*pmag))/(pmag*(u+h-F))
ndof = (ndof+(pmag*(2*F+h+t)-F)/pmag)
else
ndof = H
end
if 2*u < H:int() then
fdof = (F*F)/(u-F)
fdof = fdof-(h*(F+h*pmag))/(pmag*(u-h-F))
fdof = (fdof+(pmag*(2*F-h+t)-F)/pmag)
num = 0
local uc = u
repeat
num = num + 1
new_uc = 2*F*F*(pmag-1) + F*(uc-2*pmag*uc) + h*pmag*uc
new_uc = (new_uc/( F*(2*pmag-1) + pmag*(h-2*uc)))
uc = new_uc
until uc:int() >= (H/3):int()
num = num + 1
elseif u < H:int() then
fdof = (F*F)/(u-F)
fdof = fdof-(h*(F+h*pmag))/(pmag*(u-h-F))
fdof = (fdof+(pmag*(2*F-h+t)-F)/pmag)
num = 2
else
fdof = fmath.new(inf+1)
num = 1
end
end
function get_focus_distance_upper() -- returns upper focus distance in mm, but note 'accuracy' is 1cm from Canon
local x=-1
if (bi.platform=="m3") and (bi.platsub=="101a") then
x=peek(0x00244916, 2)
elseif (bi.platform=="m3") and (bi.platsub=="120f") then
x=peek(0x0024495A, 2)
elseif (bi.platform=="m3") and (bi.platsub=="121a") then
x=peek(0x0024495A, 2)
elseif (bi.platform=="m10") then -- same address in 110d, 110f, 110g
x=peek(0x00272016, 2)
elseif (bi.platform=="m100") and (bi.platsub=="100a") then
x=peek(0x001FC7FA, 2)
else
print('Wrong platform')
end
if x == -1 then return -1 else return x*10 end
end
function get_focus_distance_lower() -- returns lower focus distance in mm, but note 'accuracy' is 1cm from Canon
local x=-1
if (bi.platform=="m3") and (bi.platsub=="101a") then
x=peek(0x00244918, 2)
elseif (bi.platform=="m3") and (bi.platsub=="120f") then
x=peek(0x0024495C, 2)
elseif (bi.platform=="m3") and (bi.platsub=="121a") then
x=peek(0x0024495C, 2)
elseif (bi.platform=="m10") then -- same address in 110d, 110f, 110g
x=peek(0x00272018, 2)
elseif (bi.platform=="m100") and (bi.platsub=="100a") then
x=peek(0x001FC7FC, 2)
else
print('Wrong platform')
end
if x == -1 then return -1 else return x*10 end
end
function get_focus_distance()
local a = fmath.new(get_focus_distance_lower(),1)
local b = fmath.new(get_focus_distance_upper(),1)
if usefd == 0 then
return a:int()
elseif usefd == 1 then
return b:int()
else
if b:int() >= inf then
return b:int()
else
return ((2*a*b)/(a+b)):int()
end
end
end
function refocus_to(xx) -- Only moves towards infinity. Returns requested focus distance in mm
local current_x = x
local last_step_x = current_x
local steps_total = 0
local steps = 1
local last_step_count = steps_total
local steps_back = 0
if xx < current_x then return -1 end -- don't use
if xx <= get_focus_distance_upper() then -- xx is within the current Canon focus step
current_x = get_focus_distance_upper()
repeat
call_event_proc("EFLensCom.MoveFocus", steps, 1)
sleep(25)
steps_total = steps_total + 1
until get_focus_distance_lower() >= current_x
else -- find the Canon focus step that covers xx
while current_x < xx do
call_event_proc("EFLensCom.MoveFocus", steps, 1) -- move smallest step you can towards infinity
steps_total = steps_total + steps
sleep(25) -- for system to catch up
current_x = get_focus_distance_lower() -- Get Canon based focus position
if (current_x ~= last_step_x) and (current_x < xx) then -- just went through an intermediate step change, ie not yet reached the step that covers xx
last_step_x = current_x
last_step_count = steps_total -- close enough
end
end
end
-- now fine tune position backwards if required
steps_back = ((current_x - xx) * (steps_total - last_step_count)) / (current_x - last_step_x)
if steps_back > 0 then
for i = 1, steps_back do call_event_proc("EFLensCom.MoveFocus", -steps, 1) end
end
sleep(25) -- for system to catch up
return xx -- return request focus position, eg the 'best' estimate in mm as to focus position
end
function lens_name()
local pname = 0
if (bi.platform=="m3") then
if (bi.platsub == "101a") then
pname = 0x00244969
else
pname = 0x002449ad
end
elseif (bi.platform=="m10") then
pname = 0x272065
elseif (bi.platform=="m100") and (bi.platsub == "100a") then
pname = 0x1FC84E
end
local len = peek(pname-1,1)
local i = 0
local t = {}
while i < len do
local c = peek(pname + i, 1)
if c == 0 then break end
table.insert(t, string.char(c))
i = i + 1
end
local name = table.concat(t)
return name
end
function restore()
clr = tg
draw.replace(obj5,"rectf",hdmi+0,0,hdmi+10,15,clr,clr) -- update left traffic light
draw.replace(obj6,"rectf",hdmi+350,0,hdmi+360,15,clr,clr) -- update right traffic light
set_prop(props.USER_TV,base_s)
set_prop(props.USER_AV,base_av)
if current_focus_mode ~= 1 then set_mf(0) end
if (current_mode ~= "M") then capmode.set(current_mode) end
set_lcd_display(1)
print_screen(0)
cls()
sleep(1000)
exit_alt()
end
function my_restore() -- currently the same as the CHDK restore function ;-)
clr = tg
draw.replace(obj5,"rectf",hdmi+0,0,hdmi+10,15,clr,clr) -- update left traffic light
draw.replace(obj6,"rectf",hdmi+350,0,hdmi+360,15,clr,clr) -- update right traffic light
set_prop(props.USER_TV,base_s)
set_prop(props.USER_AV,base_av)
if current_focus_mode ~= 1 then set_mf(0) end
if (current_mode ~= "M") then capmode.set(current_mode) end
set_lcd_display(1)
print_screen(0)
cls()
sleep(1000)
exit_alt()
end
function set_up()
if current_focus_mode ~= 1 then set_mf(1) end -- just in case
if (current_mode ~= "M") then capmode.set("M") end -- just in case
dof = get_dofinfo()
F = fmath.new(dof.focal_length,1000) -- focal length in mm (real)
s = get_prop(props.USER_TV)
base_s = s
base_av = get_prop(props.USER_AV)
base_iso = get_iso_mode()
n = fmath.new(av96_to_aperture(base_av),1000) -- aperture number as a real
s_ev = s
press("shoot_half")
repeat sleep(10) until get_shooting()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
last_s = s
x = get_focus_distance()
end
function myshoot()
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 X_bracket() -- multi image capture options
press("shoot_half")
repeat sleep(10) until get_shooting()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
s = get_prop(props.TV)
set_tv96_direct(s) -- just in case
set_iso_mode(base_iso)
if bracket == 0 then
snap()
elseif bracket == 1 then
snap()
set_tv96_direct(s - 96 * 4)
snap()
elseif bracket == 2 then
snap()
set_tv96_direct(s - 96 * 3)
snap()
elseif bracket == 3 then
snap()
set_tv96_direct(s - 96 * 2)
snap()
elseif bracket == 4 then
snap()
set_tv96_direct(s - 96 * 2)
snap()
set_tv96_direct(s - 96 * 4)
snap()
elseif bracket == 5 then
snap()
set_tv96_direct(s - 96 * 2)
snap()
set_tv96_direct(s - 96 * 4)
snap()
set_tv96_direct(s - 96 * 6)
snap()
elseif bracket == 6 then
local iso = get_iso_mode()
set_iso_mode(iso)
sleep(100)
snap()
local isov = iso
isov = isov + (isoval+1)*3
if isov > 16 then isov = 16 end
set_iso_mode(isov)
snap()
set_iso_mode(iso)
elseif bracket == 7 or bracket == 11 then -- auto bracket
shot_histo_enable(1)
snap()
sleep(100)
local lower = 1024 - hlw
local test=get_histo_range(lower,1023)
local test2 = get_histo_range(128,128+bp)
local step = 1
while test > 0 and (s + 96 * (isoval+1) * step) <= 1152 do
set_tv96_direct(s + 96 * (isoval+1) * step)
snap()
sleep(100)
test=get_histo_range(lower,1023)
step = step + 1
end
step = 1
if bracket == 11 then -- deal with the shadows
while test2 > 0 and (s - 96 * (isoval+1) * step) >= -480 do
set_tv96_direct(s - 96 * (isoval+1) * step)
snap()
sleep(100)
test2=get_histo_range(128,128+bp)
step = step + 1
end
end
shot_histo_enable(0)
elseif bracket == 8 or bracket == 9 then
snap()
local iso = get_sv96()
set_sv96(iso+96*(bracket-5))
set_tv96_direct(s + 96*(bracket-5))
snap()
set_sv96(iso)
elseif bracket == 10 then
local tv_test = tv96_to_usec(s) -- shutter speed in us
if tv_test <= 1000000/hhmin then
print("HH Check OK")
snap()
set_tv96_direct(usec_to_tv96(1000000/hhmin))
snap()
local iso = get_iso_mode()
if isoval == 1 then
isov = 800
elseif isoval == 2 then
isov = 1600
elseif isoval == 3 then
isov = 3200
end
set_iso_mode(isov)
set_tv96_direct(usec_to_tv96(1000000/hhmin))
snap()
set_iso_mode(iso)
else
print("Check Xposure")
end
end
set_tv96_direct(s)
end
function bookend()
set_tv96_direct(960)
if not non_m then set_av96_direct(get_max_av96()) end
local ecnt = get_exp_count()
shoot()
set_tv96_direct(s)
if not non_m then set_av96_direct(base_av) end
repeat sleep(10) until (get_exp_count() ~= ecnt)
end
function hiatus()
local M = fmath.new(Mmax,1000)
local Fr = fmath.new(Fmax,1000)
-- Note the following is an estimate of min zoomed mag, based on assuming MFD is fixed across the zoom range and a thin lens model
local M1 = (1+M)
local k = Fr/F
m=k*M1*M1
m=m-(M1*(k:sqrt())*(k*M1*M1-4*M):sqrt())
m=m-2*M
m=m/(2*M)
t = (F * (1 + m) * (1 + m))
t = MFD - t/m -- this is an estimate of the spilt thin lens thickness (mm)
return t
end
function lens_info()
MFD = 0
if lens_name() == "EF-M11-22mm f/4-5.6 IS STM" then -- calculate lens thickness from manufacturer's data
--if lens_name() == "E-M11-22mm f/4-5.6 IS STM" then -- calculate lens thickness from manufacturer's data
-- add other lenses as required, ie replace the following for your lens
-- For the above lens, 300 is the lens max magnification x 1000, ie from manufacturer's data or your measurement
-- 22000 is the lens maximun zoom, or the prime, focal length in microns
-- 150 is the lens minimum focus distance (mm), ie from manufacturer's data or your measurement
Fmax = 22000 -- Longest focal length (mm) * 1000
Mmax = 300 -- Max magnification * 1000
MFD = 150 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
elseif lens_name() == "EF-M28mm f/3.5 MACRO IS STM" then
Fmax = 28000 -- Longest focal length (mm) * 1000
Mmax = 1000 -- Max magnification * 1000
MFD = 97 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
elseif lens_name() == "EF-M55-200mm f/4.5-6.3 IS STM" then
Fmax = 200000 -- Longest focal length (mm) * 1000
Mmax = 210 -- Max magnification * 1000
MFD = 960 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
elseif lens_name() == "EF-M18-55mm f/3.5-5.6 IS STM" then
Fmax = 55000 -- Longest focal length (mm) * 1000
Mmax = 240 -- Max magnification * 1000
MFD = 150 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
elseif lens_name() == "EF-M15-45mm f/3.5-6.3 IS STM" then
Fmax = 45000 -- Longest focal length (mm) * 1000
Mmax = 250 -- Max magnification * 1000
MFD = 250 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
elseif lens_name() == "TS-E24mm f/3.5L II" then
Fmax = 24000 -- Longest focal length (mm) * 1000
Mmax = 340 -- Max magnification * 1000
MFD = 230 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
--[[
elseif lens_name() == "10-20mm" then
Fmax = 20000 -- Longest focal length (mm) * 1000
Mmax = 130 -- Max magnification * 1000
MFD = 240 -- Minimum Focus Distance in mm
t = hiatus() -- distance between front and rear principals
return true
elseif lens_name() == "your lens" then
copy template above
]]
else -- lens not recognised
return false
end
end
function update()
local pos = 0
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
lens_info()
update_DoFs(x) -- update u and DoFs
draw.replace(obj1,"rectf",hdmi+0,0,hdmi+360,15,tg,tg) -- clear the info bar
if x > inf then
temp = ">>H:INF"
elseif u >= H:int() then
temp = (10*(u/H)):int()
if temp >= 50 then
temp = ">>H:"..units(fmath.new(x,1))
else
temp = (temp/10).."."..((temp-(temp/10)*10)).."xH:"..units(fmath.new(x,1))
end
else
local xl = get_focus_distance_lower()
local xu = get_focus_distance_upper()
--temp = "#"..num..":"..units(fmath.new(x,1))
temp = "#"..num..":"..units(fmath.new(xl,1)).."-"..units(fmath.new(xu,1))
end
draw.replace(obj2,"string",hdmi+10,0,temp,"white",tg) -- draw focus position info
local inf_blur = ((overlap*(H-F))/(u-F))
if x >= inf then inf_blur = fmath.new(0,1) end
if diff == 1 then -- show impact of diffraction on infinity blur
inf_blur = (inf_blur*inf_blur + (1342*n/1000)*(1342*n/1000)):sqrt()
end
inf_blur = inf_blur:int()
local qqq = get_user_tv_id()
if get_alt_mode() and evmode == 1 and not image then -- change to Survey mode
local temp_s = get_user_tv96()
if (temp_s > 1152 or temp_s < -480) then temp = "BULB" else temp = tv_str[qqq+16].."s" end
temp_s = fmath.new(temp_s,96)
temp_s = fmath.new(s_ev,96)-temp_s
if (10*temp_s):int() == 0 then
temp_s = " "..temp_s:tostr(1)
elseif (10*temp_s):int() > 0 then
temp_s = "+"..temp_s:tostr(1)
else
temp_s = temp_s:tostr(1)
end
if (get_user_tv96() > 1152 or get_user_tv96() < -480) then
temp = "???Ev:BULB"
else
temp = temp_s.."Ev:"..temp
end
if (s_ev > 1152 or s_ev < -480) then temp = "???Ev:"..tv_str[qqq+16].."s" end
if (s_ev > 1152 or s_ev < -480) and (get_user_tv96() > 1152 or get_user_tv96() < -480) then temp = "???Ev:???" end
pos = 180-4*(string.len(temp))
draw.replace(obj3,"string",hdmi+pos, 0,temp,"white",tg)
elseif get_alt_mode() and evmode == 2 then -- change to ETTR mode
local histo={}
local total = 0
local top = 0
local stop3 = 0
local stop2 = 0
local stop1 = 0
local stop4 = 0
local hlwc = 0
histo,total=get_live_histo()
for i = 229,255,1 do stop3 = stop3 + histo[i] end -- get the total count in the requested 1/3 stop
for i = 206,228,1 do stop2 = stop2 + histo[i] end -- get the total count in the requested 1/3 stop
for i = 185,205,1 do stop1 = stop1 + histo[i] end -- get the total count in the requested 1/3 stop
for i = 242,255,1 do stop4 = stop4 + histo[i] end -- get the total count in the requested zone
for i = 256-hlw,255,1 do hlwc = hlwc + histo[i] end -- get the total count in the requested zone
draw.replace(obj3,"string",hdmi+pos, 0,"",tg,tg)
clr = "green_light"
if stop1 == 0 then
clr = "grey_dark"
elseif stop1 >= (ettrpl*total)/1000 and stop1 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop1 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj7,"rectf",hdmi+145,0,hdmi+175,15,tg,clr,2)
clr = "green_light"
if stop2 == 0 then
clr = "grey_dark"
elseif stop2 >= (ettrpl*total)/1000 and stop2 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop2 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj8,"rectf",hdmi+175,0,hdmi+205,15,tg,clr,2)
clr = "green_light"
if stop3 == 0 then
clr = "grey_dark"
elseif stop3 >= (ettrpl*total)/1000 and stop3 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop3 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj9,"rectf",hdmi+205,0,hdmi+235,15,tg,clr,2)
clr = "green_light"
if stop4 == 0 then
clr = "grey_dark"
elseif stop4 >= (ettrpl*total)/1000 and stop3 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop4 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj10,"rectf",hdmi+222,2,hdmi+235,13,clr,clr,1)
clr = "white"
if hlwc == 0 then clr = tg end
draw.replace(obj11,"rectf",hdmi+238,2,hdmi+242,13,clr,clr)
elseif get_alt_mode() and evmode == 0 then
if (get_user_tv96() > 1152 or get_user_tv96() < -480) then temp = "BULB" else temp = tv_str[qqq+16].."s" end
temp = F:int().."mm:"..temp
pos = 180-4*(string.len(temp))
draw.replace(obj3,"string",hdmi+pos, 0,temp,"white",tg)
end
if dofs == 0 then
temp = units(ndof)..":"..inf_blur.."um"
elseif dofs == 2 then
if u < H:int() then
temp = units(ndof)..":"..units(fdof)
else
temp = units(ndof)..":"..inf_blur.."um"
end
elseif dofs == 1 then
if u < H:int() then
temp = units(ndof)..":"..units(fdof)
else
temp = units(ndof)..":INF"
end
end
pos = 350-8*(string.len(temp))
draw.replace(obj4,"string",hdmi+pos,0,temp,"white",tg)
if traf == 1 and image then -- display traffic lights
local clr = tg
if x == last_image_x then
clr = "yellow"
elseif (ndof >= last_image_ndof) and (ndof <= last_image_fdof) then
clr = "green"
elseif (ndof > last_image_fdof) or (ndof < last_image_ndof) then
clr = "red"
end
draw.replace(obj5,"rectf",hdmi+0,0,hdmi+10,15,clr,clr) -- update left traffic light
if x == last_image_x then
clr = "yellow"
elseif (fdof <= last_image_fdof) and (fdof >= last_image_ndof) then
clr = "green"
elseif (fdof > last_image_fdof) or (fdof < last_image_ndof) then
clr = "red"
end
draw.replace(obj6,"rectf",hdmi+350,0,hdmi+360,15,clr,clr) -- update right traffic light
local ee = (F*(pmag-1))/(pmag) -- pupil extension in mm
local blur = ee*(u+last_image_u)-2*u*last_image_u
blur = blur + F*(u+last_image_u-2*ee)
blur = blur*n
blur = ((1000*F*F*(u-last_image_u))/blur):int() -- blur at overlap in microns
if blur < 0 then blur = -blur end
if x < inf then
temp = F:int().."mm:"..blur.."um"
else
temp = F:int().."mm:".."???"
end
pos = 180-4*(string.len(temp))
draw.replace(obj3,"string",pos, 0,temp,"white",tg)
else
clr = tg
draw.replace(obj5,"rectf",hdmi+0,0,hdmi+10,15,clr,clr) -- update left traffic light
draw.replace(obj6,"rectf",hdmi+350,0,hdmi+360,15,clr,clr) -- update right traffic light
end
draw.overdraw()
dirty = false
end
function non_m_update()
local pos = 0
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
draw.replace(obj1,"rectf",hdmi+0,0,hdmi+360,15,tg,tg) -- clear the info bar
local qqq = get_user_tv_id()
if get_alt_mode() and evmode == 1 and not image then -- change to Ev or Exp feedback
local temp_s = get_user_tv96()
if (temp_s > 1152 or temp_s < -480) then temp = "BULB" else temp = tv_str[qqq+16].."s" end
temp_s = fmath.new(temp_s,96)
temp_s = fmath.new(s_ev,96)-temp_s
if (10*temp_s):int() == 0 then
temp_s = " "..temp_s:tostr(1)
elseif (10*temp_s):int() > 0 then
temp_s = "+"..temp_s:tostr(1)
else
temp_s = temp_s:tostr(1)
end
if (get_user_tv96() > 1152 or get_user_tv96() < -480) then
temp = "???Ev:BULB"
else
temp = temp_s.."Ev:"..temp
end
if (s_ev > 1152 or s_ev < -480) then temp = "???Ev:"..tv_str[qqq+16].."s" end
if (s_ev > 1152 or s_ev < -480) and (get_user_tv96() > 1152 or get_user_tv96() < -480) then temp = "???Ev:???" end
elseif get_alt_mode() and evmode == 2 then -- change to ETTR mode
local histo={}
local total = 0
local top = 0
local stop3 = 0
local stop2 = 0
local stop1 = 0
local stop4 = 0
local hlwc = 0
histo,total=get_live_histo()
for i = 229,255,1 do stop3 = stop3 + histo[i] end -- get the total count in the requested 1/3 stop
for i = 206,228,1 do stop2 = stop2 + histo[i] end -- get the total count in the requested 1/3 stop
for i = 185,205,1 do stop1 = stop1 + histo[i] end -- get the total count in the requested 1/3 stop
for i = 242,255,1 do stop4 = stop4 + histo[i] end -- get the total count in the requested zone
for i = 256-hlw,255,1 do hlwc = hlwc + histo[i] end -- get the total count in the requested zone
draw.replace(obj3,"string",hdmi+pos, 0,"",tg,tg)
clr = "green_light"
if stop1 == 0 then
clr = "grey_dark"
elseif stop1 >= (ettrpl*total)/1000 and stop1 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop1 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj7,"rectf",hdmi+145,0,hdmi+175,15,tg,clr,2)
clr = "green_light"
if stop2 == 0 then
clr = "grey_dark"
elseif stop2 >= (ettrpl*total)/1000 and stop2 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop2 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj8,"rectf",hdmi+175,0,hdmi+205,15,tg,clr,2)
clr = "green_light"
if stop3 == 0 then
clr = "grey_dark"
elseif stop3 >= (ettrpl*total)/1000 and stop3 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop3 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj9,"rectf",hdmi+205,0,hdmi+235,15,tg,clr,2)
clr = "green_light"
if stop4 == 0 then
clr = "grey_dark"
elseif stop4 >= (ettrpl*total)/1000 and stop3 < (ettrpu*total)/100 then
clr = "yellow_light"
elseif stop4 >= (ettrpu*total)/100 then
clr = "red_light"
end
draw.replace(obj10,"rectf",hdmi+222,2,hdmi+235,13,clr,clr,1)
clr = "white"
if hlwc == 0 then clr = tg end
draw.replace(obj11,"rectf",hdmi+238,2,hdmi+242,13,clr,clr)
temp = ""
else
if (get_user_tv96() > 1152 or get_user_tv96() < -480) then temp = "BULB" else temp = tv_str[qqq+16].."s" end
temp = "??mm:"..temp
end
pos = 180-4*(string.len(temp))
draw.replace(obj3,"string",hdmi+pos, 0,temp,"white",tg)
draw.overdraw()
dirty = false
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 check_focus_etc()
dof = get_dofinfo()
local test_x = get_focus_distance()
local test_n = fmath.new(av96_to_aperture(get_user_av96()),1000)
local test_F = fmath.new(dof.focal_length,1000)
if test_F ~= F or test_n ~= n then
dirty = true
image = false
elseif test_x ~= x then
dirty = true
end
F = test_F
n = test_n
x = test_x
end
function check_buttons_etc()
wait_click(50) -- check for a button press
do
local iso = get_iso_mode()
local tv = get_prop(props.USER_TV)
snapped = false
if is_key("up") and get_alt_mode() then -- Change delay time
while (is_pressed("up")) do
sleep(500)
sleep_time = sleep_time + 1
if sleep_time > 10 then sleep_time = 0 end
print("delay ="..sleep_time.."s")
end
release("up")
elseif is_key("right") and get_alt_mode() then -- ETTR requested
set_ETTR()
elseif is_key("set") and get_alt_mode() then
click("set")
elseif is_key("left") and get_alt_mode() then
click("left")
elseif is_key("shoot_half") and get_alt_mode() then -- do a manual refresh of the info bar
s_ev = get_user_tv96()
dirty = true
elseif ((is_key("print") and bi.platform=="m3") or (is_key("video") and ((bi.platform=="m10") or (bi.platform=="m100")))) and get_alt_mode() then
if mode == 3 or mode == 0 then -- take an exposure bracket set
set_up()
sleep(sleep_time*1000)
if bookends == 1 then bookend() end
X_bracket()
ND_test()
if sky == 6 then -- take an auto ETTR exposure bracket for the sky
set_ETTR()
snap()
print("Sky X")
elseif sky > 0 then -- take a single exposure shot for the sky
set_tv96_direct(s + 96*(sky+1))
snap()
print("Sky X")
end
if bookends == 1 then bookend() end
snapped = true
last_image_ndof = ndof
last_image_fdof = fdof
last_image_x = x
last_image_u = u
finish = false
image = true
dirty = true
elseif mode == 5 then -- take a superres set (make sure you set the move logic in the menu)
set_up()
if bookends == 1 then bookend() end
sleep(sleep_time*1000)
set_tv96_direct(s)
if ndf > 0 then i = ndf end
if ndf == -1 or ndf == 0 then
i = 2
elseif ndf == -2 then
i = 4
elseif ndf == -3 then
i = 8
elseif ndf == -4 then
i = 16
elseif ndf == -5 then
i = 32
end
if ndf ~= 0 then
if srpix == 1 then -- move focus to new start
for j = 1, i/2 do
call_event_proc("EFLensCom.MoveFocus", -1, 1)
sleep(50)
end
end
for j = 1, i do
snap()
sleep(1000)
if srpix ~= 0 and get_focus_distance() < inf then
call_event_proc("EFLensCom.MoveFocus",1,1) -- move smallest step you can towards infinity
sleep(50)
end
end
end
update()
last_image_ndof = ndof
last_image_fdof = fdof
last_image_x = x
last_image_u = u
finish = false
image = true
dirty = true
if sky == 6 then
set_ETTR()
snap()
print("Sky X")
elseif sky > 0 then -- take a single exposure shot for the sky
set_tv96_direct(s + 96*(sky+1))
snap()
print("Sky X")
end
snapped = true
if bookends == 1 then bookend() end
elseif mode > 0 then -- do some auto focus bracketing
finish = true
end
elseif is_key("menu") and get_alt_mode() then
if get_config_value(1060) == 0 then set_config_value(1060,1) else set_config_value(1060,0) end
dirty = true
elseif is_key("display") and get_alt_mode() then
click("display")
dirty = true
end
if snapped then
set_iso_mode(base_iso)
set_prop(props.USER_TV,tv)
end
if get_alt_mode() ~= last_alt_state then -- ALT state changed
last_alt_state = get_alt_mode()
image = false
s_ev = get_user_tv96() -- reset ev feedback
dirty = true
end
if (last_s ~= get_user_tv96()) then
dirty = true
last_s = get_user_tv96()
end
end
end
function set_ETTR()
do
local histo={}
local total = 0
histo,total=get_live_histo()
total = 0
local max = #histo
local start = max-hlw
for i = start,(max-1),1 do total = total + histo[i] end -- get the total count in the requested quartiles
local sl = 50
if total <= 0 then -- underexposed
repeat
wheel_left()
sleep(sl)
histo,total=get_live_histo()
total = 0
for i = start,(max-1),1 do total = total + histo[i] end
until total > limit or get_prop(props.USER_TV) <= -480
end
if total > limit then
repeat
wheel_right()
sleep(sl)
histo,total=get_live_histo()
total = 0
for i = start,(max-1),1 do total = total + histo[i] end
until total <= limit or get_prop(props.USER_TV) >= 1056
end
s = get_prop(props.USER_TV)
set_user_tv96(s)
press("shoot_half")
repeat sleep(10) until get_shooting()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
return
end
end
function ND_test()
if ndf > 0 and not non_m then -- capture an ND sim bracket subset based on time
base_shutter_time = tv96_to_usec(base_s)
max_av = get_max_av96()
current_av = get_av96()
if base_shutter_time < 1000000*ndf then
if bookends == 1 then bookend() end -- additional bookends for ND brackets
del_av = max_av - current_av -- max av shift available in ev
del_tv = base_s - usec_to_tv96(ndf*1000000) -- required tv shift in ev
if del_tv < del_av then -- adjust things
temp_av = (current_av+del_tv)
temp = base_s - del_tv -- adjust shutter time to match aperture
else
temp_av = max_av -- set to max
temp = base_s - del_av -- adjust shutter time to match aperture
end
if temp < -480 then -- max shutter of 30s: just in case
temp = -480
end
i = 1 + (ndf*1000000)/tv96_to_usec(temp)
print(i.." ND brackets")
for j = 1, i do
set_tv96_direct(temp)
set_av96(temp_av)
snap()
end
if bookends == 1 then bookend() end
set_av96(current_av)
set_tv96(base_s)
end
elseif ndf > 0 and non_m then -- can't capture an ND sim bracket subset based on time (should never get here, but just in case)
print("Use -ND option")
elseif ndf <0 then -- capture an ND sim bracket subset based on ND filter
if bookends == 1 then bookend() end
if ndf == -1 then
i = 2
elseif ndf == -2 then
i = 4
elseif ndf == -3 then
i = 8
elseif ndf == -4 then
i = 16
elseif ndf == -5 then
i = 32
end
print(i.." ND brackets")
for j = 1, i do
snap()
end
if bookends == 1 then bookend() end
end
end
-- Main Section
if lens == 1 then
print_screen(-1)
print(lens_name())
sleep(3000)
return
end
set_up()
if lens_info() == false then -- adjust a few things as can't use focus mode
mode = 3
non_m = true
if ndf > 0 then
print("Change ND option")
sleep(3000)
return
end
end
print_screen(0) -- switch off logging until bracketing
if help == 0 then -- then switch off console display
set_console_layout(0,0,1,0)
print("")
set_console_autoredraw(-1)
else
set_console_layout(0,0,45,2)
end
-- Set up the bar drawing objects
obj1 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj2 = draw.add("string", 0, 0, "","white",tg)
obj3 = draw.add("string", 0,0,"","white",tg)
obj4 = draw.add("string",0,0,"","white",tg)
obj5 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj6 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj7 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj8 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj9 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj10 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
obj11 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
last_alt_state = get_alt_mode()
image = false -- no image taken yet
last_image_ndof = fmath.new(0)
last_image_fdof = fmath.new(0)
last_image_x = fmath.new(0)
last_image_u = fmath.new(0)
ecnt = get_exp_count()
finish = false
histo={}
repeat -- stay here while in asssess/shooting mode. THIS IS THE PIVOT POINT OF THE SCRIPT
if not non_m then check_focus_etc() end
check_buttons_etc()
if screen_needs_refresh() then dirty = true end
if dirty and not non_m then update() end
if dirty and non_m then non_m_update() end
sleep(300)
until finish -- then take menu requested custom brackets
if mode == 2 or mode == 4 then -- move to focus minimum
if mode == 4 and x > H:int() then
x_end = 2*H:int()
else
x_end = get_focus_distance()
end
repeat
x = get_focus_distance()
call_event_proc("EFLensCom.FocusSearchNear")
sleep(50)
until x == get_focus_distance()
update_DoFs(x)
end
set_up() -- for focus bracketing
if log == 1 then
set_console_autoredraw(-1)
print_screen(-1)
print("...")
print(os.date())
print("Focus distance: "..usefd)
print("Diffraction Aware: "..diff)
print("Overlap Blur: "..overlap.."um")
print("Assumed Pupil Mag: "..pmag)
set_console_autoredraw(1)
end
t1=os.time() -- start the clock
draw.clear()
print_screen(0)
print("Bracketing Started")
if log == 1 then print_screen(-1) end -- switch focus bracketing logging on
if bookends == 1 then bookend() end
t1=sleep_time-(os.time()-t1)
if t1 > 0 then sleep(t1*1000) end
if screen_off == 0 then set_lcd_display(0) end
print("FB #1 @ "..units(fmath.new(x)))
X_bracket() -- First exposure(s)
i = 1
while (u < (H/3):int()) do -- capture the rest of the focus brackets up to H/3
-- Note that focus bracketing is calculated/updated in u space, but actual refocusing is in x space
-- All console feedback is in x space, ie distance estimates measured from the sensor
new_u = 2*F*F*(pmag-1) + F*(u-2*pmag*u) + h*pmag*u
new_u = (new_u/( F*(2*pmag-1) + pmag*(h-2*u))):int() -- new u in mm, using DOFIS model
if new_u <= u then new_u = u + 5 end -- move at least 5mm, just in case. Remember: script is not designed for macro work!
u = new_u
xtemp = (u + F + (F*F)/(u-F) + t) -- next focus bracket estimate in x space: in mm from f(1+m)+t+u
if mode == 4 then
if xtemp:int() > x_end then break end
end
refocus_to(xtemp:int())
i = i + 1 -- keep track of number of images taken
print("FB #"..i.." @ "..units(xtemp))
X_bracket()
x = xtemp:int()
end
if mode == 4 then
if x < x_end then
x = refocus_to(x_end)
print("last image @ "..units(fmath.new(x)))
X_bracket()
end
if sky == 6 then -- take an auto ETTR exposure bracket for the sky
set_ETTR()
snap()
set_tv96_direct(base_s)
print("Sky X")
elseif sky > 0 then -- take a single exposure shot for the sky
set_tv96_direct(s + 96*(sky+1))
snap()
set_tv96_direct(base_s)
print("Sky X")
end
if bookends == 1 then bookend() end
my_restore()
return
end
if u < H:int() then -- take image(s) at H
temp = ((F*F)/(H-F) + F + t + H)
refocus_to(temp:int())
print("@H = "..units(temp))
X_bracket()
end
if u < infinity*H:int() then -- take image(s) at infinity*H
temp = ((F*F)/(infinity*H-F) + F + t + infinity*H)
if temp:int() > inf then -- adjust as at/beyond lens infinity
repeat
infinity = infinity - 1
temp = ((F*F)/(infinity*H-F) + F + t + infinity*H)
until temp:int() < inf or infinity == 2
end
refocus_to(temp:int())
print("@"..infinity.."xH = "..units(temp))
X_bracket()
end
ND_test()
if sky == 6 then -- take an auto ETTR exposure bracket for the sky
set_ETTR()
snap()
set_tv96_direct(base_s)
print("Sky X")
elseif sky > 0 then -- take a single exposure shot for the sky
set_tv96_direct(s + 96*(sky+1))
snap()
set_tv96_direct(base_s)
print("Sky X")
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