Skip to content

Instantly share code, notes, and snippets.

@pigeonhill
Last active Oct 26, 2022
Embed
What would you like to do?
--[[
@title QDFS
@chdk_version 1.7
#coc = 15 "CoC (um)" [5 30]
#brak = 0 "X bracket mode?" {ISO Tv}
#isoval = 0 "X Bracketing Value" {Canon ETR+1Ev ETR+2Ev ETR+3Ev ETR+4Ev ETR+5Ev}
#sleep_time = 0 "Delay (s)" [0 10]
#dis = 1 "Display" {#Bracks Focus}
#lensmag = 0 "Get Mag" {Off On Dynamic}
#reg = 1 "H/x Reg Dis" [1 3]
#offset = 1 "Console offset" [1 12]
#title = 1 "Title Line" {Off Full Exit}
Notes:
*
* This Quasi Depth of Field Scale script only runs on the M3 with the XIMR version of CHDK, ie CHDK 1.7
*
* The script provides visual feedback for manual deep focus bracketing, ie not for macro focus bracketing
* It also provides two exposure bracket options from an ETTR exposure (ISO or Tv) and one from whatever Canon AEB is set
* The script first tests to see if a Canon AEB is set. If not the script then looks at the ISO/Tv ETTR bracketing options. Canon meaning whatever AEB is set on the Canon side, or not
* Repeatedly pressing the INFO button will cycle through the Canon screens, including the Canon histogram, which will be active via touch.
* Here you can change the AEB for example
* The script was mainly written for wide angle lenses and specifically the EF-M 11-22mm ;-)
* Only/all EF-M lenses can be used
* An estimate of the front principal location can be found from subtracting the QDFS MFD from the Canon reported MFD
* Changing focal length will reset the DoF scale
* Switching out of ALT mode will exit the script
* Switching the console on in the script's menu will provide additional feedback, eg the lens stepper count and the Canon lower and upper focus bounds
* If you are focused beyond the hypefocal, the DoF Scale will display the infinity blur in microms (on the right of the DoF scale)
* The infinity blur info will disappear if you focus beyond the Canon 'data infinity', when additional info will be shown
* If selected, the number of brackets to the hyperfocal is also shown on the right, as you focus short of H/3
* If Focus is selected in the Display menu, then an ESTIMATE of the focus distance from the sensor is displayed: this can be compared to the Canon reported distance
* and the mag tuned as required
* Pressing the MENU button in ALT mode will toggle the bar on and off, eg as an aid to composing
* Pressing the RIGHT button will ETTR, and pushing the LEFT button will ETTL. ETTR and ETTL thresholds can be changed in the code.
* If things don't look right either change focal length to reset things or restart the script or the camera ;-)
* Further info on the script may be found on my blog, including dynamic focus calibration when the script is running
Release 1.332
photography.grayheron.net
October 2022
--]]
bi = get_buildinfo()
require "drawings"
props=require'propcase'
capmode = require("capmode")
set_exit_key("down") -- change as required
tg = 242
bar = draw.add("rectf", 0, 0, 0, 0, tg,tg)
ndof = draw.add("rectf", 0, 0, 0, 0, tg,tg)
fdof = draw.add("rectf", 0, 0, 0, 0, tg,tg)
lastdof = draw.add("rectf", 0, 0, 0, 0, tg,tg)
line = draw.add("rectf", 0, 0, 0, 0, tg,tg)
hpos = draw.add("line", 0, 0, 0, 0, tg)
h3pos = draw.add("line", 0, 0, 0, 0, tg)
h5pos = draw.add("line", 0, 0, 0, 0, tg)
h7pos = draw.add("line", 0, 0, 0, 0, tg)
h9pos = draw.add("line", 0, 0, 0, 0, tg)
h10pos = draw.add("line", 0, 0, 0, 0, tg)
h0pos = draw.add("line", 0, 0, 0, 0, tg)
blur = draw.add("string",0,0,"",tg,tg)
nb = draw.add("string",0,0,"",tg,tg)
fp = draw.add("elpsf", 1, 1, 1, 1, tg, tg)
inf = draw.add("line", 0, 0, 0, 0, tg)
bar2 = draw.add("rectf", 0, 0, 0, 0, tg,tg)
cal = draw.add("string",0,0,"",tg,tg)
show = true
ettlmode = 0
set_console_autoredraw(1)
set_console_layout(0,0,45,3)
base_s = get_prop(props.USER_TV)
base_av = get_prop(props.USER_AV)
base_iso = get_iso_mode()
set_iso_mode(base_iso)
reset_lens = false
-- Check a few things first
if (bi.platform=="m3") and (bi.platsub=="101a") then
FocusSearchFar_FW = 0xfc43dfd9
FocusSearchNear_FW = 0xfc43dfff
FocusPositionWithLensCom = 0xfc5a85c7
shutter_trigger = "print"
elseif (bi.platform=="m3") and (bi.platsub=="120f") then
FocusSearchFar_FW = 0xfc43e349
FocusSearchNear_FW = 0xfc43e36f
FocusPositionWithLensCom = 0xfc5a8adf
shutter_trigger = "print"
elseif (bi.platform=="m3") and (bi.platsub=="121a") then
FocusSearchFar_FW = 0xfc43e379
FocusSearchNear_FW = 0xfc43e39f
FocusPositionWithLensCom = 0xfc5a8b0f
shutter_trigger = "print"
else
print("Only runs on the M3")
sleep(3000)
return -- exit
end
if get_focus_mode() ~= 1 then
print("Switch to manual focus mode")
sleep(3000)
return -- exit
end
if capmode.get_name() ~= "M" then
print("Put camera in M mode")
sleep(3000)
return
end
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
if get_iso_mode() == 0 then
print("Switch out of uto ISO")
sleep(3000)
return
end
if get_iso_mode() > 1 then
print("ISO > 100 : Press any key to continue")
wait_click()
end
set_draw_title_line(title)
-- Functions --
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 -- in mm
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 lens_name() -- does what it says
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 lens_cal() -- called at the start and when focal length changes: sets lens to MFD
local test_X = get_prop(props.USER_TV)
wheel_right() -- check wheel is in Tv adjust mode
sleep(500)
reset_lens = false
draw.clear()
draw.replace(bar,"rectf",hdmi,0,hdmi+360,20,"black","black")
draw.replace(cal,"string",hdmi+180-50,2,"Calibrating","white","black")
draw.overdraw()
local temp = 0
if get_prop(props.USER_TV) == test_X then -- in ISO adjust mode
wheel_left()
click("up") -- switch from ISO to Tv adjust mode
else
wheel_left()
end
repeat
temp = call_func_ptr(FocusPositionWithLensCom)
call_func_ptr(FocusSearchFar_FW)
sleep(50)
until temp == call_func_ptr(FocusPositionWithLensCom) -- make sure we are at 'infinity'
max_count = temp
local inf_dis = get_focus_distance_lower()
repeat -- to find some useful data
call_event_proc("EFLensCom.MoveFocus", -1, 1)
sleep(50)
until get_focus_distance_lower() < inf_dis -- lower data infinity
far_count = call_func_ptr(FocusPositionWithLensCom)+1
repeat -- to find magnification near H
call_event_proc("EFLensCom.MoveFocus", -1, 1)
sleep(50)
until get_focus_distance_lower() < (H/reg):int() -- move to around the hyperfocal/reg
local h_count = call_func_ptr(FocusPositionWithLensCom)
local h_dis = get_focus_distance_lower()
repeat
temp = call_func_ptr(FocusPositionWithLensCom)
call_func_ptr(FocusSearchNear_FW)
sleep(50)
until temp == call_func_ptr(FocusPositionWithLensCom) -- make sure we are at MFD
near_count = call_func_ptr(FocusPositionWithLensCom)
focus_count = near_count - far_count
MFD = get_focus_distance_lower()
mfd = MFD -- will be updated later
h_count = h_count-near_count + 1
mag = (F*(far_count-near_count))/((far_count-near_count-h_count)*(h_dis-2*F)) -- ignores hiatus and extension at this stage, ie get's things going
draw.clear()
end
function set_up()
dof = get_dofinfo()
F = fmath.new(dof.focal_length,1000) -- focal length in mm (real)
base_av = get_prop(props.USER_AV)
n = fmath.new(av96_to_aperture(base_av),1000) -- aperture number as a real
H = (1000*(F*F))/(n*coc) + F -- Relative to front principal
press("shoot_half")
repeat sleep(10) until get_shooting()
release("shoot_half")
repeat sleep(10) until (not get_shooting())
end
function lens_info()
if lens_name():sub(1,4) == "EF-M" then
return true
else -- lens not recognised
return false
end
end
function check_focus()
dof = get_dofinfo()
dirty = false
local test_count = call_func_ptr(FocusPositionWithLensCom) - near_count
local test_F = fmath.new(dof.focal_length,1000)
local test_n = fmath.new(av96_to_aperture(get_user_av96()),1000)
if test_F ~= F then
repeat
dof = get_dofinfo()
F = fmath.new(dof.focal_length,1000)
sleep(500)
dof = get_dofinfo()
test_F = fmath.new(dof.focal_length,1000)
until test_F == F
grabp = false
dirty = true
reset_lens = true
image = false
F = test_F
n = test_n
H = 1000*(F*F)/(n*coc) + F
draw.remove(lastdof)
elseif test_n ~= n then
grabp = false
dirty = true
image = false
F = test_F
n = test_n
H = 1000*(F*F)/(n*coc) + F
draw.remove(lastdof)
elseif test_count ~= focus_count then -- update things
dirty = true
focus_count = test_count
end
if lensmag == 2 and lensm ~= -1 then
mag = lensm
dirty = true
lensm = -1
end
end
function update()
pos = 0
k = 300-2*pos
draw.replace(bar,"rectf",hdmi+pos,0,hdmi+360,20,"black","black")
local J1 = far_count - near_count
if focus_count >= J1 then
u = fmath.new(810000,1)
else
u = (F*(J1 + J1*mag - mag*focus_count))/(mag*(J1-focus_count))
end
if (focus_count) <= (far_count - near_count) and not show_mag then
if u < H then
temp = H/u
print("#:"..(focus_count).."/"..max_count-near_count..", L/U:"..get_focus_distance_lower().."/"..get_focus_distance_upper().."mm, H/"..(temp):tostr(1)..", "..del_ev:tostr(1).."Ev")
else
temp = u/H
print("#:"..(focus_count).."/"..max_count-near_count..", L/U:"..get_focus_distance_lower().."/"..get_focus_distance_upper().."mm, "..(temp):tostr(1).."H"..", "..del_ev:tostr(1).."Ev")
end
elseif not show_mag then
print("#:"..(focus_count).."/"..max_count-near_count..", >INF"..", "..del_ev:tostr(1).."Ev")
end
mfd = (F*(J1 + J1*mag - mag*0))/(mag*(J1-0)) -- Min focus distance relative to the front principal
T = MFD - mfd
T = T - F - (F*F)/(mfd-F) -- hiatus estimate at MFD
x = F + (F*F)/(u-F) + T + u
halfdof = (mfd*k)/H
p = k - (mfd*k)/u
p = p:int()
halfdof = halfdof:int()
pj = p
fill = "grey_light"
if image then
if p == pdof then
fill = "yellow"
elseif p >= (pdof-2*halfdof) and p <= (pdof+2*halfdof) then
fill = "green"
else
fill = "red"
end
end
if (p-halfdof) <=0 then
draw.replace(ndof,"rectf",hdmi+pos+0,14,hdmi+pos+p,20,fill,fill)
else
draw.replace(ndof,"rectf",hdmi+pos+p-halfdof,14,hdmi+pos+p,20,fill,fill)
end
if u:int() > H:int() then
draw.replace(fdof,"rectf",hdmi+pos+p,14,hdmi+k,20,fill,fill)
else
draw.replace(fdof,"rectf",hdmi+pos+p,14,hdmi+pos+p+halfdof,20,fill,fill)
end
draw.replace(line,"rectf", hdmi+pos,10,hdmi+k,10,"white","white")
draw.replace(fp,"elpsf", hdmi+pos+p,17,2,2,"black","black")
local h1 = H:int()
draw.replace(h0pos,"line",hdmi+pos+0,0,hdmi+pos+0,20,"white")
p = (k - (mfd*k)/(H)):int()
if p >=0 then draw.replace(hpos,"line",hdmi+pos+p,0,hdmi+pos+p,20,"red") else draw.remove(hpos) end
p = (k - (mfd*k)/((H/2))):int()
if p >= 0 then draw.replace(h3pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"white") else draw.remove(h3pos) end
p = (k - (mfd*k)/((H/4))):int()
if p >= 0 then draw.replace(h5pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"white") else draw.remove(h5pos) end
p = (k - (mfd*k)/((H/8))):int()
if p >= 0 then draw.replace(h7pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"white") else draw.remove(h7pos) end
p = (k - (mfd*k)/((H/16))):int()
if p >= 0 then draw.replace(h9pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"white") else draw.remove(h9pos) end
p = (k - (mfd*k)/((H/32))):int()
if p >= 0 then draw.replace(h10pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"white") else draw.remove(h10pos) end
p = k
draw.replace(inf,"line",hdmi+pos+p,0,hdmi+pos+p,20,"white")
local qq = 0
if dis == 1 then
qq = x:int().."mm"
draw.replace(blur,"string",hdmi+k+10,2,qq,"white","black")
elseif u:int() <= (H/3):int() and u:int() > 0 and not image and dis == 0 then
qq = (H-u)/(2*u) + 1
qq = "#"..tostring(qq:int())
draw.replace(blur,"string",hdmi+k+10,2,qq,"white","black")
end
if u:int() >= H:int() then
qq = (1/2+((coc*H)/u)):int()
if qq >= 0 then
qq = tostring(qq).."um "
draw.replace(blur,"string",hdmi+k+10,2,qq,"white","black")
else
draw.remove(blur)
end
end
if get_focus_distance_lower() >= 81910 and not image then
qq1 = (H-mfd)/(2*mfd) + 1
qq1 = tostring(qq1:int())
qq = " CoC:"..coc..", MFD:"..mfd:int()..", H:"..H:int()..", #:"..qq1
draw.replace(bar2,"rectf",hdmi+pos,0,hdmi+360,20,"black","black")
draw.replace(cal,"string",hdmi+pos+5,2,qq,"white","black")
else
draw.remove(cal)
draw.remove(bar2)
end
end
function check_overlaps()
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
if grabp and image then
if u:int() > H:int() then
draw.replace(lastdof,"rectf",hdmi+pos+pdof-halfdof,0,hdmi+k,6,"grey_light","grey_light")
else
draw.replace(lastdof,"rectf",hdmi+pos+pdof-halfdof,0,hdmi+pos+pdof+halfdof,6,"grey_light","grey_light")
end
grabp = false
end
dirty = false
draw.overdraw()
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 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 take_bracket()
if brak == 0 then
local iso = get_iso_mode()
set_iso_mode(iso)
sleep(100)
snap()
local isov = iso
isov = isov + isoval*3
if isov > 16 then isov = 16 end
set_iso_mode(isov)
sleep(100)
snap()
set_iso_mode(iso)
else
local s = get_prop(props.TV)
snap()
set_tv96_direct(s - 96 * isoval)
snap()
set_tv96_direct(s)
end
end
function set_ETTR()
do
local histo={}
local total = 0
histo,total=get_live_histo()
local total_count = total
total = 0
local max = #histo - 1
start = 230 -- top 1/3 stop
for i = start,max,1 do total = total + histo[i] end -- get the total count in the requested bins
local sl = 50
if (100*total)/total_count < 1 then
repeat
wheel_left()
sleep(sl)
histo,total=get_live_histo()
total_count = total
total = 0
for i = start,max,1 do total = total + histo[i] end
until (100*total)/total_count >= 1 or get_prop(props.USER_TV) <= -480
elseif (100*total)/total_count > 1 then
repeat
wheel_right()
sleep(sl)
histo,total=get_live_histo()
total_count = total
total = 0
for i = start,max,1 do total = total + histo[i] end
until (1000*total)/total_count <= 1 or get_prop(props.USER_TV) >= 1052
end
return
end
end
function set_ETTL()
do
local histo={}
local total = 0
local sl = 100
local test_X = get_prop(props.USER_TV)
if ettlmode == 1 then
local isov = get_iso_mode()
local i = 0
histo,total=get_live_histo()
local total_count = total
total = 0
for i = 0,25,1 do total = total + histo[i] end -- get the total count in the requested bins (see https://scantips.com/lights/gamma3.html)
if (100*total)/total_count > 10 then
repeat
wheel_right()
isov = get_iso_mode()
if isov > 16 then isov = 16 end
set_iso_mode(isov)
sleep(sl)
histo,total=get_live_histo()
total_count = total
total = 0
for i = 0,25,1 do total = total + histo[i] end
until (100*total)/total_count <= 10 or get_prop(props.USER_TV) <= -480 or isov >= 16
click("up")
end
else
histo,total=get_live_histo()
local total_count = total
total = 0
for i = 0,25,1 do total = total + histo[i] end -- get the total count in the requested bins (see https://scantips.com/lights/gamma3.html)
if (100*total)/total_count > 10 then
repeat
wheel_left()
histo,total=get_live_histo()
total_count = total
total = 0
for i = 0,25,1 do total = total + histo[i] end
until (100*total)/total_count <= 10 or get_prop(props.USER_TV) <= -480
elseif (100*total)/total_count < 10 then
repeat
wheel_right()
sleep(sl)
histo,total=get_live_histo()
total_count = total
total = 0
for i = 0,25,1 do total = total + histo[i] end
until (100*total)/total_count >= 10 or get_prop(props.USER_TV) >= 1052
end
end
return
end
end
function check_X()
del_ev = -(fmath.new(get_prop(props.USER_TV),96)-fmath.new(s,96))
if del_ev ~= 0 then dirty = true end
return
end
function check_buttons_etc()
wait_click(50) -- check for a button press
show_mag = false
if is_key("set") and (lensmag == 1 or lensmag == 2) then
lensm = (F*(far_count-near_count))/((far_count-near_count-focus_count)*(get_focus_distance_lower() - (MFD-mfd) - F))
if lensmag == 2 then
mag = lensm
elseif lensmag == 1 then
print("Tuning Mag = "..lensm:tostr(4))
sleep(3000)
show_mag = true
end
image = false
grabp = false
dirty = true
draw.remove(lastdof)
elseif is_key("up") then -- move console position
offset = offset + 1
if offset == 13 then offset = 0 end
if offset == 0 then -- then switch off console display
set_console_layout(0,0,1,0)
print("")
set_console_autoredraw(-1)
else
set_console_autoredraw(1)
set_console_layout(0,offset-1,45,offset)
end
elseif is_key("zoom_out") then -- toggle CHDK histogram on and off
if get_config_value(1060) == 0 then set_config_value(1060,1) else set_config_value(1060,0) end
elseif is_key("right") then -- ETTR requested
set_ETTR()
elseif is_key("left") then -- ETTR requested
set_ETTL()
elseif is_key(shutter_trigger) then
sleep(sleep_time*1000)
if isoval > 0 and get_prop(props.BRACKET_MODE) == 0 then
take_bracket()
else
snap()
end
last_count = focus_count
image = true
dirty = true
grabp = true
pdof = pj
elseif is_key("menu") then
if show then
draw_clear()
show = false
elseif not show then
draw.overdraw()
show = true
end
elseif is_key("display") then
click("display")
end
if ecnt ~= get_exp_count() and not is_key(shutter_trigger) then
ecnt = get_exp_count()
last_count = focus_count
image = true
dirty = true
grabp = true
pdof = pj
end
if is_key("zoom_in") then
last_count = focus_count
image = true
dirty = true
grabp = true
pdof = pj
s = get_prop(props.USER_TV)
end
end
-- Main Section --
if lens_info() == false then -- then don't use
print("Lens not recognised")
sleep(3000)
return
end
show = true
grabp = false
set_up()
lens_cal()
lensm = -1
last_count = call_func_ptr(FocusPositionWithLensCom) - near_count
focus_count = last_count
if last_count ~= 0 or (mag*100):int() <= 0 then
print("Calibration failed: Try Again!")
sleep(3000)
return
end
s = get_prop(props.USER_TV)
base_s = s
del_ev = 0
if offset == 0 then -- then switch off console display
set_console_layout(0,0,1,0)
print("")
set_console_autoredraw(-1)
else
set_console_autoredraw(1)
set_console_layout(0,offset-1,45,offset)
end
finish = false
image = false
ecnt = get_exp_count()
repeat -- stay here
if get_alt_mode() then
check_focus()
check_buttons_etc()
check_X()
if reset_lens then lens_cal() end -- focal length changed so recalibrate lens
if screen_needs_refresh() then dirty = true end
if dirty and show then
update()
check_overlaps()
dirty = false
end
elseif not get_alt_mode() then
finish = true
end
sleep(100) -- adjust as required
until finish -- exit script
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment