Skip to content

Instantly share code, notes, and snippets.

@pigeonhill
Last active July 16, 2022 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pigeonhill/f2b5e1b16e2707d5382966cc9140af3b to your computer and use it in GitHub Desktop.
Save pigeonhill/f2b5e1b16e2707d5382966cc9140af3b to your computer and use it in GitHub Desktop.
--[[
@title FBTL
@chdk_version 1.6
#overlap = 15 "Overlap Blur (um)" [5 30]
#help = 0 "Console" {Off On}
Notes:
* This test 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 feedback for manual focus bracketing.
* Set the overlap blur in the script's menu
* 'Trafic Lights' will show on the left or the right, once an image is taken
* Black shows the script is running
* Yellow means you are positioned at the same location as the last captured image
* Green means that DoF (left = near and right = far) have a positive overlap
* Red means you have a negative overlap
* White means you are at infinity
* Changing focal length will reset the traffic lights
* Switch the console on in the script's menu will provide feedback on the focus, DoFs and the lens stepper count
* If things don't look right either change focal length to reset things or switch the script off and on, ie via the exit button
Release 1.02
photography.grayheron.net
July 2022
--]]
bi = get_buildinfo()
require "drawings"
props=require'propcase'
set_exit_key("down") -- change as required
set_console_autoredraw(1)
set_console_layout(0,0,45,3)
focus_count = -999
reset_lens = false
INF = 999000 -- infinity trap
count = 0
U = 0
tg = 242
-- 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_gui_screen_width() ~= 360 then
print("Need an XIMR build")
sleep(3000)
return
end
-- Functions --
function units(xx) -- pretty print of focus distance
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 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 move_focus_by(n) -- steps
local end_count = call_event_proc('GetEFLensFocusPositionWithLensCom') + n
call_event_proc("EFLensCom.MoveFocus",n, 1)
repeat -- wait until move complete
sleep(20)
until call_event_proc('GetEFLensFocusPositionWithLensCom') == end_count
return end_count
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
print("Calibrating")
call_event_proc('InitializeAdjustmentSystem')
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
repeat
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
call_event_proc("EFLensCom.FocusSearchFar")
sleep(50)
until temp == call_event_proc('GetEFLensFocusPositionWithLensCom') -- make sure we are at 'infinity'
repeat -- to find some useful data
call_event_proc("EFLensCom.MoveFocus", -1, 1)
sleep(50)
until get_focus_distance_lower() < 81000
xc = get_focus_distance_lower() -- Canon lower focus distance 'infinity' start
sleep(20)
far_count = call_event_proc('GetEFLensFocusPositionWithLensCom')
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
repeat
temp = call_event_proc('GetEFLensFocusPositionWithLensCom')
call_event_proc("EFLensCom.FocusSearchNear")
sleep(10)
until temp == call_event_proc('GetEFLensFocusPositionWithLensCom') -- make sure we are at MFD
near_count = call_event_proc('GetEFLensFocusPositionWithLensCom')
MFD = fmath.new(get_focus_distance_lower(),1)
k = (MFD*(MFD-4*F)):sqrt()
total_count = far_count - near_count -- lens max number of steps at the current focal length
reset_lens = false
end
function TL_focus_pos(nn) -- functionalised estimate of focus from the stepper motor position
local a = fmath.new(get_focus_distance_lower(),1)
local b = fmath.new(get_focus_distance_upper(),1)
local c = ((2*a*b)/(a+b)):int()
local d = MFD
-- use a, b or c to 'scale'' the basic functional form
-- change this function as required
-- With the 11-22mm lens, a seems to work well
local temp = (total_count*MFD - 2*F*nn)/(total_count-nn) - (a*nn)/(total_count)
if temp:int() < 0 then
return fmath.new(INF + 1,1)
else
return temp
end
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 thin lens principal, ie NOT the sensor
u = (((xx*(xx-4*F)):sqrt() + xx)/2):int() -- focus distance in mm from front principal
pmag = 1 -- ignore pupil magnification
t = 0 -- as using a thin lens, the hiatus is zero
if xx:int() < 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)
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)
else
fdof = fmath.new(INF+1)
end
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
end
function lens_info()
if lens_name() == "EF-M11-22mm f/4-5.6 IS STM" then -- calculate lens thickness from manufacturer's data
return true
elseif lens_name() == "EF-M28mm f/3.5 MACRO IS STM" then
return true
elseif lens_name() == "EF-M55-200mm f/4.5-6.3 IS STM" then
return true
elseif lens_name() == "EF-M18-55mm f/3.5-5.6 IS STM" then
return true
elseif lens_name() == "EF-M15-45mm f/3.5-6.3 IS STM" then
return true
else -- lens not recognised
return false
end
end
function check_focus()
dof = get_dofinfo()
dirty = false
local test_count = call_event_proc('GetEFLensFocusPositionWithLensCom')
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 then -- recalibrate lens and update focus position
dirty = true
reset_lens = true
image = false
clr = tg
draw.replace(left,"rectf",hdmi+0,0,hdmi+10,15,clr,clr) -- update left traffic light
draw.replace(right,"rectf",hdmi+350,0,hdmi+360,15,clr,clr) -- update right traffic light
elseif test_count ~= focus_count then -- update focus position
dirty = true
end
F = test_F
focus_count = test_count
end
function update() -- update focus position estimate in the console
count = call_event_proc('GetEFLensFocusPositionWithLensCom') - near_count
x = TL_focus_pos(count)
update_DoFs(x)
if x:int() < INF and count <= total_count then
if fdof:int() > INF then
print(units(x)..", NDoF:"..ndof:int()..", FDoF:oo, #:"..count)
else
print(units(x)..", NDoF:"..ndof:int()..", FDoF:"..fdof:int()..", #:"..count)
end
else
print("oo"..",NDoF = "..ndof:int())
end
end
function check_overlaps()
if get_gui_screen_width() == 360 then
hdmi = 0
else
hdmi = 60
end
if get_exp_count() == ecnt then
if not image then
draw.replace(left,"rectf",hdmi+0,0,hdmi+10,15,"black","black")
draw.replace(right,"rectf",hdmi+350,0,hdmi+360,15,"black","black")
draw.overdraw()
end
else
if image then -- display traffic lights
local clr = tg
if x == last_image_x then
clr = "yellow"
elseif x:int() > INF then
clr = "white"
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(left,"rectf",hdmi+0,0,hdmi+10,15,clr,clr) -- update left traffic light
if x == last_image_x then
clr = "yellow"
elseif x:int() > INF then
clr = "white"
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(right,"rectf",hdmi+350,0,hdmi+360,15,clr,clr) -- update right traffic light
else
draw.replace(left,"rectf",hdmi+0,0,hdmi+10,15,"black","black")
draw.replace(right,"rectf",hdmi+350,0,hdmi+360,15,"black","black")
end
draw.overdraw()
dirty = false
end
end
function check_buttons_etc()
wait_click(50) -- check for a button press
if (is_key("print") and get_alt_mode()) then
shoot()
snapped = true
last_image_ndof = ndof
last_image_fdof = fdof
last_image_x = x
last_image_u = u
finish = false
image = true
dirty = true
end
end
-- Main Section --
if lens_info() == false then -- then don't use
print("Can't use for now")
sleep(3000)
return
end
call_event_proc('FA.Create')
sleep(100)
set_up()
lens_cal()
x = TL_focus_pos(count)
update_DoFs(x)
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,1)
end
finish = false
image = false
last_image_ndof = fmath.new(0)
last_image_fdof = fmath.new(0)
last_image_x = fmath.new(0)
ecnt = get_exp_count()
left = draw.add("rectf", 0, 0, 0, 0,"black","black")
right = draw.add("rectf", 0, 0, 0, 0, "black","black")
repeat -- stay here
check_focus()
if reset_lens then lens_cal() end -- focal length changed so recalibrate lens
if screen_needs_refresh() then dirty = true end
if dirty then update() end
check_buttons_etc()
check_overlaps()
sleep(100) -- adjust a 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