Saturday, May 28, 2016

Another Update towards the Ultimate Bracketing In-camera Script

As readers of my blog are aware, I have been putting some effort into writing a bracketing script for (non-macro) focus bracketing.

In this post I offer my latest version for testing and feedback.

I have tried to make the script 'robust' and have built in some error checking (but still read the comments at the start of the script).

The script will automatically do the following things:

  • Take a set of focus brackets from near to infinity
  • Take a set of exposure brackets from the user set shadow end exposure (hint use ML's RAW spotmeter), to the ETTR position
  • Take exposure and focus bracket sets together
  • Take a set of exposure brackets, ie to simulate (in post) a longer ND time
  • Take any number of the same exposure brackets, eg for post processing for super-res or eliminating people
The usual caveats apply, ie I wrote the script for my use and to run on my 5D3 with my lenses. However, I'm confident it should run on any non-EOSM camera that runs the ML Lua module. 

Note the lens must report focus distance, ie have AF.

Finally, I really would welcome feedback from others, ie to help me in my efforts and to make the script even better.


UPDATED: 29th May 2016

--[[
Auto Bracketing Script
Auto focus bracketing with exposure bracketing
Plus the script does both time and frame bracketing, ie for LE situations
Release 0.95
This version has some error detection, but the user must be aware of certain things:
* Check the correct DoF diffraction options are set in ML, eg diffraction aware is on for best data
* Must use a lens that reports focus distance and DoF can be calculated
* Lens must be in AF mode
* Must be in manual mode
* Switch stabalisation off as you will not be hand holding ;-)
* Must be in LV mode: script won't run unless in LV
* ETTR ON: settings recommended are - no link to dual, SNRs to OFF, highlights very low or zero (your choice_, and not Always On or Auto Snap
* as this may 'confuse' the script
* You can switch focus bracketing on/off in the menu, ie use LE and/or exposure mode alone. The script handles the logic, ie if LE mode is being used,
* then you can't use focus or exposure bracketing
* Image review must not be set to HOLD in Canon menu
* Assumes lens is focused at nearest point to start a focus stack, ie script moves to infinity
* Script takes last bracket with focus point at infinity
* When exposure bracketing, set base exposure to capture shadows (the script will calculate the ETTR of the scene)
* If ETTR fails, then try adjusting ETTR set time to give your camera more time (or experiment with less)
* Final thought: photography should be relaxing, hence I personally don't mind that this script takes time to run. As it's
* running, look around you and enjoy nature and the moment :-)
*******************************************************************************************************
*                                                                                  *
* If you reference this script, please consider acknowledging me at http://photography.grayheron.net/ *
*                                                                                  *
*******************************************************************************************************
--]]

-- Declare a few variables for the script whick load when camera switced on
a1 = 0
b1 = 0
a2 = 0
b2 = 0
fp = 0
inf = 100000
base_tv = 0
delay = 0
factor = 0
num_brackets = 0
ETTR_tv = 0
base_tv = 0
user_tv = 0
LE_brackets = 0
running = false
no_tv_error = true
brackets_requested = false
flash = true

function reset()  -- tidy up at the end
    no_tv_error = true
    brackets_requested = false
    running = true
    display.notify_box("***FINISHED***",4000)
    beep (3, 200 , 1000)  -- notify end of script
    display.clear()
end

function my_shoot()
    if camera.shutter.apex < -4 then
        camera.bulb(camera.shutter.value)
        else
        camera.shoot(64, false)
    end
end

function check_bookend()
if keymenu.submenu["Bookends?"].value == "yes"
    then
        local tv = camera.shutter.ms
        local av = camera.aperture.value
        camera.shutter.ms = 1
        camera.aperture.apex = 9
        my_shoot()
        camera.shutter.ms = tv
        camera.aperture.value = av
    end
end

