Skip to content

Instantly share code, notes, and snippets.

@dermotbalson
Last active December 18, 2015 10:58
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save dermotbalson/5771827 to your computer and use it in GitHub Desktop.
motion
-- Main
function setup()
-- Start using the front camera
cameraSource(CAMERA_FRONT)
screenCamWidth=0 --to fit image on screen
Settings() --keep this separate
parameter.integer("AlertLevel",0,5,0) --rough indicator of movement activity, 1-
parameter.integer("Sensitivity",50,400,100)
parameter.boolean("Picture",true)
parameter.action("Clear",function() output.clear() end) --to clear print output area
parameter.integer("FPS",0,60,60)
end
function Settings()
lum_r,lum_g,lum_b=0.21,.71,.08 --luminosity weightings
--(taken from here: http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale)
pixelNoise=100 --individual pixel difference allowed before it is noted as a change
noiseThreshold=.05 -- fraction of changing pixels required to raise an alert
sampleSize=20 --examine every X pixels
iterationWeight=0.02 --weight to give new image when merging with background image
bgTable={} --table to hold sampled pixels for background
camWidth=0
swipe={}
interval=0.1
timer=0
end
--most of this code comes from the camera demo project
function draw()
background(220)
while camWidth==0 do
local img=image(CAMERA)
camWidth, camHeight = spriteSize( CAMERA )
screenCamWidth=math.min(WIDTH,camWidth)
return
end
FPS=FPS*.9+.1/DeltaTime
timer=timer+DeltaTime
if Picture then sprite( CAMERA,WIDTH/2,HEIGHT/2,screenCamWidth) end
if timer>interval then
display=takePhoto()
timer=0
end
if display~=nil then sprite(display,WIDTH/2,HEIGHT/2,screenCamWidth) end
end
--take photo and analyse pixel sample
function takePhoto()
local img=image(CAMERA) --take picture
if Picture then local img2 end --secondary image overlaid to show moving pixels
--fill background table if this is the initial background image
if #bgTable==0 then
for i=1,camWidth,sampleSize do
for j=1,camHeight,sampleSize do
local r,g,b=img:get(i,j)
--calculate luminosity
--use sequential table because we'll always work through it in the same order
bgTable[#bgTable+1]=r*lum_r+g+lum_g+b*lum_b
end
end
--also set number of pixels that can be changed before we create an alert
pixelChangesAllowed=noiseThreshold*#bgTable
--initialise directional variables
--these store the average (centre) of the changing pixels
lastX,lastY=0,0
print("Ready")
else --this is a subsequent image, do comparisons with background
if Picture then img2=image(camWidth,camHeight) end
pushStyle()
fill(255,255,0)
if Picture then setContext(img2) end
local n,count=0,0 --n marks place in table, count adds up changed pixels
local weightX,denomX,weightY,denomY=0,0,0,0 -- calculate centre of moving pixels using weights
--(centre is used for determining direction of movement)
for i=1,camWidth,sampleSize do
for j=1,camHeight,sampleSize do
local r,g,b=img:get(i,j)
--calculate luminosity
local lum=r*lum_r+g+lum_g+b*lum_b
n=n+1
--compare with background
local u=math.abs(bgTable[n]-lum)
if u>pixelNoise then
count=count+1
--calc weighted ave of x and y of above-threshold changes
weightX=weightX+u*i denomX=denomX+u
weightY=weightY+u*j denomY=denomY+u
if Picture then ellipse(i,j,3) end --draw marked on secondary image
end
--mix the background and current image
--this is so the background gradually iterates toward the current image
--allowing any changes in the background to gradually be included
bgTable[n]=bgTable[n]*(1-iterationWeight)+lum*iterationWeight
end
end
AlertLevel=count*10/#bgTable --the parameter value roughly indicating activity level
popStyle()
if Picture then setContext() end
--now we want to figure out the direction of any swipe
--this is hard and the approach below needs improvement
--currently it tests if the average x or y has moved in any direction by more than x pixels
local swipePixels=Sensitivity -- threshold for swipe, ie if average x or y moves by more, it is a swipe
local overrideMult=1 --if x change greater than this multiple of y, then cancel y (and vice versa)
--(reason is so we capture the main movement)
if count>pixelChangesAllowed then --we have a significant change
local currX,currY=weightX/denomX,weightY/denomY --weighted mean of changing points
--see if the change in weighted mean x is the biggest (since this movement started)
if lastX~=nil then
local dx=currX-lastX
--set swipe.x if we don't have one or if latest movement is bigger
if swipe.x==nil or math.abs(swipe.x)< math.abs(dx) then swipe.x=dx end
end
--same for y
if lastY~=nil then
local dy=currY-lastY
if swipe.y==nil or math.abs(swipe.y)< math.abs(dy) then swipe.y=dy end
end
--set lastX, lastY for next time
--if we already have existing values, blend the old and new values using q
local q=.75
if lastX==nil then lastX,lastY=currX,currY
else lastX,lastY=lastX*q+currX*(1-q),lastY*q+currY*(1-q) end
if Picture then return img2 end
else --changes are below threshold, clean up and report any movement
lastX,lastY=nil,nil
--if x and y have moved significantly, only keep one of them if it is significantly above the other
if swipe.x~=nil and swipe.y~=nil then
local swipeRatio=math.abs(swipe.x/swipe.y)
if swipeRatio>overrideMult then
swipe.y=nil
elseif swipeRatio<1/overrideMult then
swipe.x=nil
end
end
--now report the results
if swipe.x~=nil then if swipe.x>0 then print("Right") else print("Left") end end
if swipe.y~=nil then if swipe.y>0 then print("Up") else print("Down") end end
swipe.x,swipe.y=nil,nil
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment