Skip to content

Instantly share code, notes, and snippets.

@pigeonhill
Last active June 13, 2023 19:26
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save pigeonhill/34c48d571a05e4abfc351030c893d002 to your computer and use it in GitHub Desktop.
Depth Of Field Info (Split lens version)
--[[
DOFIS
Depth of Field Info Script
NOTE: This script assumes you are running the Lua fix from the experimental area or have the Lua fix module in your build, ie Latest Lua fix Build (2020-12-28)
In LV this script dynamically displays (in the ML top bar) the enhanced focus information: in non-LV mode, the Canon 'Shooting Settings' screen provides focus bracketing feedback.
In LV, if the display option is set to DoFs, then if you are focused less than H, DOFI will show the relative (R), either side of the point of focus,
or absolute (A) DoFs, relative to the sensor, plus show the blurs when focus is greater than H.
The script is macro aware and will switch DoF models if the magnification is greater than the one you set in the DOFIS menu. Macro DoF reporting only works in relative DoF mode.
DOFIS provides visual feedback when a user-set diffraction criterion is met, ie a diffraction limited point. One way to set this is to assume it is at. say, 3 sensor pixcels.
Green means below the limit, red above the limit.
The script visually shows the overlap status to aid focus bracketing: green means a positive overlap and red a focus gap between the current focus and the last image taken and...
yellow means the current focus is the same as the last image captured. The criterion used is the ML CoC value, with or without diffraction awareness.
If required, eg in bright sunshine when it is difficult to see the LV, you can switch on a Traffic Light mode. In TL mode the overlap feedback (on the left) and the infinity defocus blur (on the right)...
are shown as two large circles. The overlap colours are as above. The infinity blur colours are as follows:
- Red = infinity blur > than the ML set (overlap) defocus blur, ie the CoC used as the overlap criterion = focused at less than H
- Yellow = infinity defocus blur between the overlap and 1/3 of the overlap defocus blur, ie focused between H and 3H
- Green = infinity defocus blur < CoC/3, ie focused beyond 3H but not yet at infinity
- Black = focused at infinity
TL mode is initiated after you take an image and reset by changing the aperture value.
If full auto focus bracketing is switched on and a registered (non EFM) AF lens is found, and focus is less than H, and diffraction is not too high, DOFIS will automatically focus bracket to 4x hyperfocal...
Note Canon focus feedback becomes coarser as you focus towards infinity, so focus control becomes harder.
If you are focus bracketing in full mode, you have an option to take an exposure bracket at 4xH.
To use the infinity exposure bracketing, ETTR for the scene, eg the sky in a landscape, as your base exposure, and set the Ev lift you wish to use for the land.
ML AB will be switched off, if on, in full mode.
If semi auto focus bracketing is switched on (dx or H), then pressing the trigger key will move the lens to the next focus position requested.
In semi mode you can use ML AB.
Note in semi H mode, DOFIS will move the lens close to H, ie just before or just after, following which you can manually fine tune.
You can switch off auto focus bracketing and only use DOFIS exposure bracketing.
DOFIS will also show an estimate of the number of brackets from where you are focused to the (overlap) hyperfocal.
Changing aperature or focal length will reset DOFI bracketing, as will exposing the ML menu, eg to access additional info.
You can choose betwen a (registered) zoom, asymmetric, split thin lens model or a simple thin lens model. DOFIS shows this as S or T.
DOFIS alerts you to diffraction aware being on by displaying a + rather than a - to the right of the S ot T alert.
The normal DOFIS feedback has the following format: {S or T}{- or +}{#n or =H or >H or INF}{A or R}{[near DoF][far DoF] or [near DoF][blurs] or [blurs]}
You need to set the ML CoC, as this will be the the defocus overlap criterion. If diffraction aware is on, overlap will be adjusted so the ML Coc is the total blur...
comprised of the defocus blur and the diffraction blur in quadrature. If the diffraction blur is greater than the ML CoC, DOFIS will alert you.
If registered in the script, and found, a lens will be automatically used in Auto mode. Also, in Auto mode, if the lens is not found, DOFIS defaults to a thin lens model, thus
I recommend the Auto setting always be on.
You can access other infomation in the DOFIS menu, eg the AoVs and magnification at your point of focus etc. Note, this info will change according to whether you are...
using a registered lens or a thin lens; for example, to fully account for focus breathing in the AoV, DOFIS needs to know the pupil magnification.
DOFIS was mainly developed for a 5D3, but also tested om an EOSM. On the EOSM auto focusing modes should not be used.
Release 1.99
March 2021
Garry George
photography.grayheron.net
--]]
require("config")
mylens = {}
--[[
Add your Lens Info here
DOFIS relies on Canon/ML distance reporting is 'acceptable' ;-)
Best to measure the lens Max Mag yourself or seek our actual manufactuer's data, ie rather than using advertising data
Also, measure pupil mag by taking focused images of the exit and entrance pupils, with a focused ruler in the plane of the pupil...
(for example see http://photography.grayheron.net/2020/09/dofis-start-of-major-update.html)
I recommend setting move to 0 on the EOSM or any camera where the lens movement is too slow, eg a macro lens. Use ML focus bracketing or just use manual DOFIS feedback when focus bracketing.
--]]
-- Lens #1
mylens[1] = {}
mylens[1][1] = "12-24mm" -- lens name
mylens[1][2] = 0.154 -- Max Mag at longest FL
mylens[1][3] = 280 -- Minimum Focus at longest FL
mylens[1][4] = 24 -- Longest FL
mylens[1][5] = -1 -- move direction (towards infinity)
mylens[1][6] = 6.4 -- Pupil mag at widest FL
mylens[1][7] = 4.2 -- Pupil mag at longest FL
mylens[1][8] = 12 -- Widest FL
mylens[1][9] = 0.085 -- Max Mag at widest FL
mylens[1][10] = 280 -- Minimum Focus at widest FL
--Lens #2
mylens[2] = {}
mylens[2][1] = "EF24-105mm f/4L IS USM"
mylens[2][2] = 0.227
mylens[2][3] = 450
mylens[2][4] = 105
mylens[2][5] = -1
mylens[2][6] = 4.08
mylens[2][7] = 1
mylens[2][8] = 24
mylens[2][9] = 0.071 -- Max Mag at widest FL
mylens[2][10] = 480 -- Minimum Focus at widest FL
--Lens #3
mylens[3] = {}
mylens[3][1] = "EF100mm f/2.8L Macro IS USM"
mylens[3][2] = 1.0
mylens[3][3] = 300
mylens[3][4] = 100
mylens[3][5] = 0
mylens[3][6] = 0.846
mylens[3][7] = 0.846
mylens[3][8] = 100
mylens[3][9] = 1.0 -- Max Mag at widest FL
mylens[3][10] = 300 -- Minimum Focus at widest FL
--Lens #4
mylens[4] = {}
mylens[4][1] = "EF-M11-22mm f/4-5.6 IS STM"
mylens[4][2] = 0.3
mylens[4][3] = 150
mylens[4][4] = 22
mylens[4][5] = 0
mylens[4][6] = 3.2
mylens[4][7] = 2.26
mylens[4][8] = 11
mylens[4][9] = 0.13 -- Max Mag at widest FL
mylens[4][10] = 150 -- Minimum Focus at widest FL
--Lens #5
mylens[5] = {}
mylens[5][1] = "TS-E24mm f/3.5L II"
mylens[5][2] = 0.34 -- Max Mag at longest FL
mylens[5][3] = 230 -- Minimum Focus at longest FL
mylens[5][4] = 24 -- Longest FL
mylens[5][5] = 0 -- move direction (towards infinity)
mylens[5][6] = 2.64 -- Pupil mag at widest FL
mylens[5][7] = 2.64 -- Pupil mag at longest FL
mylens[5][8] = 24 -- Widest FL
mylens[5][9] = 0.34 -- Max Mag at widest FL
mylens[5][10] = 230 -- Minimum Focus at widest FL
-- add additional lenses here and remove comment dashes. Make sure lens[n] number is contiguous with previous lenses
--Lens #n
--mylens[n] = {}
--mylens[n][1] =
--mylens[n][2] =
--mylens[n][3] =
--mylens[n][4] =
--mylens[n][5] =
--mylens[n][6] =
--mylens[n][7] =
--mylens[n][8] =
--mylens[n][9] =
--mylens[n][10] =
--********Change the following as required**********
button = KEY.RATE -- change as required
button_t = "RATE" -- add a 4 letter reminder here
--button = KEY.PLAY -- change as required
--button_t = "PLAY" -- add a 4 letter reminder here
--
-- Explicity confirm your sensor data here
sensor_h = 36
sensor_v = 24
sensor_p = 6.3 -- sensor photosite (um)
--sensor_h = 22.3
--sensor_v = 14.9
--sensor_p = 4.29 -- sensor photosite (um)
p_factor = 3 -- 3 to 4, with 3 a mimimum for
-- accounting for anti aliasing
--**************************************************
menu.set("DOF Settings","DOF info in LiveView",0)
wave = 0.550
infinity = 655000 -- trap ML reported infinity
f = lens.focal_length
x = lens.focus_distance
a = camera.aperture.value
ML_blur = menu.get("DOF Settings","Circle of Confusion",0) -- this is the overlap blur you need to set in ML
defocus_blur = ML_blur
H = 0
ndof = 0
fdof = 0
infinity_blur = 0
diff_blur = 0
diff_blur_inf = 0
total_blur = 0
blurs = ""
base_blurs = ""
num = 0
u = 0
last_u = u
last_f = f
last_I_f = f
last_x = 0
last_image_x = x
last_a = a
last_ndof = ndof
last_fdof = fdof
lens_0 = 0
lens_e = 0
CON = "ON"
COFF = "OFF"
image_taken = false
max_mag = 0
min_mag = 0
max_fl = 0
min_fl = 0
mfd = 0
mfd_l = 0
mfd_w = 0
temp = ""
more = {"LT","LE","FP","M@x","Focus","HF","Defocus","Diff","LN","#B","H-AoV","V-AoV","D-AoV","P_mag","Pano","Ratio","lp"}
choices1 = {"Blurs","R-DoFs","A-DoFs"}
choices2 = {"Auto","Thin"}
choices3 = {"OFF","Full","Semi(dx)","Semi(H)"}
choices4 = {"OFF","Mode 1","Mode 2"}
lens_t = 0
mag_u = 0
lens_model_ok = nil
diff = false
no_good = false
auto_bracket = false
direction = 0
new_u = 0
refresh = true
help_txt = ""
p_mag = 1 -- pupil mag at f
p = 0
exit_pupil = 0
ratio = 0
t_l = 0
t_w = 0
h = 0
current_fdof = 0
current_ndof = 0
mag_t = 0.5
lp_data = 0
lp = 0
a_posf = 0
J = 0
update_now = true
no_good = false
cleaned = true
function my_display(text,time,x_pos,y_pos)
text = string.rep("\n",y_pos)..string.rep(" ",x_pos)..text
display.notify_box(text,time*1000)
end
if (x==0 or a==0 or f==0) then
no_good = true
my_display("Can't use DOFIS",2,0,0)
return
end
function property.APERTURE:handler(value)
image_taken = false
update_now = true
cleaned = false
end
function my_shoot()
camera.shoot()
end
function myround(num, dp) -- written this way to eliminate rounding errors with other approaches and because ML Lua doesn't have a string.format
if dp and dp>0 then
local adjusted = false
if num < 1 and num >= 0 then -- number positive and less than 1
num = num + 1 -- needed to protect 0s extraction
adjusted = true
elseif num < 0 and num > -1 then -- number negative and less than 1
num = num - 1 -- needed to protect 0s extraction
adjusted = true
end
local temp = math.floor(num) -- integer part
local temp2 = tostring(math.floor(num*10^dp)) -- shift dp and round
temp2 = temp2:sub(-dp) -- decimal part
if adjusted and num >= 0 then
temp = "0"
elseif adjusted and num < 0 then
temp ="-0"
end
return temp.."."..temp2
end
return tostring(math.floor(num))
end
function x2u(xin) -- convert from sensor to front principal focus distance and update a few lens distances
if xin >= infinity then
mag_u = 0
else
local q = xin - lens_t
mag_u = (q-2*f-math.sqrt(q*(q-4*f)))/(2*f) -- magnification
end -- magnification
lens_e = f*mag_u -- 'lens extension' in the DOFIS model
lens_0 = f + lens_e + lens_t -- position of 'front principal' from the sensor plane
return xin-lens_0
end
function overlap_blur()
local w = last_u
local uu = u
if uu < w then
uu,w = w,uu
end
if uu==w then
w=0
else
w=(f*(uu+w-2*p)+p*(uu+w)-2*uu*w)/(w-uu)
w=math.abs(1000*(f*f)/(a*w))
end
if image_taken then return w else return -1 end
end
function Bookends()
if DOFIS_Menu.submenu["Bookends?"].value == "ON" then
local temp = camera.shutter.ms
camera.shutter.ms = 1
my_shoot()
camera.shutter.ms = temp
end
end
function update()
f = lens.focal_length
a = camera.aperture.value
x = lens.focus_distance
last_x = x
last_a = a
if DOFIS_Menu.submenu["IR Freq"].value == CON then wave = 0.85 else wave=0.55 end
ML_blur = menu.get("DOF Settings","Circle of Confusion",0)
if f ~= last_I_f or a ~= last_a or menu.visible then -- reset bracketing
image_taken = false
cleaned = false
last_I_f = f
last_a = a
end
if DOFIS_Menu.submenu["Diffraction Aware"].value == CON then diff = true else diff = false end
if DOFIS_Menu.submenu["Lens Model"].value == "Auto" then -- check for a registered lens
lens_model_ok = false
lens_t = 0
p = 0
p_mag = 1
for i=1,#mylens do
local mag_f = 0
local X_f = 0
if lens.name == mylens[i][1] then
max_mag = mylens[i][2]
min_mag = mylens[i][9]
mfd_l = mylens[i][3]
mfd_w = mylens[i][10]
max_fl = mylens[i][4]
min_fl = mylens[i][8]
if max_fl ~= min_fl then -- this is a zoom lens, so estimate MFD, Max Mag and p_mag at f, ie assume linear change over zoom range
mag_f = min_mag + ((f-min_fl)*(max_mag-min_mag))/(max_fl-min_fl)
X_f = mfd_w + ((f-min_fl)*(mfd_l-mfd_w))/(max_fl-min_fl)
p_mag = mylens[i][6] + ((f-min_fl)*(mylens[i][7]-mylens[i][6]))/(max_fl-min_fl)
else -- it's a prime lens
mag_f = max_mag
X_f = mfd_l
p_mag = mylens[i][6]
end
lens_t = X_f - (f*(1+mag_f)*(1+mag_f))/mag_f
direction = mylens[i][5]
p = f*(1-1/p_mag)
lens_model_ok = true
break
end
end
elseif DOFIS_Menu.submenu["Lens Model"].value == "Thin" then
lens_model_ok = false
lens_t = 0
p = 0
p_mag = 1
else
lens_model_ok = nil -- shouldn't ever get here
end
diff_blur = 2.44*wave*a*(1+mag_u/p_mag) -- at focus x
diff_blur_inf = 2.44*wave*a -- at infinity
defocus_blur = ML_blur -- zero diffraction case
if diff then -- calculate defocus blur
if diff_blur < ML_blur then
defocus_blur = math.sqrt(ML_blur*ML_blur - diff_blur*diff_blur)
no_good = false
else
--no_good = true -- diffraction too high
defocus_blur = 1
no_good = true
end
end
h = (1000*f*f)/(a*defocus_blur)
H = h + f -- as measured from the front principal, ie insensitive to pupil mag
do -- esitmate of diffraction impacted resolution FoM
local aa = 380/(wave*a*(1+mag_u/p_mag)) -- (lp/mm) MTF50 diffraction based resolution at aperture a
local bb = 1000/(p_factor*sensor_p) -- (lp/mm) sensor max resolution
local cc = 380/((wave*camera.aperture.min.value)) -- (lp/mm) MTF50 diffraction based resolution, wide open and at infinity
local dd = (1/aa)*(1/aa) + (1/bb)*(1/bb)
lp = math.sqrt(1/dd) -- lp/mm estimate
dd = math.sqrt(((1/cc)*(1/cc) + (1/bb)*(1/bb))/dd)
lp_data = 100*dd -- % lp/mm resolution reduction from infinity and wide open
end
u = x2u(x) -- focus distance from 'front principal'. Note: H, AoV and all relative DoF calculations are also sensitive to entrance pupil's location
infinity_blur = defocus_blur*(H-f)/(u-f) -- defocus infinity blur
exit_pupil = myround((lens_0+p),0) -- exit pupil position or pano pivot point
if DOFIS_Menu.submenu["On/Off"].value == CON and DOFIS_Menu.submenu["Show TS Info"].value == CON and DOFIS_Menu.submenu["Hinge Calculator"].value ~= 0 then
a_posf = math.tan(0.01745329251*DOFIS_Menu.submenu["Hinge Calculator"].value/10)
a_posf = math.atan((a_posf*(u-f))/f)//0.01745329251
a_posf = a_posf + DOFIS_Menu.submenu["Hinge Calculator"].value/10
if a_posf < 0 then a_posf = 180 + a_posf end
J = f/math.sin(0.01745329251*DOFIS_Menu.submenu["Hinge Calculator"].value/10)
end
-- estimate of (non-macro) absolute depths of field
if x > infinity then
ndof = H
else
ndof = u-((u-f)*(u-p))/(h+(u-f)) -- as measured from the front principal, but factoring in the pupil mag
end
if u < H then
fdof = u+((u-p)*(u-f))/(h-(u-f)) -- from the front principal
ratio = (u-ndof)/(fdof-u) -- relative near/far DoF ratio
else
fdof = infinity
ratio = 0
end
mag_t = DOFIS_Menu.submenu["Mag Model"].value/10
if mag_u > mag_t then -- use macro DoF estimates
ndof = (0.002*wave*((1+mag_u/p_mag)*a)*((1+mag_u/p_mag)*a))/(mag_u*mag_u)
fdof = u+ndof
ndof = u-ndof
end
-- estimate [:-)] of number of focus brackets to get to H
do
local u_stop = H/3
local uu = u
if uu < u_stop then
num = 2
while uu < u_stop
do
uu=(uu*(H-2*f))/(H-2*uu)
num = num + 1
end
elseif u > H then
num = 1
else
num = 2
end
end
total_blur = math.sqrt(infinity_blur*infinity_blur + diff_blur*diff_blur)
base_blurs = "["..myround(infinity_blur,0).."/"..myround(diff_blur,0).."/"..myround(total_blur,0).."]"
if DOFIS_Menu.submenu["Display Options"].value == "Blurs" then -- only show infinity blurs
blurs = base_blurs
else
local tempn = 0
local tempf = 0
if x > infinity then -- no info here
blurs = ""
elseif u >= H then -- only show infinity blurs
tempn = ndof+lens_0
if tempn > 1000 then
tempn = myround(tempn/1000,2).."m"
else
tempn = myround(tempn/10,0).."cm"
end
blurs = ">H:"..tempn..":A["..myround(infinity_blur,0).."/"..myround(diff_blur,0).."/"..myround(total_blur,0).."]"
else
if DOFIS_Menu.submenu["Display Options"].value == "R-DoFs" then
tempn = u-ndof
tempf = fdof-u
if mag_u > mag_t then
blurs = "["..myround(tempn,1).."mm]M["..myround(tempf,1).."mm]"
elseif tempf > 1000 then
blurs = "["..myround(tempn/1000,2).."m]R["..myround(tempf/1000,2).."m]"
elseif tempn < 100 then
blurs = "["..myround(tempn).."mm]R["..myround(tempf).."mm]"
else
blurs = "["..myround(tempn/10,0).."cm]R["..myround(tempf/10,0).."cm]"
end
else
tempn = ndof+lens_0
tempf = fdof+lens_0
if tempf > 1000 then
blurs = "["..myround(tempn/1000,2).."m]A["..myround(tempf/1000,2).."m]"
else
blurs = "["..myround(tempn/10,0).."cm]A["..myround(tempf/10,0).."cm]"
end
end
end
if no_good and (mag_u < mag_t) then blurs = "N 2 high" end
end
if DOFIS_Menu.submenu["Traffic Lights"].value ~= COFF and lv.running and lv.overlays == 2 and not menu.visible then
local c = 0
if image_taken and not no_good then
if (x == last_image_x) then
c = COLOR.YELLOW
display.circle(250,240,70,c,c)
c=overlap_blur()
if c ~= -1 then
display.print(myround(c,0),250-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.YELLOW)
end
elseif (ndof > last_fdof) or (fdof < last_ndof) then
c = COLOR.RED
display.circle(250,240,70,c,c)
c=overlap_blur()
if c ~= -1 then
display.print(myround(c,0),250-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.WHITE,COLOR.RED)
end
else
c = COLOR.GREEN1
display.circle(250,240,70,c,c)
c=overlap_blur()
if c ~= -1 then
display.print(myround(c,0),250-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.GREEN1)
end
end
if u < H then
c = COLOR.RED
display.circle(470,240,70,c,c)
c = myround(infinity_blur,0)
display.print(myround(c,0),470-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.WHITE,COLOR.RED)
elseif u < 3*H then
c = COLOR.YELLOW
display.circle(470,240,70,c,c)
c = myround(infinity_blur,0)
display.print(myround(c,0),470-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.YELLOW)
elseif u < infinity then
c = COLOR.GREEN1
display.circle(470,240,70,c,c)
c = myround(infinity_blur,0)
display.print(myround(c,0),470-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.GREEN1)
else
c = COLOR.BLACK
display.circle(470,240,70,c,c)
end
elseif DOFIS_Menu.submenu["Traffic Lights"].value ~= COFF and lv.running and lv.overlays == 2 and not menu.visible then
if DOFIS_Menu.submenu["Traffic Lights"].value ~= "Mode 1" then
if not cleaned then
display.circle(250,240,70,COLOR.TRANSPARENT,COLOR.TRANSPARENT)
display.circle(470,240,70,COLOR.TRANSPARENT,COLOR.TRANSPARENT)
cleaned = true
end
else
if u < H then
c = COLOR.RED
display.circle(470,240,70,c,c)
c = myround(infinity_blur,0)
display.print(myround(c,0),470-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.WHITE,COLOR.RED)
elseif u < 3*H then
c = COLOR.YELLOW
display.circle(470,240,70,c,c)
c = myround(infinity_blur,0)
display.print(myround(c,0),470-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.YELLOW)
elseif u < infinity then
c = COLOR.GREEN1
display.circle(470,240,70,c,c)
c = myround(infinity_blur,0)
display.print(myround(c,0),470-FONT.LARGE:width(myround(c,0))/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.GREEN1)
else
c = COLOR.BLACK
display.circle(470,240,70,c,c)
end
c = myround(lp_data,0).."%"
display.circle(250,240,70,COLOR.gray(lp_data),COLOR.gray(lp_data))
display.print(c,250-FONT.LARGE:width(c)/2,240-FONT.LARGE.height/2,FONT.LARGE,COLOR.BLACK,COLOR.gray(lp_data))
end
end
end
end
function check_auto_bracket()
event.shoot_task = nil -- switch off shot_task event handler while auto bracketing
auto_bracket = false
local xx = lens.focus_distance
local uu = x2u(xx)
local start_xx = xx
local focus_error = false
local exit_now = false
local diff_ok = true
if diff then -- calculate defocus blur
if diff_blur < ML_blur then
local defocus_blur = math.sqrt(ML_blur*ML_blur - diff_blur_inf*diff_blur_inf) -- use diffraction value at infinity, ie mag = 0
no_good = false
else
no_good = true -- diffraction too high
diff_ok = false
end
end
if lens_model_ok and lens.af and direction ~= 0 and diff_ok then -- carry out focus bracketing
local h = (1000*f*f)/(a*defocus_blur)
local H = h + f -- H as measured from the front principal
if DOFIS_Menu.submenu["Auto Bracketing"].value == "Full" and uu < H then
menu.set("Shoot","Advanced Bracket",0) -- Can't use this mode with advanced ML backeting
current_fdof = uu+((uu-p)*(uu-f))/(h-(uu-f)) -- as impacted by the pupil mag
local tv = camera.shutter.apex
if DOFIS_Menu.submenu["Infinity bracket?"].value ~= 0 then -- adjust shutter to account for the Infinity bracket request
camera.shutter.apex = tv - DOFIS_Menu.submenu["Infinity bracket?"].value
end
sleep(DOFIS_Menu.submenu["Auto delay"].value)
Bookends()
my_shoot()
repeat
while not exit_now
do
repeat msleep(10) until lv.running
lens.focus(direction,2) -- move with medium steps towards the next focus position
repeat msleep(10) until lv.running
xx = lens.focus_distance
uu = x2u(xx)
current_ndof = uu-((uu-f)*(uu-p))/(h+(uu-f)) -- as impacted by the pupil mag extension, p
if current_ndof > current_fdof or xx > infinity then exit_now = true end
end
exit_now = false
while not exit_now
do
repeat msleep(10) until lv.running
lens.focus(-direction,1) -- move back in small steps to achieve a positive overlap
repeat msleep(10) until lv.running
xx = lens.focus_distance
uu = x2u(xx)
if xx <= start_xx then -- error
focus_error = true
exit_now = true
beep()
end
current_ndof = uu-((uu-f)*(uu-p))/(h+(uu-f)) -- as impacted by the pupil mag extension, p
if current_ndof <= current_fdof then exit_now = true end
end
exit_now = false
my_shoot()
current_fdof = uu+((uu-p)*(uu-f))/(h-(uu-f)) -- as impacted by the pupil mag
start_xx = xx
until (uu >= H/3) or focus_error
repeat msleep(10) until lv.running
uu = x2u(lens.focus_distance)
while uu < H do
lens.focus(direction,1) -- note using the smallest steps now as running out of Canon steps
uu = x2u(lens.focus_distance)
end
lens.focus(-direction,1)
my_shoot() -- take a shot at H
repeat msleep(10) until lv.running
while uu < 4*H do
lens.focus(direction,1) -- note using the smallest steps now as running out of Canon steps
uu = x2u(lens.focus_distance)
end
lens.focus(-direction,1)
my_shoot() -- take an infinity shot at 4*H, ie a defocus blur of overlap CoC/4
repeat msleep(10) until lv.running
if DOFIS_Menu.submenu["Infinity bracket?"].value ~= 0 then -- do a Infinity bracket shot at 4*H
local current_shutter = camera.shutter.apex
camera.shutter.apex = current_shutter + DOFIS_Menu.submenu["Infinity bracket?"].value
my_shoot()
camera.shutter.apex = current_shutter
end
Bookends()
camera.shutter.apex = tv -- reset shutter
repeat msleep(10) until lv.running
elseif DOFIS_Menu.submenu["Auto Bracketing"].value == "Semi(H)" then -- go to H
while x2u(lens.focus_distance) < H do lens.focus(direction,2) end
while x2u(lens.focus_distance) > H do lens.focus(-direction,1) end
while x2u(lens.focus_distance) < H do lens.focus(direction,1) end
elseif DOFIS_Menu.submenu["Auto Bracketing"].value == "Semi(dx)" then -- use semi auto bracketing
uu = x2u(xx)
if uu >= H then
current_fdof = infinity
else
current_fdof = uu+((uu-p)*(uu-f))/(h-(uu-f)) -- as impacted by the pupil mag
end
exit_now = false
if uu < H and current_fdof < infinity then
while not exit_now
do
repeat msleep(10) until lv.running
lens.focus(direction,2) -- move with medium steps towards the next focus position
repeat msleep(10) until lv.running
xx = lens.focus_distance
uu = x2u(xx)
current_ndof = uu-((uu-f)*(uu-p))/(h+(uu-f)) -- as impacted by the pupil mag
if current_ndof > current_fdof or xx >= infinity then exit_now = true end
end
exit_now = false
while not exit_now
do
repeat msleep(10) until lv.running
lens.focus(-direction,1) -- move back in small steps to achieve a positive overlap
repeat msleep(10) until lv.running
xx = lens.focus_distance
uu = x2u(xx)
current_ndof = uu-((uu-f)*(uu-p))/(h+(uu-f)) -- as impacted by the pupil mag
if xx <= start_xx then
exit_now = true
end
if current_ndof <= current_fdof then exit_now = true end
end
end
end
end
refresh = true
event.shoot_task = check_stuff
end
function update_nonLV()
local c = COLOR.BLACK
local font_s = FONT.LARGE
local focus = x
local text = ""
local xp = 22
local yp = 0
local w = 720 - 2*xp
local d = FONT.LARGE.height + 4
local defocus_b = myround(defocus_blur,0)
local diff_check = ""
if lens_model_ok then
if diff then diff_check = "S+" else diff_check = "S-" end
else
if diff then diff_check = "T+" else diff_check = "T-" end
end
if camera.mode ~= MODE.M then
display.print("SWITCH TO M MODE",xp+w/2-font_s:width("SWITCH TO M MODE")/2,yp+2,font_s,COLOR.WHITE,COLOR.BLACK)
return
end
if focus > infinity then
text = "oo/["..diff_check..defocus_b.."]"..base_blurs.."um"
elseif focus > 1000 then
text = myround(focus/1000,2).."m/["..diff_check..defocus_b.."]"..base_blurs.."um"
else
text = myround(focus/10,0).."cm/["..diff_check..defocus_b.."]"..base_blurs.."um"
end
if focus >= infinity then
--
elseif (defocus_blur-infinity_blur) <= defocus_blur/10 and u > H then
text = text.."/=H"
elseif u > H then
text = text.."/>H"
else
text = text.."/#"..myround(num,0)
end
local pos = font_s:width(text)/2
local cb = COLOR.GRAY
if image_taken and not no_good then
if (focus == last_image_x) then
c = COLOR.YELLOW
display.rect(xp,yp,w,d,cb,c)
display.print(text,xp+w/2-pos,yp+2,font_s,COLOR.BLACK,c)
elseif (ndof > last_fdof) or (fdof < last_ndof) then
c = COLOR.RED
display.rect(xp,yp,w,d,cb,c)
display.print(text,xp+w/2-pos,yp+2,font_s,COLOR.WHITE,c)
else
c = COLOR.GREEN1
display.rect(xp,yp,w,d,cb,c)
display.print(text,xp+w/2-pos,yp+2,font_s,COLOR.BLACK,c)
end
elseif not no_good then
c = COLOR.BLACK
display.rect(xp,yp,w,d,cb,c)
display.print(text,xp+w/2-font_s:width(text)/2,yp+2,font_s,COLOR.WHITE,c)
else
display.print("Can't use DOFIS: Check Lens",xp+w/2-font_s:width("Can't use DOFIS: Check Lens")/2,yp+2,font_s,COLOR.WHITE,COLOR.BLACK)
end
if menu.get("Expo","Dual ISO",0) == 1 then display.print("DI",495,105,FONT.MED,COLOR.YELLOW,COLOR.BLACK) end
if menu.get("Shoot","Silent Picture",0) == 1 then display.print("SP",155,105,FONT.MED,COLOR.YELLOW,COLOR.BLACK) end
display.print(lens.focal_length.."mm",335,105,FONT.MED,COLOR.YELLOW,COLOR.BLACK)
end
function check_stuff()
if (lens.focus_distance==0 or camera.aperture.value==0 or lens.focal_length==0) and lens.focus_distance ~= last_x then
no_good = true
if (camera.gui.mode == 0 and not lv.running and DOFIS_Menu.submenu["Focus Bracketing"].value == CON) then
display.draw(update_nonLV)
end
my_display("Can't use DOFIS",2,0,0)
return
else
no_good = false
end
if DOFIS_Menu.submenu["On/Off"].value == COFF then return true end
if auto_bracket then check_auto_bracket() end
if (last_x ~= lens.focus_distance or menu.visible or camera.gui.mode == 0 or update_now) then
update()
if (camera.gui.mode == 0 and not lv.running and DOFIS_Menu.submenu["Focus Bracketing"].value == CON) then
display.draw(update_nonLV)
end
update_now = false
end
return true
end
function check_keys(kk)
if kk == KEY.UNPRESS_FULLSHUTTER then
update_now = true
last_image_x = x
last_ndof = ndof
last_fdof = fdof
image_taken = true
last_I_f = f
last_u = u
return true
end
if kk == button and DOFIS_Menu.submenu["Auto Bracketing"].value ~= COFF then
auto_bracket = true
return false
else
auto_bracket = false
return true
end
end
DOFIS_Menu = menu.new
{
parent = "Focus",
name = "DOFIS",
help = "Make sure camera, eg FF or APS-C, is set correctly in the script",
help2 = "Shows DoFs (abs or rel) + defocus, diffraction & total inf blurs. Plus focus bracketing",
submenu =
{
{name = "On/Off",
choices = {CON,COFF},
},
{name = "Display Options",
choices = choices1,
help = "Relative DoFs or Blurs in DOFIS window",
},
{name = "Focus Bracketing",
choices = {CON,COFF},
help = "Switches the focus bracketing feedback on or off",
},
{name = "Auto Bracketing",
choices = choices3,
help = "Full = move (n(x to dx) to 4H) + shoot, Semi = move (x to dx or x to H)",
help2 = "ML AB will be switched off in full mode",
rinfo = function(this)
if this.value == "OFF" then
return ""
else
if this.value == "Full" and num <= 20 then
this.help2 = "Number of focus brackets"
return "#"..myround(num)
elseif this.value == "Full" and num > 20 then
this.help2 = "Number of focus brackets"
return ">20"
else
this.help2 = "Full = x to H, Semi = x to ?"
return button_t
end
end
end,
},
{name = "Diffraction Aware",
choices = {CON,COFF},
update = function(this)
if this.value == "ON" then
diff = true
else
diff = false
end
end,
help = "Switches diffraction aware on/off for DoFs",
},
{name = "lp/mm Feedback",
choices = {CON,COFF},
help = "Switches diffraction impacted IQ feedback on/off",
help2 = "Feedback is shown as a % of that at infinity and a wide open aperature",
},
{name = "Traffic Lights",
choices = choices4,
help = "Switches traffic light feedback on/off",
help2 = "Shows overlap and infinity blur status",
},
{name = "IR Freq",
choices = {CON,COFF},
help = "Switches diffraction blur to IR frequency",
help2 = "Off covers visible and full spectrum capture",
},
{name = "Infinity bracket?",
min = 0,
max = 6,
unit = UNIT.DEC,
rinfo = function(this)
if this.value == 0 then
return "OFF"
else
return "Ev"
end
end,
help = "Take an exposure bracket (2-6Ev) at 4xH: ETTR as the base exposure",
help2 = "Can only be used when auto focus bracketing",
},
{name = "Auto delay",
min = 0,
max = 2,
unit = UNIT.DEC,
help = "Only used when auto focus bracketing, at the start of the bracketing sequence",
rinfo = function(this) return "Sec" end,
},
{
name = "Bookends?",
help = "Places an underexposed frame at start and end of the auto bracket set",
choices = {CON,COFF},
},
{name = "Lens Model",
choices = choices2,
help = "Switch between lens models",
help2 = "OK means registered lens found. Not Ok meand will use thin lens model",
rinfo = function(this)
if DOFIS_Menu.submenu["Lens Model"].value == "Auto" then
if lens_model_ok then
return "OK"
else
return "Not OK"
end
end
return ""
end,
},
{name = "Mag Model",
min = 5,
max = 10,
unit = UNIT.DEC,
value = 5,
rinfo = function(this)
mag_t = this.value/10
return myround(mag_t,1)
end,
help = "Mag to transition to macro model DoF",
help2 = "Mag shown on right",
},
{name = "Hinge Calculator",
min = 0,
max = 85,
unit = UNIT.DEC,
value = 1,
rinfo = function(this)
if this.value ~= 0 then
return "J="..myround(f/math.sin(0.01745329251*this.value/10)/10,0).."cm"
else
return "J=Inf"
end
end,
help = "Tilt angle in 1/10 degrees",
help2 = "Converts to Hinge distance from lens FP",
},
{name = "Show TS Info",
choices = {CON,COFF},
update = function(this)
if this.value == "ON" then
diff = true
else
diff = false
end
end,
help = "Show J & PoSF angle from vertical in degrees",
help2 = "Make sure lens tilt is set correctly in DOFIS menu",
},
{
name = "Additional Info",
choices = more,
rinfo = function(this)
help_txt = ""
if this.value == "LT" then
temp = myround(lens_t,0).."mm"
help_txt = "Lens thickness. Note: can be + or -"
elseif this.value == "LE" then
temp = myround(lens_e,2).."mm"
help_txt = "Focus lens extension. Note: zero at infinity"
elseif this.value == "FP" then
temp = myround(lens_0,0).."mm"
help_txt = "Front principal position relative to sensor (J zero for TS-E)"
elseif this.value == "M@x" then
temp = myround(mag_u,3)
help_txt = "Magnification at the current focus"
elseif this.value == "LN" then
temp = "LN: "..lens.name
my_display(temp,1,5,10)
temp = ""
help_txt = "Lens name for registering lens in DOFIS"
elseif this.value == "Focus" then
if x > infinity then
temp = "oo"
elseif x < 1000 then
temp = myround(x/10,0).."cm"
else
temp = myround(x/1000,2).."m"
end
help_txt = "From Canon: let's hope it's good enough ;-)"
elseif this.value == "Defocus" then
temp = myround(defocus_blur,0).."um"
help_txt = "ML set (overlap) defocus blur, in microns"
elseif this.value == "Diff" then
temp = myround(diff_blur,0).."um"
help_txt = "Focus sensitive diffraction, in microns"
elseif this.value == "HF" then
if (h+f+lens_0) > 1000 then
temp = myround((h+f+lens_0)/1000,2).."m"
else
temp = myround((h+f+lens_0)/10,0).."cm"
end
help_txt = "Hyperfocal from the sensor plane"
elseif this.value == "#B" then
temp = "#"..myround(num)
help_txt = "Number of focus bracket to reach the hyperfocal"
elseif this.value == "H-AoV" then
temp = myround(114.591*math.atan(sensor_h/(2*f*(1+mag_u/p_mag))),1)
help_txt = "Horizontal/landscape AoV in degrees"
elseif this.value == "V-AoV" then
temp = myround(114.591*math.atan(sensor_v/(2*f*(1+mag_u/p_mag))),1)
help_txt = "Vertical/portrait AoV in degrees"
elseif this.value == "D-AoV" then
temp = math.sqrt(sensor_h*sensor_h + sensor_v*sensor_v)
temp = myround(114.591*math.atan(temp/(2*f*(1+mag_u/p_mag))),1)
help_txt = "Diagonal AoV in degrees"
elseif this.value == "P_mag" then
temp = myround(p_mag,1)
help_txt = "Pupillary magnification"
elseif this.value == "Pano" then
temp = exit_pupil.."mm"
help_txt = "Pano nodal postion from sensor (exit pupil)"
elseif this.value == "Ratio" then
temp = myround(ratio,2)
help_txt = "Estimate of DoF ratio (near/far)"
elseif this.value == "lp" then
temp = myround(lp,0)
help_txt = "Diffraction impacted resolution in lp/mm"
end
return temp
end,
update = function(this)
this.help2 = help_txt
end,
help = "Toggle through additional info",
help2 = ""
}
}
}
lv.info
{
name = "DoF Info",
value = "",
priority = 100,
update = function(this)
this.background = COLOR.TRANSPARENT_BLACK
this.foreground = COLOR.WHITE
if DOFIS_Menu.submenu["On/Off"].value == COFF then
this.value = ""
else
this.value = blurs
end
end
}
lv.info
{
name = "more info",
priority = 100,
value = "",
update = function(this)
if DOFIS_Menu.submenu["On/Off"].value == CON then
this.background = COLOR.TRANSPARENT_BLACK
this.foreground = COLOR.WHITE
if DOFIS_Menu.submenu["Focus Bracketing"].value == CON then
if image_taken then
if (lens.focus_distance == last_image_x) then
this.background = COLOR.YELLOW
this.foreground = COLOR.BLACK
elseif (ndof > last_fdof) or (fdof < last_ndof) then
this.background = COLOR.RED
this.foreground = COLOR.WHITE
else
this.background = COLOR.GREEN1
this.foreground = COLOR.BLACK
end
end
end
if lens_model_ok == nil then
this.value = "??"
elseif lens_model_ok then
if diff then this.value = "S+" else this.value = "S-" end
else
if diff then this.value = "T+" else this.value = "T-" end
end
if x > infinity then
this.value = this.value.."oo"
elseif mag_u > mag_t then
this.value = this.value.."m"..myround(mag_u,2)
elseif (defocus_blur-infinity_blur) <= defocus_blur/10 and u > H then
this.value = this.value.."=H"
elseif u > H then
this.value = this.value..">H"
else
this.value = this.value.."#"..myround(num,0)
end
else
this.value = ""
end
end
}
lv.info
{
name = "Diff info",
priority = 100,
value = "",
update = function(this)
if DOFIS_Menu.submenu["On/Off"].value == CON and DOFIS_Menu.submenu["lp/mm Feedback"].value == CON then
this.background = COLOR.GREEN1
this.foreground = COLOR.BLACK
if (wave*a*(1+mag_u/p_mag))/0.38 > p_factor*sensor_p then
this.background = COLOR.RED
this.foreground = COLOR.YELLOW
end
this.value = myround(lp_data,0).."%"
else
this.value = ""
end
end
}
lv.info
{
name = "PoSF info",
priority = 100,
value = "",
update = function(this)
if DOFIS_Menu.submenu["On/Off"].value == CON and DOFIS_Menu.submenu["Show TS Info"].value == CON and DOFIS_Menu.submenu["Hinge Calculator"].value ~= 0 then
this.background = COLOR.TRANSPARENT_BLACK
this.foreground = COLOR.WHITE
this.value = myround(J/10,0).."cm:"..myround(a_posf,0)
else
this.value = ""
end
end
}
event.shoot_task = check_stuff
event.keypress = check_keys
config.create_from_menu(DOFIS_Menu) -- keep a track of the script's menu state at camera close
console.hide()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment