pageauc / pi-timolo

Raspberry PI-TIMOLO ( PI-TImelapse, MOtion, LOwLight ) uses RPI picamera and OpenCV for Remote Headless Security Monitoring using Motion Tracking, Rclone Auto Sync files with remote storage services. Auto Twilight Transitions and Low Light Camera Settings. Panoramic images using PanTiltHat and More. This project is featured on GitHub Awesome software.
MIT License
549 stars 101 forks source link

Combine motion and timelapse #5

Closed krist10an closed 9 years ago

krist10an commented 9 years ago

Thanks for the excellent program, worked right out of the box for me :+1:

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 commented 9 years ago

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: :+1:]

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 https://github.com/pageauc/pi-timolo/issues/5.

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

krist10an commented 9 years ago

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 commented 9 years ago

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: :+1:]

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 https://github.com/pageauc/pi-timolo/issues/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=560 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 commented 9 years ago

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 commented 9 years ago

Hi Claude,

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

pageauc commented 9 years ago

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: :baby:] 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: :smile:] . 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 https://github.com/pageauc/pi-timolo/issues/5#issuecomment-103369903.

jeffisfast commented 9 years ago

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 commented 9 years ago

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 https://github.com/pageauc/pi-timolo/issues/5#issuecomment-103666847.

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

jeffisfast commented 9 years ago

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

jeffisfast commented 9 years ago

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 commented 9 years ago

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 https://github.com/pageauc/pi-timolo/issues/5#issuecomment-103912953.

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

krist10an commented 9 years ago

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 commented 9 years ago

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 https://github.com/pageauc/pi-timolo/issues/5#issuecomment-104370303.

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

krist10an commented 9 years ago

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 :+1:

pageauc commented 9 years ago

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: :+1:]

— Reply to this email directly or view it on GitHub https://github.com/pageauc/pi-timolo/issues/5#issuecomment-104733386.

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

pageauc commented 9 years ago

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

pageauc commented 9 years ago

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

Version 2.6 release notes

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: :+1:]

— Reply to this email directly or view it on GitHub https://github.com/pageauc/pi-timolo/issues/5#issuecomment-104733386.

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

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