function find_ETTR()
-- Find the ETTR shutter value: make sure you have set ML ETTR values correctly
    local m = menu.get("Auto ETTR","Trigger mode")
    menu.set("Auto ETTR","Trigger mode",0) -- set ETTR menu to get an ETTR exposure
    local check_tv = 0
    repeat -- hang around until ETTR value has stabilised
        check_tv = camera.shutter.apex
        msleep (keymenu.submenu["ETTR Set Time?"].value*1000)  -- adjust this for your camera. The time delay allows the ML ETTR process to settle down
    until check_tv == camera.shutter.apex
    ETTR_tv = camera.shutter.apex
    menu.set("Auto ETTR","Trigger mode",m) -- reset to user ETTR menu setting
    if ETTR_tv < base_tv
    then -- base TV needs resetting
        display.notify_box("Warning Shadow Tv < ETTR Tv",4000)
        beep (3, 200 , 500)  -- warning message
        no_tv_error = false
    end
end

function calc_brackets()
    if no_tv_error then
        num_brackets = (ETTR_tv - base_tv)/factor
        num_brackets = math.floor(math.abs(num_brackets))
        if num_brackets <= 1 then num_brackets = 1 end
    end
end

function check_LE()
--    Check to see if user has requested LE bracketing of some kind
    if keymenu.submenu["LE mode?"].submenu["select"].value ~= 0
    then
    if keymenu.submenu["LE mode?"].submenu["Mode?"].value == "time"
    then
    LE_brackets = math.ceil(keymenu.submenu["LE mode?"].submenu["select"].value/camera.shutter.value)
    else
    LE_brackets = keymenu.submenu["LE mode?"].submenu["select"].value
    end
    brackets_requested = true
    end
end

function take_brackets()
    local i
    if LE_brackets == 0
    then
        if no_tv_error and brackets_requested then -- take exposure brackets
            camera.shutter.apex = ETTR_tv
            my_shoot()
            for i = 1, num_brackets , 1
            do
                camera.shutter.apex = camera.shutter.apex - factor
                my_shoot()
            end
            if camera.shutter.apex > base_tv
            then
                camera.shutter.apex = base_tv  
                my_shoot()
            end
        else -- take a single image
            camera.shutter.apex = base_tv
            my_shoot()
        end
    else -- take some LE bracketing images
        for i = 1, LE_brackets , 1
        do
            my_shoot()
        end
--        if no_tv_error then check_bookend() end
    end
end

function albs()
    if brackets_requested and keymenu.submenu["LE mode?"].submenu["select"].value == 0 then
        find_ETTR()
        calc_brackets() -- number of exposure brackets
    else
        msleep(delay)
    end
    if no_tv_error then check_bookend() end
--     Get DoF info & take first(only) exposure(s)
    a2 = lens.dof_near
    b2 = lens.dof_far
    if no_tv_error then take_brackets() end
-- take focus brackets to infinity if requested
    if keymenu.submenu["Focus bracketing?"].value == "yes"
    then -- do focus bracketing
    if lens.focus_distance < inf and no_tv_error then
        repeat
        a1=a2
        b1=b2
            repeat
                lens.focus(-1,1)
                b2 = lens.dof_far
                a2 = lens.dof_near
                fp = lens.focus_distance
            until a2 > b1 or fp >= inf
        lens.focus(1,1) -- move back one step for focus bracket overlap
        take_brackets()
        until fp >= inf
    end
    end
    if no_tv_error then check_bookend() end
    reset()  -- do some tiding up beforing exiting
end

function setup() -- does some checking before calling the main function
    menu.close()
    factor = tonumber(keymenu.submenu["Ev bracket delta per image"].value) -- set exposure bracketing offset (0, 1 or 2)
    if not lv.enabled then lv.start() end -- just in case
    if camera.model_short == "EOSM" then
        display.notify_box("Sorry doesn't work with the EOSM", 3000)
    else
        check_LE()
        if factor ~= 0 then brackets_requested = true else brackets_requested = false end
        if running -- then script has already been used since camera switched on
        then -- reset some stuff...just to be sure :-)
            no_tv_error = true
        end
        if lens.focus_distance ~= 0 and lens.af -- lens is OK
        then -- fit to run the main function
            base_tv = camera.shutter.apex
            albs()
        else  -- lens not OK!
            beep (3, 200 , 500)  -- warning message
            display.notify_box("Check your lens", 5000)
        end
    end
