Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine motion and timelapse #5

Closed
krist10an opened this issue Apr 2, 2015 · 17 comments
Closed

Combine motion and timelapse #5

krist10an opened this issue Apr 2, 2015 · 17 comments

Comments

@krist10an
Copy link

Thanks for the excellent program, worked right out of the box for me 👍

Would it be possible to combine motion detection and timelapse in order to create a timelapse movie that updates more often when there is motion? You can see what I had in mind here:
https://youtu.be/gyeTsl8ghcQ?t=3m

I like the effect, allthou the street in the video is a bit too busy to see the effect very clearly :-)

@pageauc
Copy link
Owner

pageauc commented Apr 2, 2015

Krist.

Looks like a great idea. Pi Timolo does have video when motion is detected
but I like the idea of increasing the rate once motion is detected. We
will be going back to Canada shortly so I might not get chance to do this
right away but definitely will implement and will let you know. Should just
need an additional function and boolean toggle. I suspect there will need
to be three (3) additional settings. One to turn the fast motion capture
setting on and off. Second for fast motion time between images and third
would be the duration after motion detected before going back to normal
motion detect mode.

Is this what you were thinking?.
Claude ..

On Thu, Apr 2, 2015 at 9:38 AM, krist10an notifications@github.com wrote:

Thanks for the excellent program, worked right out of the box for me [image:
👍]

Would it be possible to combine motion detection and timelapse in order to
create a timelapse movie that updates more often when there is motion? You
can see what I had in mind here:
https://youtu.be/gyeTsl8ghcQ?t=3m

I like the effect, allthou the street in the video is a bit too busy to
see the effect very clearly :-)


Reply to this email directly or view it on GitHub
#5.

See my YouTube Channel at http://www.youtube.com/user/pageaucp

@krist10an
Copy link
Author

Hi Claude,

Thanks, sounds like a nice way of doing it.

Come to think of it, it might be possible to get this effect by just set the output directory for the motion detection and timelapse in the same output folder and using the same name prefix? In that case you get extra images every time there is motion and when generating a movie it will look like it slows down when motion is present. In that case I guess the numbering might not work directly, but date/timestamps should.

@pageauc
Copy link
Owner

pageauc commented Apr 30, 2015

Hi Krist

I have fixed some bugs and added the fast time lapse feature. Was
wondering if you wanted to test pi-timolo version 2.3 before it is posted
to github. I have attached the two new files.

I have fixed motion video, text colour display bugs and added new quick
timelapse after motion detected option. I have attached a new version 2.3
of pi-timolo.py and a new version of config.py since there are new
variables and some cleanup. I have done testing and everything seems to
work OK but would appreciate if you give it a try. If you turn off write
text to images then the quick time lapse will work faster. I was thinking
of only writing text to first image but still thinking about this. I have
not updated github and will wait till you have had a chance to test for me.

When you copy the attached files to the RPI you should run dos2unix command
for the new pi-timolo.p file. if you do not have dos2unix on the rpi it
can be added with the following command. This will avoid any init.d
problems when running headless as a daemon

sudo apt-get install dos2unix
_cd ~_
cd pi-timolo
dos2unix pi-timolo.py
chmod +x pi-timolo.py

to test from putty ssh or desktop terminal execute the following

_cd ~_
cd pi-timolo
sudo ./pi-timolo.py

Note. The old config.py and templates files will not work due to new and
deleted variables.

Thanks Claude ...

On Thu, Apr 2, 2015 at 10:38 AM, krist10an notifications@github.com wrote:

Thanks for the excellent program, worked right out of the box for me [image:
👍]

Would it be possible to combine motion detection and timelapse in order to
create a timelapse movie that updates more often when there is motion? You
can see what I had in mind here:
https://youtu.be/gyeTsl8ghcQ?t=3m

I like the effect, allthou the street in the video is a bit too busy to
see the effect very clearly :-)


Reply to this email directly or view it on GitHub
#5.

See my YouTube Channel at http://www.youtube.com/user/pageaucp

User Configuration variable settings for pitimolo

Purpose - Motion Detection Security Cam

Created - 13-Dec-2014

Done by - Claude Pageau

configTitle = "pitimolo default configuration motion and timelapse 1080p images"
configName = "pitimolo-default-config"

These settings should both be False if this script is run as a background /etc/init.d daemon

verbose = True # Sends detailed logging info to console. set to False if running script as daeman
logDataToFile = False # logs diagnostic data to a disk file for review default=False

print a test image

imageTestPrint = False # default=False Set to True to print one image and exit (useful for aligning camera)

Image Settings

imageNamePrefix = 'cam1-' # Prefix for all image file names. Eg front-
imageWidth = 1920 # Full Size Image Width in px default=1980
imageHeight = 1080 # Full Size Image Height in px default=1080
imageVFlip = False # Flip image Vertically default=False
imageHFlip = False # Flip image Horizontally default=False
imagePreview = False # Preview image on connected RPI Monitor default=False
noNightShots = False # Don't Take images at Night default=False
noDayShots = False # Don't Take images during day time default=False

Low Light Night Settings

nightMaxShut = 5.8 # default=5 sec Highest cam shut exposure time. IMPORTANT 6 sec works sometimes but occasionally locks RPI and HARD reboot required to clear
nightMinShut = .001 # default=.002 sec Lowest camera shut exposure time for transition from day to night (or visa versa)
nightMaxISO = 800 # default=800 Max cam ISO night setting
nightMinISO = 100 # lowest ISO camera setting for transition from day to night (or visa versa)
nightSleepSec = 10 # default=10 Sec - Time period to allow camera to calculate low light AWB
nightDayTimer = 2 * 60 # (Not used in ver 1.2) Check stream changes to determine if entering twilight zones
sunsetThreshold = 90 # If in Day and pixAverage below this then time to switch to low light mode and ramp settings
sunriseThreshold = 220 # (Not used in ver 1.2) If in Night and pixAverage below this then time to ramp low light settings
twilightThreshold = 90 # New variable to replace sunset and sunrise Threshold settings

Date/Time Settings for Displaying info Directly on Images

showDateOnImage = True # Set to False for No display of date/time on image default= True
showTextBottom = True # Location of image Text True=Bottom False=Top
showTextWhite = True # Colour of image Text True=White False=Black
showTextWhiteNight = True # Change night text to white. Might help if night needs white instead of black during day or visa versa

Motion Detect Settings

motionOn = True # True = motion capture is turned on. False= No motion detection
motionPrefix = "mo-" # Prefix Motion Detection images
motionDir = "motion" # Storage Folder for Motion Detect Images
threshold = 10 # How much a pixel has to change to be counted default=10 (1-200)
sensitivity = 200 # Number of changed pixels to trigger motion default=300
motionVideoOn = False # If set to True then video clip is taken rather than image
motionVideoTimer = 10 # Number of seconds of video clip to take if Motion Detected default=10
motionQuickTLOn = True # if set to True then take a quick time lapse sequence rather than a single image (overrides motionVideoOn)
motionQuickTLTimer = 10 # Duration in seconds of quick time lapse sequence after initial motion detected default=10
motionQuickTLInterval = 0 # Time between each Quick time lapse image 0 is fast as possible
motionForce = 60 * 60 # Force single motion image if no Motion Detected in specified seconds. default=60*60
motionNumOn = True # True=On (filenames by sequenced Number) otherwise date/time used for filenames
motionNumStart = 1000 # Start motion number sequence
motionNumMax = 500 # Max number of motion images desired. 0=Continuous default=0
motionNumRecycle = True # After numberMax reached restart at numberStart instead of exiting default=True
motionMaxDots = 100 # Number of motion dots before starting new line
createLockFile = True # default=False if True then sync.sh will call grive to sync files to your web google drive if .sync file exists
# Lock File is used to indicate motion images are added so sync.sh can sync in background via sudo crontab -e

Time Lapse Settings

timelapseOn = True # Turns timelapse True=On False=Off
timelapseTimer = 5 * 60 # Seconds between timelapse images default=5*60
timelapseDir = "timelapse" # Storage Folder for Time Lapse Images
timelapsePrefix = "tl-" # Prefix timelapse images with this prefix
timelapseExit = 0 * 60 # Will Quit program after specified seconds 0=Continuous default=0
timelapseNumOn = True # True=On (filenames Sequenced by Number) otherwise date/time used for filename
timelapseNumStart = 1000 # Start of timelapse number sequence
timelapseNumMax = 1000 # Max number of timelapse images desired. 0=Continuous default=0
timelapseNumRecycle = True # After numberMax reached restart at numberStart instead of exiting default=True

---------------------------------------------- End of User Variables -----------------------------------------------------

#!/usr/bin/python

pitimolo - Raspberry Pi Long Duration Timelapse, Motion Detection, with Low Light Capability

written by Claude Pageau Dec-2014 (original issue)

getStreamImage function based on utpalc code based on brainflakes lightweight motion detection code on Raspberry PI forum - Thanks

Complete pi-timolo code is available on my github repo at https://github.com/pageauc

2.3 released 1-May-2015 added motion quick TL and fixed video hang bug

progVer = "ver 2.3"

import os
mypath=os.path.abspath(file) # Find the full path of this python script
baseDir=mypath[0:mypath.rfind("/")+1] # get the path location only (excluding script name)
baseFileName=mypath[mypath.rfind("/")+1:mypath.rfind(".")]
progName = os.path.basename(file)

Check for variable file to import and error out if not found.

configFilePath = baseDir + "config.py"
if not os.path.exists(configFilePath):
msgStr = "ERROR - Missing config.py file - Could not find Configuration file %s" % (configFilePath)
showMessage("readConfigFile", msgStr)
quit()
else:
# Read Configuration variables from config.py file
from config import *

if verbose:
print("------------------------------ Loading Python Libraries --------------------------------------")
else:
print("Note: verbose=False (Disabled) Set verbose=True to Display Detailed Messages.")

import remaining python libraries

import sys
import time
import datetime
import picamera
import picamera.array
import numpy as np
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
from fractions import Fraction

#==================================

System Variables

Should not need to be customized

#==================================
SECONDS2MICRO = 1000000 # Used to convert from seconds to microseconds
nightMaxShut = int(nightMaxShut * SECONDS2MICRO) # default=5 sec IMPORTANT- 6 sec works sometimes but occasionally locks RPI and HARD reboot required to clear
nightMinShut = int(nightMinShut * SECONDS2MICRO) # lowest shut camera setting for transition from day to night (or visa versa)
testWidth = 100 # width of rgb image stream used for motion detection and day/night changes
testHeight = 75 # height of rgb image stream used for motion detection and day/night changes
daymode = False # default should always be False.
progNameVer = "%s %s" %(progName, progVer)
motionPath = baseDir + motionDir # Store Motion images
motionNumPath = baseDir + motionPrefix + baseFileName + ".dat" # dat file to save currentCount
timelapsePath = baseDir + timelapseDir # Store Time Lapse images
timelapseNumPath = baseDir + timelapsePrefix + baseFileName + ".dat" # dat file to save currentCount
lockFilePath = baseDir + baseFileName + ".sync"

#-----------------------------------------------------------------------------------------------
def userMotionCodeHere():
# Users can put code here that needs to be run prior to taking motion capture images
# Eg Notify or activate something.

# User code goes here

return   

#-----------------------------------------------------------------------------------------------
def shut2Sec (shutspeed):
shutspeedSec = shutspeed/float(SECONDS2MICRO)
shutstring = str("%.3f sec") % ( shutspeedSec )
return shutstring

#-----------------------------------------------------------------------------------------------
def showTime():
rightNow = datetime.datetime.now()
currentTime = "%04d%02d%02d_%02d:%02d:%02d" % (rightNow.year, rightNow.month, rightNow.day, rightNow.hour, rightNow.minute, rightNow.second)
return currentTime

#-----------------------------------------------------------------------------------------------
def showMessage(functionName, messageStr):
if verbose:
now = showTime()
print ("%s %s - %s " % (now, functionName, messageStr))
return

#-----------------------------------------------------------------------------------------------
def showDots(dotcnt):
if motionOn and verbose:
dotcnt += 1
if dotcnt > motionMaxDots + 2:
print("")
dotcnt = 0
elif dotcnt > motionMaxDots:
print("")
stime = showTime() + " ."
sys.stdout.write(stime)
sys.stdout.flush()
dotcnt = 0
else:
sys.stdout.write('.')
sys.stdout.flush()
return dotcnt

#-----------------------------------------------------------------------------------------------
def checkConfig():
if not motionOn and not timelapseOn:
msgStr = "Warning - Both Motion and Timelapse are turned OFF - motionOn=%s timelapseOn=%s"
showMessage("checkConfig", msgStr)
return

#-----------------------------------------------------------------------------------------------
def logToFile(dataToAppend):
if logDataToFile:
logFilePath = baseDir + baseFileName + ".log"
if not os.path.exists(logFilePath):
open(logFilePath, 'w').close()
msgStr = "Create New Data Log File %s" % logFilePath
showMessage(" logToFile", msgStr)
filecontents = dataToAppend
f = open(logFilePath, 'ab')
f.write(filecontents)
f.close()
return

#-----------------------------------------------------------------------------------------------
def takeTestImage():
# Check if any parameter was passed to this script from the command line.
# This is useful for taking a single image for aligning camera without editing script settings.
mytime=showTime()
testfilename = "takeTestImage.jpg"
testfilepath = baseDir + testfilename
takeDayImage(testfilepath)
imagetext = "%s %s" % (mytime, testfilename)
writeTextToImage(testfilepath, imagetext, daymode)
msgStr = "imageTestPrint=%s Captured Test Image to %s " % (imageTestPrint, testfilepath)
showMessage ("takeTestImage", msgStr)
sys.exit(2)
return

#-----------------------------------------------------------------------------------------------
def displayInfo(motioncount, timelapsecount):
if verbose:
print("")
print("Note: To Send Full Output to File Use command - python -u ./%s | tee -a log.txt" % progName)
print(" Set logDataToFile=True to Send checkIfDay Data to File %s.log" % progName)
print("")
print("%s" % progNameVer)
print("-------------------------------------- Settings ----------------------------------------------")
print("Config File .. Title=%s" % configTitle)
print(" config-template filename=%s" % configName)
print("Images ....... Size=%ix%i Prefix=%s VFlip=%s HFlip=%s Preview=%s" % (imageWidth, imageHeight, imageNamePrefix, imageVFlip, imageHFlip, imagePreview))
print(" twilightThreshold=%i noNightShots=%s noDayShots=%s" % (twilightThreshold, noNightShots, noDayShots))
shutStr = shut2Sec(nightMaxShut)
print(" nightMaxShut=%s nightMaxISO=%i nightSleep=%i sec" % (shutStr, nightMaxISO, nightSleepSec))
print("Image Text ... On=%s Bottom=%s (False=Top) WhiteText=%s (False=Black) showTextWhiteNight=%s" % (showDateOnImage, showTextBottom, showTextWhite, showTextWhiteNight))
print("Motion ....... On=%s Prefix=%s threshold=%i(How Much) sensitivity=%i(How Many) forceTimer=%i min(If No Motion)" % (motionOn, motionPrefix, threshold, sensitivity, motionForce/60))
print(" motionPath=%s" % (motionPath))
if motionNumOn:
print(" Num Seq motionNumOn=%s current=%s numStart=%i numMax=%i numRecycle=%s" % (motionNumOn, motioncount, motionNumStart, motionNumMax, motionNumRecycle))
print(" motionNumPath=%s " % (motionNumPath))
print(" Video motionVideoOn=%s motionVideoTimer=%i sec (superseded by QuickTL)" % (motionVideoOn, motionVideoTimer))
print(" Quick TL motionQuickTLOn=%s motionQuickTLTimer=%i sec motionQuickTLInterval=%i sec (0=fastest)" % (motionQuickTLOn, motionQuickTLTimer, motionQuickTLInterval))
if createLockFile:
print("Grive Sync ... On=%s Path=%s Note: syncs for motion images only." % (createLockFile, lockFilePath))
print("Time Lapse ... On=%s Prefix=%s Timer=%i sec timeLapseExit=%i sec (0=Continuous)" % (timelapseOn, timelapsePrefix, timelapseTimer, timelapseExit))
print(" timelapsePath=%s" % (timelapsePath))
if timelapseNumOn:
print("TL Numbering . On=%s current=%s numStart=%i numMax=%i numRecycle=%s" % (timelapseNumOn, timelapsecount, timelapseNumStart, timelapseNumMax, timelapseNumRecycle))
print(" numPath=%s" % (timelapseNumPath))
print("Logging ...... verbose=%s (Details to Console) logDataToFile=%s logfile=%s" % ( verbose, logDataToFile, baseDir + baseFileName + ".log" ))
print("------------------------------------ Log Activity --------------------------------------------")
checkConfig()
return

#-----------------------------------------------------------------------------------------------
def checkImagePath():
# Checks for image folders and creates them if they do not already exist.
if motionOn:
if not os.path.isdir(motionPath):
msgStr = "Creating Image Motion Detection Storage Folder" + motionPath
showMessage ("checkImagePath", msgStr)
os.makedirs(motionPath)
if timelapseOn:
if not os.path.isdir(timelapsePath):
msgStr = "Creating Time Lapse Image Storage Folder" + timelapsePath
showMessage ("checkImagePath", msgStr)
os.makedirs(timelapsePath)
return

#-----------------------------------------------------------------------------------------------
def getCurrentCount(numberpath, numberstart):
# Create a .dat file to store currentCount or read file if it already Exists
# Create numberPath file if it does not exist
if not os.path.exists(numberpath):
msgStr = "Creating File " + numberpath + " numberstart=" + str(numberstart)
showMessage("getCurrentCount", msgStr)
open(numberpath, 'w').close()
f = open(numberpath, 'w+')
f.write(str(numberstart))
f.close()
# Read the numberPath file to get the last sequence number
with open(numberpath, 'r') as f:
writeCount = f.read()
f.closed
numbercounter = int(writeCount)
return numbercounter

