Thursday, January 3, 2019

Landscape Focus Bracket Script for the G7X

In this post I'll be talking about my latest 'toy', the Canon Powershot G7X and in particular focus stacking with it.

As we know, macro focus stacking is rather 'easy' to undertake, albeit rather tedious. This is because the near and far depths if field (DoFs), ether side of the point of focus, are virtually symmetrical but very small. 

As soon, however, as we move away from the macro end, the near and far DoFs become progressively non-symmetric, until we reach the hyperfocal point (H), where the near DoF is, of course, H/2 and the far is at 'infinity'. Also the defocus blur field changes through the scene; being zero microns at the point of focus and the 'circle of confusion' set blur at the near and far DoFs.

However, it's relatively simple to estimate (sic) the bracketing that is required. That is, if I'm focused at Sn, where do I focus next (Sn+1) to ensure that my current far DoF is the same as my next near DoF, as illustrated here:


If we didn't explicitly calculate the correct Sn+1 focus position, and, say, only assume a fixed refocus delta, as we do in macro photography, we would end up with 'focus gaps', like this:


As it is near impossible to guarantee that the refocused position is a perfect match, we usually use an overlap 'insurance' to cover ourselves, like this:

With macro focus bracketing, which is usually indoors, we can either make use of rails or even try and move the lens in-camera. But for landscape photography, rails will not really work; plus we also we need to calculate the next focus position.

To date I've tried to solve the in-camera solution by Lua scripting on my EOS 5D3, using Magic Lantern. But I've now given up with that approach, as ML can not (yet) explicitly control the lens, ie drive it to an explicit position. Instead, on my EOS cameras I now use the DoF Bar to give me the required feedback, so I may manually adjust focus to the correct position.

With my 'new' G7X I'm pleased to report that everything just got brighter, as the CHDK environment does allow me to explicitly drive the lens; albeit with a little, but acceptable,  'jitter'.

The script below (a Lua CHDK script I've written that I call Landscape Bracketing) provides three user variables. The first is a simple delay to allow you to trigger the script on the tripod: the default is 3 seconds. The second input is whether you are requesting the script to create a start and end (dark) bookend frame, which helps identify the bracket set in post: the default is yes. The last input is the overlap logic that the script uses: the default is none, ie each bracket will link to the next using the CHDK CoC value. The other two options are 'some' and 'more'. Some will use a overap blur of CoC/1.25 and 'more' will use one of CoC/1.5.

In general the script captures the bracket set from near to just short of H. It then takes two more images: one at H and one at 2H.

As an example, here is a 7 bracket set (screen capture from Lightroom) that the script captured of my test scene:


Here is the near (1st image) showing the narrow DoF at that point:


Here is the last (7th Image), taken at 2*H, which insures our infinity focus quality


Here is the focus stacked image after a round trip to Helicon Focus, showing there are no focus gaps:


Finally, here is the script:
--[[
@title Landscape Bracketing
Release 0.9 - Tested on a G7X
@chdk_version 1.5

@param c Script Delay (s)
    @default c 3
    @range   c 0 5

@param b Bookends?
    @default b 0
    @values  b Yes No

@param g Overlap?
    @default g 0
    @values  g None Some More
--]]

set_mf(1)
sleep(c*1000)
dof = get_dofinfo()
base_h = dof.hyp_dist
h = base_h -- used to ensure overlay or some or more

if g == 1 then
    h = h*5/4
elseif g == 2 then
    h = h*3/2
end

x = get_focus()
x_start = x
s=get_tv96()

function refocus(x)
local dis = 0
set_focus(x)
repeat
    dis = get_focus()
    sleep(100) -- seems to work on G7X, may need increasing on other cameras
until dis == get_focus()
end

if x_start < base_h then -- ok to bracket
    if b == 0 then -- create bookend
        set_tv96(960)
        shoot()
        set_tv96(s)
    end

    refocus(x_start) -- explicit refocus, just in case
    while  (x <= h/3) -- capture brackets less than at the hyperfocal
    do
        shoot()
        x = x*h/(h - 2*x) -- next focus position
        if x <= 0 then break end -- as now past h
        refocus(x)    
        dof = get_dofinfo()
    end

    refocus(base_h)
    shoot()

    refocus(2*base_h)
    shoot()

    if b == 0 then -- create bookend
        set_tv96(960)
        shoot()
        set_tv96(s)
    end
else
    print("...beyond H")
end

refocus(x_start)  

print("...done")

exit_alt()

I must say, I'm impressed with the power of the CHDK Lua environment, which provides more functionality than Magic Lantern's Lua; but to be fair we are comparing apples and pears, ie a full frame DSLR to a point and shoot camera. Nevertheless, the G7X represents a great little travel companion for my pocket.

No comments:

Post a Comment