end

keymenu = menu.new
{
    parent = "Shoot", -- change this to position the script into other ML menu locations
    name = "Auto Bracketing",
    help = "Remember: LV, DoF & AF on",
    depends_on = DEPENDS_ON.LIVEVIEW,
    warning = function(this)
            if menu.get("Expo","Auto ETTR") == 0 then return "ETTR switched off" end
            end,
    submenu =
    {
        {
            name = "Run Script",
            help = "Does what it says after pressing SET",
            warning = function(this)
                flash = not flash
                if not lv.enabled then return "LV not enabled" end
            end,
            select = function(this)
            if menu.get("Expo","Auto ETTR") ~= 0 and lv.enabled then
            task.create(setup)
            end
            end,
        },
        {
            name = "Focus bracketing?",
            help = "Switches focus bracketing mode on/off",
            choices = {"no","yes"},
            update = function(this)
                if keymenu.submenu["LE mode?"].submenu["select"].value ~= 0 then
                menu.set("Auto Bracketing","Focus bracketing?",0)
                end
            end,
            warning = function(this)
                flash = not flash
                if keymenu.submenu["LE mode?"].submenu["select"].value ~= 0 then
                if flash then
                    return "LE mode on"
                    else
                    return ""
                end
                end
            end,
        },
        {
            name = "Ev bracket delta per image",
            help = "0 = no auto exposure bracketing",
            min = 0,
            max = 2,
            update = function(this)
                    if keymenu.submenu["LE mode?"].submenu["select"].value ~= 0 then
                    menu.set("Auto Bracketing","Ev bracket delta per image",0) end
            end,
            warning = function(this)
                if keymenu.submenu["LE mode?"].submenu["select"].value ~= 0 then
                if flash then
                    return "LE mode on"
                    else
                    return ""
                end
                end
            end
        },
        {
            name = "LE mode?",
            rinfo = function(this)
                if keymenu.submenu["LE mode?"].submenu["select"].value == 0
                    then return "off"
                    else return "active"
                end
            end,
            help = "In seconds or frames. Zero = no LE bracketing",
            halp2 = "LE or frame bracketing trumps all other bracketing modes",
           submenu =
            {
                {
                    name = "select",
                    min = 0,
                    max = 120,
                    rinfo = function(this)
                        if keymenu.submenu["LE mode?"].submenu["Mode?"].value == "time" then return "secs" else return "frames" end
                    end,
                    update = function(this)
                        if keymenu.submenu["LE mode?"].submenu["select"].value ~= 0 then
                        menu.set("Auto Bracketing","Ev bracket delta per image",0)
                        menu.set("Auto Bracketing","Focus bracketing?",0)
                        end
                    end,
                    warning = function(this)
                    if keymenu.submenu["LE mode?"].submenu["Mode?"].value == "time" then
                    LE_brackets = math.ceil(keymenu.submenu["LE mode?"].submenu["select"].value/camera.shutter.value)
                    if LE_brackets == 0 then
                    return "LE bracket taking switched off"
                    else
                    return "Note: "..tostring(LE_brackets).." brackets will be taken"
                    end
                    end
                    end
                },
                {
                    name = "Mode?",
                    choices = {"time","# frames"},
                    help = "In seconds or #frames. Zero = no LE bracketing",
                }
            }
        },
        {
            name = "Bookends?",
            help = "Places a dark frame at start and end of focus bracket set",
            choices = {"no","yes"},
        },
        {
            name = "Delay",
            help = "Delays script start by stated number of secs",
            choices = {"2","5","0"},
            update = function(this)
                delay = tonumber(keymenu.submenu["Delay"].value)*1000 -- delay in ms
            end,
        },
        {
            name = "ETTR Set Time?",
            help = "A camera specific variable in seconds",
            help2 = "Only change if ETTR fails to find a solution",
            min = 2,
            max = 8,
            value = 4 -- set this to time that works in your camera
        }
    }
}

No comments:

Post a Comment