#-----------------------------------------------------------------------------------------------
def writeTextToImage(imagename, datetoprint, daymode):
# function to write date/time stamp directly on top or bottom of images.
if showTextWhite:
FOREGROUND = ( 255, 255, 255 ) # rgb settings for white text foreground
textColour = "White"
else:
FOREGROUND = ( 0, 0, 0 ) # rgb settings for black text foreground
textColour = "Black"
if showTextWhiteNight and ( not daymode):
FOREGROUND = ( 255, 255, 255 ) # rgb settings for black text foreground
textColour = "White"
# centre text and compensate for graphics text being wider
x = int((imageWidth/2) - (len(imagename)*2))
if showTextBottom:
y = (imageHeight - 50) # show text at bottom of image
else:
y = 10 # show text at top of image
TEXT = imageNamePrefix + datetoprint
font_path = '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf'
font = ImageFont.truetype(font_path, 24, encoding='unic')
text = TEXT.decode('utf-8')
img = Image.open(imagename)
draw = ImageDraw.Draw(img)
# draw.text((x, y),"Sample Text",(r,g,b))
draw.text(( x, y ), text, FOREGROUND, font=font)
img.save(imagename)
msgStr = "Added " + textColour + " Text[" + datetoprint + "] on " + imagename
showMessage(" writeDataToImage",msgStr)
return

#-----------------------------------------------------------------------------------------------
def postImageProcessing(numberon, counterstart, countermax, counter, recycle, counterpath, filename, daymode):
# If required process text to display directly on image
if not motionVideoOn:
rightNow = datetime.datetime.now()
if showDateOnImage:
dateTimeText = "%04d%02d%02d_%02d:%02d:%02d" % (rightNow.year, rightNow.month, rightNow.day, rightNow.hour, rightNow.minute, rightNow.second)
if numberon:
counterStr = "%i " % ( counter )
imageText = counterStr + dateTimeText
else:
imageText = dateTimeText
# Now put the imageText on the current image
writeTextToImage(filename, imageText, daymode)
if createLockFile and motionOn:
createGriveLockFile(filename)
# Process currentCount for next image if number sequence is enabled
if numberon:
counter += 1
if countermax > 0:
if (counter > counterstart + countermax):
if recycle:
counter = counterstart
else:
print("%s - Exceeded Image Count numberMax=%i" % ( progName, countermax ))
print("Exiting %s" % progName)
sys.exit(2)
# write next image counter number to dat file
currentTime = showTime()
writeCount = str(counter)
if not os.path.exists(counterpath):
msgStr = "Create New Counter File writeCount=" + str(writeCount) + " " + counterpath
showMessage("postImageProcessing", msgStr)
open(counterpath, 'w').close()
f = open(counterpath, 'w+')
f.write(str(writeCount))
f.close()
msgStr = "Next Counter=" + str(writeCount) + " " + counterpath
showMessage(" postImageProcessing", msgStr)
return counter

#-----------------------------------------------------------------------------------------------
def getFileName(path, prefix, numberon, counter):
# build image file names by number sequence or date/time
if numberon:
if motionVideoOn:
filename = path + "/" + prefix + str(counter) + ".h264"
else:
filename = path + "/" + prefix + str(counter) + ".jpg"
else:
rightNow = datetime.datetime.now()
if motionVideoOn:
filename = "%s/%s%04d%02d%02d-%02d%02d%02d.h264" % ( path, prefix ,rightNow.year, rightNow.month, rightNow.day, rightNow.hour, rightNow.minute, rightNow.second)
else:
filename = "%s/%s%04d%02d%02d-%02d%02d%02d.jpg" % ( path, prefix ,rightNow.year, rightNow.month, rightNow.day, rightNow.hour, rightNow.minute, rightNow.second)
return filename

#-----------------------------------------------------------------------------------------------
def takeDayImage(filename):
# Take a Day image using exp=auto and awb=auto
with picamera.PiCamera() as camera:
camera.resolution = (imageWidth, imageHeight)
# camera.rotation = cameraRotate #Note use imageVFlip and imageHFlip variables
time.sleep(0.5) # sleep for a little while so camera can get adjustments
if imagePreview:
camera.start_preview()
camera.vflip = imageVFlip
camera.hflip = imageHFlip
# Day Automatic Mode
camera.exposure_mode = 'auto'
camera.awb_mode = 'auto'
camera.capture(filename)
msgStr = "Size=%ix%i exp=auto awb=auto %s" % (imageWidth, imageHeight, filename)
dataToLog = showTime() + " takeDayImage " + msgStr + "\n"
logToFile(dataToLog)
showMessage(" takeDayImage", msgStr)
return

#-----------------------------------------------------------------------------------------------
def takeNightImage(filename):
dayStream = getStreamImage(True)
dayPixAve = getStreamPixAve(dayStream)
currentShut, currentISO = getNightCamSettings(dayPixAve)
# Take low light Night image (including twilight zones)
with picamera.PiCamera() as camera:
# Take Low Light image
# Set a framerate of 1/6fps, then set shutter
camera.resolution = (imageWidth, imageHeight)
if imagePreview:
camera.start_preview()
camera.vflip = imageVFlip
camera.hflip = imageHFlip
camera.framerate = Fraction(1, 6)
camera.shutter_speed = currentShut
camera.exposure_mode = 'off'
camera.iso = currentISO
# Give the camera a good long time to measure AWB
# (you may wish to use fixed AWB instead)
time.sleep(nightSleepSec)
camera.capture(filename)
shutSec = shut2Sec(currentShut)
msgStr = "Size=%ix%i dayPixAve=%i ISO=%i shut=%s %s" %( imageWidth, imageHeight, dayPixAve, currentISO, shutSec, filename )
dataToLog = showTime() + " takeNightImage " + msgStr + "\n"
logToFile(dataToLog)
showMessage(" takeNightImage", msgStr)
return

#-----------------------------------------------------------------------------------------------
def takeVideo(filename):
# Take a short motion video if required
msgStr = "Size %ix%i for %i sec %s" % (imageWidth, imageHeight, motionVideoTimer, filename)
showMessage(" takeVideo", msgStr)
if motionVideoOn:
with picamera.PiCamera() as camera:
camera.resolution = (imageWidth, imageHeight)
camera.start_recording(filename)
camera.wait_recording(motionVideoTimer)
camera.stop_recording()
return

#-----------------------------------------------------------------------------------------------
def createGriveLockFile(imagefilename):
# If required create a lock file to indicate grive (sync.sh) has file(s) to process
if createLockFile:
if not os.path.exists(lockFilePath):
open(lockFilePath, 'w').close()
msgStr = "Create grive sync.sh Lock File " + lockFilePath
showMessage(" createGriveLockFile", msgStr)
rightNow = datetime.datetime.now()
now = "%04d%02d%02d-%02d%02d%02d" % ( rightNow.year, rightNow.month, rightNow.day, rightNow.hour, rightNow.minute, rightNow.second )
filecontents = now + " createGriveLockFile - " + imagefilename + " Ready to sync using sudo ./sync.sh command."
f = open(lockFilePath, 'w+')
f.write(filecontents)
f.close()
return

#-----------------------------------------------------------------------------------------------
def getStreamImage(isDay):
# Capture an image stream to memory based on daymode
with picamera.PiCamera() as camera:
time.sleep(0.5)
camera.resolution = (testWidth, testHeight)
with picamera.array.PiRGBArray(camera) as stream:
if isDay:
camera.exposure_mode = 'auto'
camera.awb_mode = 'auto'
else:
# Take Low Light image
# Set a framerate of 1/6fps, then set shutter
# speed to 6s
camera.framerate = Fraction(1, 6)
camera.shutter_speed = nightMaxShut
camera.exposure_mode = 'off'
camera.iso = nightMaxISO
# Give the camera a good long time to measure AWB
# (you may wish to use fixed AWB instead)
time.sleep( nightSleepSec )
camera.capture(stream, format='rgb')
return stream.array

#-----------------------------------------------------------------------------------------------
def getStreamPixAve(streamData):
# Calculate the average pixel values for the specified stream (used for determining day/night or twilight conditions)
pixAverage = int(np.average(streamData[...,1]))
return pixAverage

#-----------------------------------------------------------------------------------------------
def getNightCamSettings(dayPixAve):
# Calculate Ratio
if dayPixAve <= twilightThreshold:
ratio = ((twilightThreshold - dayPixAve)/float(twilightThreshold))
outShut = int(nightMaxShut * ratio)
outISO = int(nightMaxISO * ratio)
else:
ratio = 0.0
outShut = nightMinShut
outISO = nightMinISO
# Do some Bounds Checking to avoid potential problems
if outShut < nightMinShut:
outShut = nightMinShut
if outShut > nightMaxShut:
outShut = nightMaxShut
if outISO < nightMinISO:
outISO = nightMinISO
if outISO > nightMaxISO:
outISO = nightMaxISO
msgStr = "dayPixAve=%i ratio=%.3f ISO=%i shut=%i %s" % ( dayPixAve, ratio, outISO, outShut, shut2Sec(outShut))
showMessage(" getNightCamSettings", msgStr)
return outShut, outISO

#-----------------------------------------------------------------------------------------------
def checkIfDay(currentDayMode, dataStream):
# Try to determine if it is day, night or twilight.
dayPixAverage = 0
if currentDayMode:
dayPixAverage = getStreamPixAve(dataStream)
else:
dayStream = getStreamImage(True)
dayPixAverage = getStreamPixAve(dayStream)
if dayPixAverage > twilightThreshold:
currentDayMode = True
else:
currentDayMode = False
return currentDayMode

#-----------------------------------------------------------------------------------------------
def timeToSleep(currentDayMode):
if noNightShots:
if currentDayMode:
sleepMode=False
else:
sleepMode=True
elif noDayShots:
if currentDayMode:
sleepMode=True
else:
sleepMode=False
else:
sleepMode=False
return sleepMode

#-----------------------------------------------------------------------------------------------
def checkForTimelapse (timelapseStart):
# Check if timelapse timer has expired
rightNow = datetime.datetime.now()
timeDiff = ( rightNow - timelapseStart).total_seconds()
if timeDiff > timelapseTimer:
timelapseStart = rightNow
timelapseFound = True
else:
timelapseFound = False
return timelapseFound

#-----------------------------------------------------------------------------------------------
def checkForMotion(data1, data2):
# Find motion between two data streams based on sensitivity and threshold
motionDetected = False
pixChanges = 0;
pixColor = 1 # red=0 green=1 blue=2
for w in range(0, testWidth):
for h in range(0, testHeight):
# get the diff of the pixel. Conversion to int
# is required to avoid unsigned short overflow.
pixDiff = abs(int(data1[h][w][pixColor]) - int(data2[h][w][pixColor]))
if pixDiff > threshold:
pixChanges += 1
if pixChanges > sensitivity:
break; # break inner loop
if pixChanges > sensitivity:
break; #break outer loop.
if pixChanges > sensitivity:
motionDetected = True
if motionDetected:
dotCount = showDots(motionMaxDots + 2) # New Line
msgStr = "Found Motion - sensitivity=" + str(sensitivity) + " Exceeded ..."
showMessage("checkForMotion", msgStr)
return motionDetected

#-----------------------------------------------------------------------------------------------
def dataLogger():
# Replace main() with this function to log day/night pixAve to a file.
# Note variable logDataToFile must be set to True in config.py
# You may want to delete pi-timolo.log to clear old data.
print "dataLogger - One Moment Please ...."
while True:
dayStream = getStreamImage(True)
dayPixAverage = getStreamPixAve(dayStream)
nightStream = getStreamImage(False)
nightPixAverage = getStreamPixAve(nightStream)
logdata = "nightPixAverage=%i dayPixAverage=%i twilightThreshold=%i " % ( nightPixAverage, dayPixAverage, twilightThreshold )
showMessage("dataLogger",logdata)
logdata = now + " " + logdata
logToFile(logdata)
time.sleep(60)
return

#-----------------------------------------------------------------------------------------------
def Main():
# Main program initialization and logic loop
dotCount = 0 # Counter for showDots() display if not motion found (shows system is working)
checkImagePath()
timelapseNumCount = 0
motionNumCount = 0
moCnt = "non"
tlCnt = "non"
if timelapseOn:
if timelapseNumOn:
timelapseNumCount = getCurrentCount(timelapseNumPath, timelapseNumStart)
tlCnt = str(timelapseNumCount)
if motionOn:
if motionNumOn:
motionNumCount = getCurrentCount(motionNumPath, motionNumStart)
moCnt = str(motionNumCount)
displayInfo(moCnt, tlCnt)
if imageTestPrint:
takeTestImage() # prints one image and exits if imageTestPrint = True in config.py
# image stream is taken with low light settings and if Day will be almost all white pix val >240
sunset = False
daymode = False
data1 = getStreamImage(True)
daymode = checkIfDay(daymode, data1)
if not daymode:
data1 = getStreamImage(False)
timelapseStart = datetime.datetime.now()
checkDayTimer = timelapseStart
checkMotionTimer = timelapseStart
forceMotion = False # Used for forcing a motion image if no motion for motionForce time exceeded
msgStr = "Entering Motion Detection and Time Lapse Loop Working ..."
showMessage("Main", msgStr)
dotCount = showDots(motionMaxDots) # reset motion dots
# Start main program loop here. Use Ctl-C to exit if required.
while True:
daymode = checkIfDay(daymode, data1)
data2 = getStreamImage(daymode) # This gets the second stream of motion analysis
rightNow = datetime.datetime.now() # refresh rightNow time
if not timeToSleep(daymode):
if timelapseOn:
takeTimeLapse = checkForTimelapse(timelapseStart)
if takeTimeLapse:
dotCount = showDots(motionMaxDots + 2) # reset motion dots
msgStr = "Scheduled Time Lapse Image - daymode=" + str(daymode)
showMessage("Main", msgStr)
imagePrefix = timelapsePrefix + imageNamePrefix
filename = getFileName(timelapsePath, imagePrefix, timelapseNumOn, timelapseNumCount)
if daymode:
takeDayImage(filename)
else:
takeNightImage(filename)
timelapseNumCount = postImageProcessing(timelapseNumOn, timelapseNumStart, timelapseNumMax, timelapseNumCount, timelapseNumRecycle, timelapseNumPath, filename, daymode)
timelapseStart = datetime.datetime.now() # reset timelapse timer
dotCount = showDots(motionMaxDots)
if motionOn:
# IMPORTANT - Night motion detection may not work very well due to long exposure times and low light (may try checking red instead of green)
# Also may need night specific threshold and sensitivity settings (Needs more testing)
motionFound = checkForMotion(data1, data2)
rightNow = datetime.datetime.now()
timeDiff = (rightNow - checkMotionTimer).total_seconds()
if timeDiff > motionForce:
dotCount = showDots(motionMaxDots + 2) # New Line
msgStr = "No Motion Detected for " + str(motionForce / 60) + " minutes. Taking Forced Motion Image."
showMessage("Main", msgStr)
checkMotionTimer = rightNow
forceMotion = True
if motionFound or forceMotion:
dotCount = showDots(motionMaxDots + 2) # New Line
checkMotionTimer = rightNow
if forceMotion:
forceMotion = False
imagePrefix = motionPrefix + imageNamePrefix
# check if motion Quick Time Lapse option is On. This option supersedes motionVideoOn
if motionQuickTLOn:
msgStr = "motion Quick Time Lapse for %i sec every %i sec" % (motionQuickTLTimer, motionQuickTLInterval)
showMessage("Main", msgStr)
checkTimeLapseTimer = datetime.datetime.now()
keepTakingImages = True
while keepTakingImages:
rightNow = datetime.datetime.now()
timelapseDiff = (rightNow - checkTimeLapseTimer).total_seconds()
if timelapseDiff > motionQuickTLTimer:
keepTakingImages=False
else:
filename = getFileName(motionPath, imagePrefix, motionNumOn, motionNumCount)
takeDayImage(filename)
motionNumCount = postImageProcessing(motionNumOn, motionNumStart, motionNumMax, motionNumCount, motionNumRecycle, motionNumPath, filename, daymode)
time.sleep(motionQuickTLInterval)
else:
filename = getFileName(motionPath, imagePrefix, motionNumOn, motionNumCount)
if motionVideoOn:
takeVideo(filename)
else:
if daymode:
takeDayImage(filename)
else:
takeNightImage(filename)
motionNumCount = postImageProcessing(motionNumOn, motionNumStart, motionNumMax, motionNumCount, motionNumRecycle, motionNumPath, filename, daymode)
if motionFound:
# =========================================================================
# Put your user code in userMotionCodeHere() function at top of this script
# =========================================================================
userMotionCodeHere()
dotCount = showDots(motionMaxDots)
else:
dotCount = showDots(dotCount) # show progress dots when no motion found
data1 = data2
return

#-----------------------------------------------------------------------------------------------
if name == 'main':
try:
Main()
finally:
print("")
print("+++++++++++++++++++++++++++++++++++")
print("%s - Exiting Program" % progName)
print("+++++++++++++++++++++++++++++++++++")
print("")

@krist10an
Copy link
Author

Hi Claude,

Thanks! We have a new member of the family joining very-soon-now(tm), so I cannot promise a speedy response, but I'll try it when I have some time.

@krist10an
Copy link
Author

Hi Claude,

Sorry for taking so long to test this. We just had a baby, so he got most my attention lately 👶 I tried to download the attachments you made, but all line breaks are removed which makes it a bit difficult to test Python code 😄 . Would it be an option for you to push the changes to a branch so I can test that?

@pageauc
Copy link
Owner

pageauc commented May 19, 2015

Kristen

Files would be in Unix Format.

Source and instructions are on Github at
https://github.com/pageauc/pi-timolo.

Here are brief instructions FYI. Please note you will need the new
config.py due to new variables. Also grive is no longer working since
google changed the docs api.

Quick Setup

SSH (putty) or Desktop Terminal login to RPI and perform the following

cd ~
mkdir pi-timolo (or a folder name of your choice)
cd ./pi-timolo
wget https://raw.github.com/pageauc/pi-timolo/master/pi-timolo.tar
tar -pxvf pi-timolo.tar

Install dependancies and required software

sudo ./setup-timolo.sh

Initialize pi-timolo.py files, motion and test motion.

python ./pi-timolo.py

Verify motion then ctrl-c to exit pi-timolo.py

Edit config.py file using nano editor to change any desired settings

per comments.

ctrl-x y to Save

nano config.py

test edit changes.

sudo ./pi-timolo.py

Regards

Claude ...

On Tue, May 19, 2015 at 2:49 AM, krist10an notifications@github.com wrote:

