Skip to content

Instantly share code, notes, and snippets.

@pigeonhill
Last active Aug 5, 2022
Embed
What would you like to do?
--[[
@title QDFS
@chdk_version 1.6
#coc = 15 "CoC (um)" [5 30]
#help = 1 "Console" {Off On}
Notes:
* This Quasi Depth of Field Scale script only runs on the M3, M10 or M100 with the XIMR version of CHDK. But note, not tested on the M100.
* Go to https://drive.google.com/drive/folders/1drk1xi6kMMIeF5xSqXgnpjGCffF8CVkd?usp=sharing for XIMR builds
*
* The script provides visual feedback for manual focus bracketing
* It was mainly written for wide angle lenses and specifically the EF-M 11-22mm ;-)
* The lens extension and pupil mag are ignored
* Only registered lenses can be used
* Changing focal length will reset the DoF scale
* Switch out and back into ALT mode will remove the DoF of the last captured image
* Switching the console on in the script's menu will provide 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 left of the scale)
* The infinity blur info will disappear if you focus beyond the Canon 'data infinity', when additional info will be shown
* The number of brackets to the hyperfocal is shown on the right, as you focus
* If you focus at infinity you will see additional info
* Pressing the INFO button in ALT mode will toggle the bar on and off, eg as an aid to composing
* If things don't look right either change focal length to reset things or switch the camera on and off
Release 1.13
photography.grayheron.net
August 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)
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)
cal = draw.add("string",0,0,"",tg,tg)
show = true
set_console_autoredraw(1)
set_console_layout(0,0,45,3)
reset_lens = false
-- Check a couple of things first
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_focus_mode() ~= 1 then
print("Need manual focus mode")
sleep(3000)
return
end
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
-- 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
reset_lens = false
call_event_proc('FA.Create')
draw.clear()
draw.replace(bar,"rectf",hdmi,0,hdmi+360,20,"white","white")
draw.replace(cal,"string",hdmi+180-50,2,"Calibrating","black","white")
draw.overdraw()
call_event_proc('InitializeAdjustmentSystem')
sleep(100)
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
repeat
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
sleep(50)
call_event_proc("EFLensCom.FocusSearchFar")
until temp == call_event_proc('GetEFLensFocusPositionWithLensCom') -- make sure we are at 'infinity'
max_count = temp
repeat -- to find some useful data
call_event_proc("EFLensCom.MoveFocus", -1, 1)
sleep(50)
until get_focus_distance_lower() < 81910 -- lower data infinity
far_count = call_event_proc('GetEFLensFocusPositionWithLensCom')+1
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
repeat
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
sleep(50)
call_event_proc("EFLensCom.FocusSearchNear")
until temp == call_event_proc('GetEFLensFocusPositionWithLensCom') -- make sure we are at MFD
near_count = call_event_proc('GetEFLensFocusPositionWithLensCom')
MFD = get_focus_distance_lower()
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
if f1 == f2 then
mag = m1
else
mag = ((F-f1)*(m2-m1))/(f2-f1) + m1
end
end
function lens_info()
if lens_name() == "EF-M11-22mm f/4-5.6 IS STM" then -- calculate lens thickness from manufacturer's data
f1 = 11
f2 = 22
m1 = fmath.new(13,100)
m2 = fmath.new(3,10)
return true
elseif lens_name() == "EF-M28mm f/3.5 MACRO IS STM" then
f1 = 28
f2 = 28
m1 = fmath.new(1,1)
m2 = fmath.new(1,1)
return true
elseif lens_name() == "EF-M55-200mm f/4.5-6.3 IS STM" then
return false
elseif lens_name() == "EF-M18-55mm f/3.5-5.6 IS STM" then
return false
elseif lens_name() == "EF-M15-45mm f/3.5-6.3 IS STM" then
return false
else -- lens not recognised
return false
end
end
function check_focus()
dof = get_dofinfo()
dirty = false
local test_count = call_event_proc('GetEFLensFocusPositionWithLensCom') - 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
if f1 == f2 then
mag = m1
else
mag = ((F-f1)*(m2-m1))/(f2-f1) + m1
end
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
end
function update() -- update focus position estimate in the console
if (focus_count + near_count) <= far_count then
if image then
print("Max:"..(max_count)..",Near:"..(near_count)..",Far:"..(far_count)..",Last:"..(last_count))
else
print("Max:"..(max_count)..",Near:"..(near_count)..",Far:"..(far_count)..",H:"..H:int().."mm")
end
print("#:"..(focus_count)..",Lower:"..get_focus_distance_lower().."mm,Upper:"..get_focus_distance_upper().."mm")
else
if image then
print("Max:"..(max_count)..",Near:"..(near_count)..",Far:"..(far_count)..",Last:"..(last_count))
else
print("Max:"..(max_count)..",Near:"..(near_count)..",Far:"..(far_count)..",H:"..H:int().."mm")
end
print("#:"..(focus_count)..", >INF")
end
pos = 0
k = 360-2*pos
draw.replace(bar,"rectf",hdmi+pos,0,hdmi+k,20,"white","white")
local J1 = far_count - near_count
u = (F*(J1 + J1*mag - mag*focus_count))/(mag*(J1-focus_count))
mfd = (F*(J1 + J1*mag - mag*0))/(mag*(J1-0)) -- MFD relative to the front principal
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
draw.replace(ndof,"rectf",hdmi+pos+p-halfdof,14,hdmi+pos+p,20,"black",fill)
draw.replace(fdof,"rectf",hdmi+pos+p,14,hdmi+pos+p+halfdof,20,"black",fill)
draw.replace(line,"rectf", hdmi+pos,9,hdmi+k,11,"black","black")
draw.replace(fp,"elpsf", hdmi+pos+p,17,2,2,"black","black")
local h1 = H:int()
p = (k - (mfd*k)/(H)):int()
draw.replace(hpos,"line",hdmi+pos+p,0,hdmi+pos+p,20,"red")
p = (k - (mfd*k)/((H/2))):int()
draw.replace(h3pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"black")
p = (k - (mfd*k)/((H/4))):int()
draw.replace(h5pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"black")
p = (k - (mfd*k)/((H/8))):int()
draw.replace(h7pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"black")
p = (k - (mfd*k)/((H/16))):int()
draw.replace(h9pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"black")
p = (k - (mfd*k)/((H/32))):int()
draw.replace(h10pos,"line",hdmi+pos+p,3,hdmi+pos+p,17,"black")
local qq = 0
if u:int() >= H:int() then
qq = ((coc*H)/u):int()
if qq >= 0 then
qq = " "..tostring(qq).."um "
draw.replace(blur,"string",hdmi+pos+5,2,qq,"black","white")
else
draw.remove(blur)
end
else
draw.remove(blur)
end
if u:int() <= (H/3):int() and u:int() > 0 then
qq = (H-u)/(2*u) + 1
qq = " #"..tostring(qq:int()).." "
draw.replace(blur,"string",hdmi+k-6*(string.len(qq))-15,2,qq,"black","white")
else
draw.remove(nb)
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(cal,"string",hdmi+pos+5,2,qq,"black","white")
else
draw.remove(cal)
end
end
function check_overlaps()
local temp = focus_count
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
if grabp and image then
draw.replace(lastdof,"rectf",hdmi+pos+pdof-halfdof,0,hdmi+pos+pdof+halfdof,6,"black","blue")
grabp = false
end
dirty = false
draw.overdraw()
end
function check_buttons_etc()
wait_click(50) -- check for a button press
if (is_key("print") and get_alt_mode()) then
shoot()
last_count = focus_count
image = true
dirty = true
grabp = true
pdof = pj
elseif (is_key("display") and get_alt_mode()) then
if show then
draw_clear()
show = false
elseif not show then
draw.overdraw()
show = true
end
end
end
-- Main Section --
if lens_info() == false then -- then don't use
print("Lens not registered")
sleep(3000)
return
end
show = true
grabp = false
set_up()
lens_cal()
last_count = call_event_proc('GetEFLensFocusPositionWithLensCom')
focus_count = last_count
if (last_count - near_count) ~= 0 then
print("Calibration failed")
sleep(3000)
return
end
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
finish = false
image = false
repeat -- stay here
if get_alt_mode() then
check_focus()
check_buttons_etc()
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()
end
end
if not get_alt_mode() and show then -- reset
image = false
draw.remove(lastdof)
dirty = true
end
sleep(100) -- adjust as required
until finish -- exit via pressing ALT exit button
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment