Skip to content

Instantly share code, notes, and snippets.

@pigeonhill pigeonhill/BRAK.lua
Last active Oct 18, 2019

Embed
What would you like to do?
Bracketeer
--[[
Bracketeer
This script dynamically displays the current infinity defocus blur, diffraction blur and the total blur at infinity, which is useful when focusing beyond the hyperfocal.
It also visually shows the overlap status to aid focus bracketing: green means a positive overlap and red a focus gap.
In addition a traffic light feedback gives you infinity focus info.
The width of the DoF visualisation is the smallest near DoFs (current position and last image taken) and the hyperfocal distance, which is useful when bracketing less than the hyperfocal.
If both current and last far DoFs are less than the hyperfocal, then the right hand edge of the bar will be the longest far DoF.
The ML CoC (total blur) is used as the overlap criterion: on a full frame it is recommended this be 20 microns.
A thin lens model is assumed, with the lens principal plane positioned at f(1+m) from the sensor, ie magnification is estimated from the TL model: so don't use the script for macro.
Release 0.72
May 2019
Garry George
photography.grayheron.net
--]]
require("config")
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
H = f + (1000*f*f)/(a*ML_blur)
last_H = H
ndof = (H*(x-f)-f*f)/(H+(x-f)-2*f)
fdof = (H*(x-f)-2*f*(x-f)+f*f)/(H-(x-f))
infinity_blur = 0
diff_blur = 0
total_blur = 0
blurs = ""
num = 0
last_f = f
last_x = x
last_a = a
last_ndof = ndof
last_fdof = fdof
last_lens_0 = 0
lens_0 = 0
dirty = true
finish = false
w_bar = 538
h_bar = 40
h_dof_bar = 17
last_pos_1 = 0
last_pos_2 = 0
pos_1 = 0
pos_2 = 0
total_dof = 0
card_count = dryos.shooting_card.file_number
CON = "ON"
COFF = "OFF"
switched_off = false
turned_off = false
zero_dof = 0
H_4_dof = 0
image_taken = false -- that is at least one has been taken
x_zero = 40
y_zero = -100
tl_x = 450
tl_y = 215
tl_r = 10
ok_2_go = true
fl_ok = true
freq = 0.550 -- 0.550 for visible band photography or 0.850 for IR (or another frequency to suit your sensor conversion)
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
function myround(nu,dp)
-- nu = a number, dp = decimal places required to show
-- returns number as a string
if dp == 0 then
nu = tostring(math.floor(nu))
else
nu = tostring(math.floor(nu)).."."..string.sub(tostring(math.floor(num*10^dp)),-dp,-1)
end
return nu
end
function box()
display.rect(x_zero+40, y_zero+150, 550, 135, COLOR.BLACK, COLOR.BLACK)
display.rect(x_zero+43, y_zero+153, 546, 129, COLOR.BLACK, COLOR.TRANSPARENT_BLACK)
end
function update()
if card_count ~= dryos.shooting_card.file_number then
last_f = lens.focal_length
last_x = lens.focus_distance
last_lens_0 = last_f*(1+(last_x-2*last_f-math.sqrt(last_x*last_x-4*last_x*last_f))/(2*last_f))
last_a = camera.aperture.value
last_H = f + (1000*f*f)/(a*ML_blur)
last_ndof = (last_H*(last_x-last_f)-last_f*last_f)/(last_H+(last_x-last_f)-2*last_f)
last_fdof = (last_H*(last_x-last_f)-2*last_f*(last_x-last_f)+last_f*last_f)/(last_H-(last_x-last_f))
if last_fdof <= 0 then last_fdof = last_H end
card_count = dryos.shooting_card.file_number
image_taken = true
end
f = lens.focal_length
x = lens.focus_distance
ML_blur = menu.get("DOF Settings","Circle of Confusion",0)
lens_0 = f*(1+(x-2*f-math.sqrt(x*x-4*x*f))/(2*f))
a = camera.aperture.value
H = f + (1000*f*f)/(a*ML_blur)
ndof = (H*(x-lens_0)-f*f)/(H+(x-last_lens_0)-2*f)
fdof = (H*(x-lens_0)-2*f*(x-lens_0)+f*f)/(H-(x-lens_0))
box()
display.print("Estimate "..tostring(num).." brackets",x_zero+50, y_zero+200,FONT.SANS_32,COLOR.TRANSPARENT_BLACK,COLOR.TRANSPARENT_BLACK)
num = 1 + math.ceil((1 + (H/(x-f)))/2)
if (x-f) > H then num = 1 end
infinity_blur = ML_blur*H/(x-f)
diff_blur = 2.44 * a * freq
total_blur = math.sqrt(infinity_blur*infinity_blur + diff_blur*diff_blur)
display.print("Def/Dif/Tot Blurs: "..blurs.." um" ,x_zero+50, y_zero+160,FONT.SANS_32,COLOR.TRANSPARENT_BLACK,COLOR.TRANSPARENT_BLACK)
blurs = myround(infinity_blur,0).."/"..myround(diff_blur,0).."/"..myround(total_blur,0)
display.print(blurs.." um",x_zero+50+FONT.SANS_32:width("Def/Dif/Tot Blurs: "), y_zero+160,FONT.SANS_32,COLOR.WHITE,COLOR.TRANSPARENT_BLACK)
display.print("Def/Dif/Tot Blurs: ",x_zero+50, y_zero+160,FONT.SANS_32,COLOR.WHITE,COLOR.TRANSPARENT_BLACK)
if num > 1 then
display.print("Estimate "..tostring(num).." brackets",x_zero+50, y_zero+200,FONT.SANS_32,COLOR.WHITE,COLOR.TRANSPARENT_BLACK)
else
display.print("Focus beyond H",x_zero+50, y_zero+200,FONT.SANS_32,COLOR.WHITE,COLOR.TRANSPARENT_BLACK)
end
-- display overlap bars
if f ~= last_f then
image_taken = false
last_f = lens.focal_length
fl_ok = false
else
fl_ok = true
end
H_4_dof = math.max(H,last_H)
if (last_fdof >= last_H) or (last_fdof <= 0) then last_fdof = H_4_dof end
if (fdof >= H) or (fdof <= 0) then fdof = H_4_dof end
if math.max(last_fdof, fdof) < H_4_dof then
H_4_dof = math.max(last_fdof, fdof)
display.rect(x_zero+46, y_zero+238, w_bar+1, h_bar, COLOR.ALMOST_WHITE, COLOR.ALMOST_WHITE)
else
display.rect(x_zero+46, y_zero+238, w_bar+1, h_bar, COLOR.WHITE, COLOR.WHITE)
end
zero_dof = math.min(last_ndof, ndof)
total_dof = H_4_dof - zero_dof
last_pos_1 = w_bar*(last_ndof - zero_dof)/total_dof
last_pos_2 = w_bar*(last_fdof - zero_dof)/total_dof
pos_1 = w_bar*(ndof - zero_dof)/total_dof
pos_2 = w_bar*(fdof - zero_dof)/total_dof
if (ndof > last_fdof) and image_taken and f == last_f then
display.rect(x_zero+47+last_pos_2, y_zero+240,pos_1-last_pos_2, 2*h_dof_bar+3, COLOR.RED, COLOR.RED)
elseif (fdof < last_ndof) and image_taken and f == last_f then
display.rect(x_zero+47+pos_2, y_zero+240, last_pos_1-pos_2, 2*h_dof_bar+3, COLOR.RED, COLOR.RED)
end
if x < infinity and f == last_f then
display.rect(x_zero+47+pos_1, y_zero+260, pos_2-pos_1, h_dof_bar, COLOR.DARK_GRAY, COLOR.DARK_GRAY)
end
if image_taken and x < infinity and fl_ok then
display.rect(x_zero+47+last_pos_1, y_zero+240, last_pos_2-last_pos_1, h_dof_bar, COLOR.GRAY, COLOR.GRAY)
elseif (not image_taken) and x < infinity and fl_ok then
display.rect(x_zero+47+pos_1, y_zero+240, pos_2-pos_1, h_dof_bar, COLOR.GRAY, COLOR.GRAY)
end
if (last_ndof < ndof) and (last_fdof > ndof) and image_taken and (last_x < infinity) and fl_ok then
display.rect(x_zero+47+pos_1, y_zero+240,last_pos_2-pos_1, 2*h_dof_bar+3, COLOR.GREEN1, COLOR.GREEN1)
elseif (last_ndof < fdof) and (last_fdof > fdof) and image_taken and (last_x < infinity) and fl_ok then
display.rect(x_zero+47+last_pos_1, y_zero+240,pos_2-last_pos_1, 2*h_dof_bar+3, COLOR.GREEN1, COLOR.GREEN1)
end
fl_ok = true
-- display traffic lights
if (x-f) < H then
display.circle(x_zero+tl_x,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.RED)
else
display.circle(x_zero+tl_x,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.BLACK)
end
if (x-f) >= H and (x-f) < 2*H then
display.circle(x_zero+tl_x+3*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.YELLOW)
else
display.circle(x_zero+tl_x+3*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.BLACK)
end
if (x-f) >= 2*H and (x-f) <= 4*H then
display.circle(x_zero+tl_x+6*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.GREEN1)
else
display.circle(x_zero+tl_x+6*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.BLACK)
end
if (x-f) > 4*H and x-f <= infinity then
display.circle(x_zero+tl_x+9*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.ORANGE)
elseif (x-f) > infinity then
display.circle(x_zero+tl_x+9*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.WHITE)
else
display.circle(x_zero+tl_x+9*tl_r,y_zero+tl_y,tl_r,COLOR.BLACK,COLOR.BLACK)
end
end
function check_stuff()
if (lv.overlays == 2 and not menu.visible) then
if (not switched_off) then
display.draw(update)
turned_off = false
elseif (not turned_off) then
display.clear()
turned_off = true
end
end
end
Bracketeer_Menu = menu.new
{
parent = "Focus",
name = "Bracketeer",
help = "Helps with infinity focusing & bracketing",
depends_on = DEPENDS_ON.LIVEVIEW,
choices = {CON,COFF},
update = function(this,delta)
if this.value == "ON" and ok_2_go then
switched_off = false
turned_off = false
else
this.value = "OFF"
switched_off = true
turned_off = false
end
end,
}
if (x==0 or a==0 or f==0) then
my_display("Can't use script",2,0,0)
ok_2_go = false
menu.set("Focus","Bracketeer","OFF")
return
end
event.shoot_task = check_stuff
config.create_from_menu(Bracketeer_Menu) -- keep a track of the script's menu state at camera close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.