Hi Claude,

Sorry for taking so long to test this. We just had a baby, so he got most
my attention lately [image: 👶] I tried to download the attachments
you made, but all line breaks are removed which makes it a bit difficult to
test Python code [image: 😄] . Would it be an option for you to push
the changes to a branch so I can test that?


Reply to this email directly or view it on GitHub
#5 (comment).

@jeffisfast
Copy link

Thanks Claude for this.

I'm wondering if there is a way for me to get both a JPG image when motion is detected and an H264 video? From playing with the settings it seems I can get one or the other, but not both. I was hoping to get a single JPG when motion is detected, and then a video for whatever duration I specify in the settings.

Jeff

@pageauc
Copy link
Owner

pageauc commented May 19, 2015

I think you can do this by adding some of the takeVideo(filename) function
logic to the userMotionCodeHere() function. If you have the correct
configuration settings for just taking a single image when motion is
detected, then the user MotionCodeHere() function will trigger and take a
video for specified motionVideoTimer time period. Make sure the
motionVideoOn is set to False in the config.py. This should generate a JPG
and a video after motion is detected. Give it a try.

Sample code to add to userMotionCodeHere() function

    filename = getVideoName(motionPath, imagePrefix, motionNumOn,

motionNumCount)
with picamera.PiCamera() as camera:
camera.resolution = (imageWidth, imageHeight)
camera.start_recording(filename)
camera.wait_recording(motionVideoTimer)
camera.stop_recording()

On Tue, May 19, 2015 at 5:03 PM, Jeff Tarr notifications@github.com wrote:

Thanks Claude for this.

I'm wondering if there is a way for me to get both a JPG image when motion
is detected and an H264 video? From playing with the settings it seems I
can get one or the other, but not both. I was hoping to get a single JPG
when motion is detected, and then a video for whatever duration I specify
in the settings.

Jeff


Reply to this email directly or view it on GitHub
#5 (comment).

See my YouTube Channel at http://www.youtube.com/user/pageaucp

@jeffisfast
Copy link

Makes sense. Will give it a try. Thanks again for everything.

@jeffisfast
Copy link

Claude,

Thanks again. Got it working, but had to manually build the filename since getVideoName breaks if MotionVideoOn is False.

Separate question now - seems that sometimes my camera thinks it should be in "night" mode when in fact it should be in "day" mode. Could you offer any suggestions for how to make it more reluctant to switching to night?

Thanks again!

@pageauc
Copy link
Owner

pageauc commented May 20, 2015

Change the twilightThreshold setting. This will adjust when the camera
tries to switch from night to day.

If the ambient day lighting is lower then reduce from default 90 to a lower
setting. You might have to experiment or check detailed logs at dusk or
dawn. I have a camera under a canopy that does not get lighting directly
from the sky. I set it to 50 and this seems to help although sometimes if
it is a darker day it tries to switch to night mode once in a while.
Similar issues exist if camera is indoors with lower ambient lighting.

You can also increase the wait time in the takeDayImage() function to maybe
1 or 2 seconds since this will allow the camera more time to establish a
good white balance. This will also slow the camera down if you are trying
to catch motion as quickly as possible. If there is good lighting I have
found you can get away with .1 second delay. It usually takes a little
trial and error. My default settings are for a camera that is out in the
open with sky above.

I am working on automating this setting in the checkIfDay() function but it
is a little tricky since you are looking for extreme pixel averages near 0
or 255 usually near dusk or dawn. This works but I am trying to not slow
the camera performance and that is the tricky part.

Hope this helps
Let me know how you make out

Regards
Claude ...

On Wed, May 20, 2015 at 10:48 AM, Jeff Tarr notifications@github.com
wrote:

Claude,

Thanks again. Got it working, but had to manually build the filename since
getVideoName breaks if MotionVideoOn is False.

Separate question now - seems that sometimes my camera thinks it should be
in "night" mode when in fact it should be in "day" mode. Could you offer
any suggestions for how to make it more reluctant to switching to night?

Thanks again!


Reply to this email directly or view it on GitHub
#5 (comment).

See my YouTube Channel at http://www.youtube.com/user/pageaucp

@krist10an
Copy link
Author

Claude,

I was not able to download the files, so I had to copy&paste them from the source here which was causing me issues since newlines were stripped for some reason. Anyway, I got that sorted out and tested the code now (finally).

Quick motion detection works and it generates several motion detection images upon detection! 6 images were captured in my first test, with showDateOnImage disabled it captured 9 images in my second test. Seems to work great, is there anything special you'd like me to test?

How do you suggest to use both the motion and timelapse images together in a movie? Motion images are stored in motion/ and timelapse in timelapse/, while the makemovie.py only uses images from timelapse/ as far as I can see.

@pageauc
Copy link
Owner

pageauc commented May 21, 2015

I would recommend you change settings so both timelapse and motion go into
into the same subfolder. Also change settings both have the same prefix for
the filename settings so all files are named the same except the date/time
sequence
This will capture a regular sequence of events on a steady frequency and
them capture quick timelapse when motion is found. To do this I would
recommend setting both time lapse and motion with number sequence turned
off. This will put the file naming in date/time order. . That way you can
sort them by filename when you make a movie.

This setup will work for shorter sequences that you are interested in that
have a longer leadin but need more detail when motion is detected. An
example would be animals that take a while to show up (lead in timelapse)
and capture some activity then leave after a while (quick timelapse on
motion detect). If you have an external hard drive you can connect to the
pi then that would allow faster saving of more images and less chance of
running out of space on the SD card.

If you want to speed things up then reduce image dimensions to maybe eg
720p or similar instead of 1080p.

Let me know how you make out.

Claude ...

On Thu, May 21, 2015 at 1:50 PM, krist10an notifications@github.com wrote:

Claude,

I was not able to download the files, so I had to copy&paste them from the
source here which was causing me issues since newlines were stripped for
some reason. Anyway, I got that sorted out and tested the code now
(finally).

Quick motion detection works and it generates several motion detection
images upon detection! 6 images were captured in my first test, with
showDateOnImage disabled it captured 9 images in my second test. Seems to
work great, is there anything special you'd like me to test?

