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