motion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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