How do you suggest to use both the motion and timelapse images together in
a movie? Motion images are stored in motion/ and timelapse in timelapse/,
while the makemovie.py only uses images from timelapse/ as far as I can see.


Reply to this email directly or view it on GitHub
#5 (comment).

See my YouTube Channel at http://www.youtube.com/user/pageaucp

@krist10an
Copy link
Author

Hi Claude,

Thanks for describing how to do it for me :-)
I tested the combined motion and timelapse by outputing them to the same folder with date filenames, and it seems to work great! Unfortunately there wasn't really anything interesting out the window I pointed the camera so I don't really see the effect in the generated video. I'll have to put it in another window and try again!

Thanks again for the fix, much appreciated 👍

@pageauc
Copy link
Owner

pageauc commented May 22, 2015

Kristen

Time lapse is deceptive sometimes. We humans live at a different faster
time frame that most of nature. You might want to think of watching grass
or plants grow and catching birds with quick motion once in a while. You
will need to set an appropriate time interval. I usually calculate how
long the video should be after editing. At 30 FPS for a 3 minute video
that would be approx 11k images. Then calculate the time period you want
to cover. To see grass grow take 3 or 4 days @ 12 hours per day would be
approx 2700 images per day or about every 3-4 minutes timelapse interval.
You should then factor in any fast motion sequences.and this depends on the
quick timelapse duration you set (lets say 10 seconds at fastest rate
possible or 0). Make sure the camera does not pick up too much random
motion of vegetation or other movement. You can set the motion threshold
higher so a pixel would need to change more before being counted. default
is 10 so raise to maybe 40 or 50 and do a little test. I like to keep
sensitivity lower if possible depending on how close to the camera and how
large the moving objects are. Usually 150 or 200 is a good start. There
is some trial and error but it is nice when it is tuned and running good
since you can just let it run unattended. I was pleasantly surprised at my
winter time lapse project.

It is here if you have not already seen it. https://youtu.be/pFkt0ZOrPzg
There is some wonderful time lapse in the documentary Antarctica a year on
the ice http://www.imdb.com/title/tt2361700/?ref_=fn_al_tt_1
Let me know what you discover.
Good Luck Claude ...

On Fri, May 22, 2015 at 2:10 PM, krist10an notifications@github.com wrote:

Hi Claude,

Thanks for describing how to do it for me :-)
I tested the combined motion and timelapse by outputing them to the same
folder with date filenames, and it seems to work great! Unfortunately there
wasn't really anything interesting out the window I pointed the camera so I
don't really see the effect in the generated video. I'll have to put it in
another window and try again!

Thanks again for the fix, much appreciated [image: 👍]


Reply to this email directly or view it on GitHub
#5 (comment).

See my YouTube Channel at http://www.youtube.com/user/pageaucp

@pageauc
Copy link
Owner

pageauc commented May 22, 2015

I believe this enhancement is implemented and tested. I had fun with this and hope it is useful for others

@pageauc pageauc closed this as completed May 22, 2015
@pageauc
Copy link
Owner

pageauc commented Jun 12, 2015

fyi I have released version 2.6 of pi-timolo at
https://github.com/pageauc/pi-timolo

Version 2.6 release notes

  • fixed bug that caused a hang when video mode was True
  • Added a new quick time lapse after motion detected feature.
  • cleaned up console display
  • misc bug fixes
  • Added gdrive binary for easy syncing of files with internet based
    google drive seehttps://github.com/odeke-em/drive/releases amd binary
    and https://github.com/odeke-em/drive for additional implementation
    details

see Readme.md for details.

Please note, I am also using another very good web based interface that is
mentioned in my Readme.md file extract below. I have this setup with a 3
second buffer prior to motion. Let me know if you have any questions about
my setup. I still like pi-timolo for headless security camera operation.

If you are looking for a good web based interactive RPI camera interface I
would highly recommend this application. It is easy to install and works
well. But I still prefer pi-timolo for remote headless camera situations.

http://elinux.org/RPi-Cam-Web-Interface

This interface can also be setup to use gdrive syncing. Contact me if you
need details.

On Fri, May 22, 2015 at 3:57 PM, Claude Pageau pageauc@gmail.com wrote:

Kristen

Time lapse is deceptive sometimes. We humans live at a different faster
time frame that most of nature. You might want to think of watching grass
or plants grow and catching birds with quick motion once in a while. You
will need to set an appropriate time interval. I usually calculate how
long the video should be after editing. At 30 FPS for a 3 minute video
that would be approx 11k images. Then calculate the time period you want
to cover. To see grass grow take 3 or 4 days @ 12 hours per day would be
approx 2700 images per day or about every 3-4 minutes timelapse interval.
You should then factor in any fast motion sequences.and this depends on the
quick timelapse duration you set (lets say 10 seconds at fastest rate
possible or 0). Make sure the camera does not pick up too much random
motion of vegetation or other movement. You can set the motion threshold
higher so a pixel would need to change more before being counted. default
is 10 so raise to maybe 40 or 50 and do a little test. I like to keep
sensitivity lower if possible depending on how close to the camera and how
large the moving objects are. Usually 150 or 200 is a good start. There
is some trial and error but it is nice when it is tuned and running good
since you can just let it run unattended. I was pleasantly surprised at my
winter time lapse project.

It is here if you have not already seen it. https://youtu.be/pFkt0ZOrPzg
There is some wonderful time lapse in the documentary Antarctica a year on
the ice http://www.imdb.com/title/tt2361700/?ref_=fn_al_tt_1
Let me know what you discover.
Good Luck Claude ...

On Fri, May 22, 2015 at 2:10 PM, krist10an notifications@github.com
wrote:

Hi Claude,

Thanks for describing how to do it for me :-)
I tested the combined motion and timelapse by outputing them to the same
folder with date filenames, and it seems to work great! Unfortunately there
wasn't really anything interesting out the window I pointed the camera so I
don't really see the effect in the generated video. I'll have to put it in
another window and try again!

Thanks again for the fix, much appreciated [image: 👍]


Reply to this email directly or view it on GitHub
#5 (comment).

See my YouTube Channel at http://www.youtube.com/user/pageaucp

See my YouTube Channel at http://www.youtube.com/user/pageaucp

@swarfer swarfer mentioned this issue Feb 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants