This page describes how you can use CHDK to capture photos and videos of birds.
Pose Your Birds[]
Bird feeders are a great way to attract birds to your camera.
- Build a feeder that forces the bird to perch at a set distance from the camera.
- Site the feeder in sunlight but watch for the camera casting a shadow.
- Leave the feeder out for a while without the camera so they get used to it.
- Use a bird cutout on a clothes peg to frame your shot.
Here is a very simple feeder made from coat hanger wire and an old bottle top.
Lazy CHDK Birding[]
This is a very easy way to get great bird photos from your CHDK camera.
- Set up your feeder as above with your camera near it on a tripod.
- Turn on Mute in your Canon menu to prevent scaring the birds.
- Lock the subject distance using CHDK menu>Enhanced Photo Operations>Override Subject Distance.
- Load script "interval.lua" and set interval to 1 second.
- Start script running.
CHDK will start taking photos which you can sort through to find any goodies like these.
DaveCam Script[]
DaveCam continuously records video clips of bird visits to your feeder station. It deletes any clips without visits as it goes along. It also produces an AviSynth file which trims the remaining videos and combines them into a single clip. This clip can be played in media players or edited in VirtualDub.
The forum thread for the script is here and the parameters are :
Subject distance (cm) - the camera focus is locked at this distance.
Video FPS - the frame rate of your video.
Trigger threshold - change in scene brightnessthat will trigger the start of an event.
Release threshold - the event will be judged to have ended when the scene brightness returns to within this value.
Grid options - standard motion detection grid options.
Detection lag 1/10s - time taken for camera to recognise an event (my A810 value is 14).
Interval between events (s) - time to wait for a subsequent event before closing the clip. Allows for several closely spaced events on each clip.
Longest event (s) - maximum event duration. Used to prevent false events keeping clip open.
Frames before event - adds frames to AviSynth file before event occurs. This lets you see the bird landing.
Frames after event - similar to above.
Clip length (s) - clip will be deleted if no events occur during this time.
You will need to install AviSynth on your PC to view the combined clip. You will also need to install the QTSource plugin.
DaveCam was written for an A810 which records Quicktime (.mov) videos and has a dedicated video button.
--[[
@title DaveCam by Davo
@param d Subject distance (cm)
@default d 20
@param f Video FPS
@default f 30
@param t Trigger threshold
@default t 30
@param n Release threshold
@default n 10
@param g Grid options
@default g 0
@range g 0 3
@param l Detection lag 1/10s
@default l 14
@param v Interval between events (s)
@default v 30
@param z Longest event (s)
@default z 120
@param b Frames before event
@default b 30
@param a Frames after event
@default a 30
@param c Clip length (s)
@default c 300
--]]
-- md variables
ma = 8 -- columns
mb = 6 -- rows
mc = 1 -- measure mode
mf = t -- trigger threshold for m
mg = g -- draw grid
mh = 0 -- not used in LUA
mi = 0 -- region masking mode
mj = 0 -- first column
mk = 0 -- first row
ml = 0 -- last column
mm = 0 -- last row
mn = 0 -- optional parameters
mo = 1 -- pixel step
mp = 0 -- trigger Delay (mSec)
noise = n -- background noise
timeout = z * 1000
interval = v * 1000
-- Edit variables
frames_before = b
frames_after = a
max_clip = c * 1000
fps = f
md_lag = l * 100 -- camera md lag time (ms)
-- Other variables
md_original = {}
md_trigger = {}
md_current = {}
clips = 0
filename = ""
subject_distance = 10 * d
events = 0
tot_events = 0
avs_text = ""
timed_out = false
-- Functions
function fill_md_array(name)
for row = 1, mb do
for col = 1, ma do
name[(row - 1) * ma + col] = md_get_cell_val(col,row)
end -- for col
end -- for row
end -- fill_array()
function compare_scenes(original, current, sensitivity)
for row = 1, mb do
for col = 1, ma do
difference = math.abs(original[(row - 1) * ma + col] - current[(row - 1) * ma + col])
if difference >= sensitivity then
return false
end -- if
end -- for col
end -- for row
return true
end -- compare_scenes()
function get_current_folder()
dir = os.listdir("A/DCIM", false)
count = table.getn(dir)
return(dir[count])
end
function get_last_clip_name()
dir = os.listdir("A/DCIM/" .. get_current_folder(), false)
count = table.getn(dir)
return(dir[count])
end -- end of get_last_clip_name()
function update_avs_file()
last_clip = get_last_clip_name()
if clips == 1 then
filename = "A/DCIM/" .. get_current_folder() .. "/" .. string.sub(last_clip, 1, 8) .. ".AVS"
end
avs_text = "trim(QTInput(\"" .. last_clip .. "\", audio = 1)," .. f_start[1] .. "," .. f_end[1] .. ")\r\n"
if events > 1 then
for i = 2, events do
if f_start[i] < f_end[i-1] then -- remove overlaps
f_start[i] = f_end[i-1] + 1
end
avs_text = avs_text .. "\\++ " .. "trim(QTInput(\"" .. last_clip .. "\", audio = 1)," .. f_start[i] .. "," .. f_end[i] .. ")\r\n"
end
end
if clips == 1 then
tfile = io.open(filename, "w")
tfile:write(avs_text)
tfile:close()
else
avs_text = "\\++ " .. avs_text
tfile = io.open(filename, "a")
tfile:write(avs_text)
tfile:close()
end
print("Updated AVS file")
end
function restore()
--print(get_movie_status())
if get_movie_status() == 4 then
post_levent_to_ui('PressMovieButton')
end
print("User stopped script")
tfile = io.open(filename, "a")
tfile:write("Subtitle(\"DaveCam\", align=3)")
tfile:close()
end
repeat
avs_text = ""
finished = false
timed_out = false
events = 0
f_start = {}
f_end = {}
set_focus(subject_distance)
sleep(500)
press("shoot_half")
sleep(1000)
click("left")
sleep(1000)
release("shoot_half")
clip_start = get_tick_count()
press("video")
sleep(2000)
release("video")
-- Get original scene
zones = md_detect_motion( ma, mb, mc, 200, 100, 900, mg, mh, mi, mj, mk, ml, mm, mn, mo, mp)
fill_md_array(md_original)
repeat
if events == 0 then
zones = md_detect_motion( ma, mb, mc, max_clip, 100, mf, mg, mh, mi, mj, mk, ml, mm, mn, mo, mp)
else
zones = md_detect_motion( ma, mb, mc, interval, 100, mf, mg, mh, mi, mj, mk, ml, mm, mn, mo, mp)
end
if zones == 0 then
finished = true
else
events = events + 1
tot_events = tot_events + 1
print("Clip " .. clips + 1 .. ", event " .. events)
t_start = get_tick_count()
f_start[events] = ((fps * (t_start - clip_start - md_lag)) / 1000) - frames_before
repeat
zones = md_detect_motion( ma, mb, mc, 200, 100, 900, mg, mh, mi, mj, mk, ml, mm, mn, mo, mp)
fill_md_array(md_current)
if (get_tick_count() - t_start) > timeout then
timed_out = true
finished = true
end
until compare_scenes(md_original, md_current, noise) == true or timed_out == true
f_end[events] = ((fps * (get_tick_count() - clip_start - md_lag)) / 1000) + frames_after
end
until finished == true
if timed_out == true then
sleep(2000 + frames_after * 1000 / fps)
end
press("video")
sleep(2000)
release("video")
sleep(500)
-- Clip management
if events == 0 then
-- delete clip
clip_name = get_last_clip_name()
rubbish = "A/DCIM/" .. get_current_folder() .. "/" .. clip_name
os.remove(rubbish)
--print("Deleted " .. clip_name)
else
-- update AVS file
clips = clips + 1
update_avs_file()
end
until false