Raspberry amb càmera i mòbil android

Fa poc vaig rebre la Raspberry Pi Camera i he aprofitat per fer quatre proves i enxampar un llangardaix ocel·lat que volta prop de casa però que s’amaga de seguida al apropar-nos. Sabent on té el seu cau, i que acostuma a sortir quan hi toca el sol, vaig deixar la càmera apuntant-hi i executant un script python per anar fent fotos cada cert temps o quan detectés moviment.

Si la raspberry es col·loca en algun indret proper al router i s’agafa el wifi, facilita molt les coses ja que es pot accedir via ssh i control·lar la càmera des de l’ordinador de sobretaula. En el cas de no tenir wifi, una solució per control·lar la raspberry és accedir-hi des del telèfon mòbil on es disposa de pantalla i teclat.

Accés a la RPi des d’un mòbil android:

Per connectar el mòbil android a la raspberry només cal configurar un fitxer de xarxa a la raspberry i després endollar el cable usb des del mòbil a la raspberry i utilitzar alguna app per connectar-s’hi. A tenir en compte que el mòbil ha de tenir l’opció de “USB Tethering” per poder-ho fer.

sudo nano /etc/network/interfaces
I afegir:
allow-hotplug usb0
auto usb0
iface usb0 inet static
address 192.168.42.42
netmask 255.255.255.0
network 192.168.42.0
broadcast 192.168.42.255
I reiniciar la RPi.

A partir d’ara ja es pot manipular la raspberry des del mòbil. Connectar el cable usb des del mòbil a la raspberry. Al mòbil, activar l’opció “USB tethering” (amb el meu Motorola Razr m’obliga a desconnectar el wifi per poder-ho activar). Des de l’app ConnectBot o similars connectar-se a pi@192.168.42.42 per control·lar la raspberry. Ídem amb l’app ES File explorer per accedir als fitxers de la raspberry i per exemple veure o baixar les fotos fetes.

Scripts en Python per automatizar les fotos a fer:

Per tal de fer les fotos vaig partir d’un script de www.raspberrypi.org/forums/viewtopic.php?f=43&t=45235 que bàsicament vaig retocar per entrar els paràmetres al executar-lo i no haver d’editar el codi pyhton des del mòbil.

La part de detecció de moviment cal ajustar-la segons les condicions de llum, vent, etc, i requereix fer alguna prova amb diferents paràmetres per què pugui ser més o menys fiable.

Un dels paràmetres de l’aplicació de la raspberry per fer les fotos, raspistill, és el timeout, quan temps triga a fer la foto, en microsegons. Si es posa un valor molt baix com 200 i no hi ha gaire llum les fotos es veuen negres, i aleshores és preferible augmentar el valor a 1000 o més, suposo que per donar temps a auto-enfocar millor. Amb un petit script faig un bucle amb diferents opcions i trio la més adient. Una altra opció podria ser no utilitzar els ajustaments automàtics i definir-los explícitament segons les condicions.

I per crear un vídeo timelapse amb totes les fotos fetes només cal executar un script que s’encarrega de codificar-ho en un fitxer avi.

Descarregar scripts pipcapture, pipmovie i piptest en python

python piptest.py

#!/usr/bin/python
# -*- coding: latin-1 -*-

import os
import time
import subprocess

filepath = "/home/pi/picam"
filenamePrefix = "test"

fixedSettings = "-w 2592 -h 1944 -q 100 -n"
variableSettings  = [
	'-t 200',
	'-t 500',
	'-t 1000',
	'-t 2000',
	'-t 5000'
]
#variableSettings  = [
#	'-t 500 -ISO 100',
#	'-t 500 -ISO 400',
#	'-t 500 -ISO 800'
#]

total_photos = len(variableSettings)
print "Starting photo sequence (%i images)" % (total_photos)

photo_counter = 0
for varSetting in variableSettings:
	photo_counter = photo_counter + 1
	filename = filepath + "/" + filenamePrefix + "_%02d%s.jpg" % (photo_counter, varSetting.replace(' ', ''))
	subprocess.call("raspistill %s %s -o %s" % (fixedSettings, varSetting, filename), shell=True)
	print ' [' + str(photo_counter) + ' of ' + str(total_photos) + '] ' + filename    
	time.sleep(0.25) # Interval between photos (seconds)

print "Finished photo sequence"

python pipcapture.py

#!/usr/bin/python
# -*- coding: latin-1 -*-

# original script by brainflakes, improved by pageauc, peewee2 and Kesthal
# www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=45235
# Retocs per activar/desactivar motion, capturar imatge inicial, beep, ...

# You need to install PIL to run this script
# type "sudo apt-get install python-imaging-tk" in an terminal window to do this

import StringIO
import subprocess
import os
import time
from datetime import datetime
from PIL import Image

def default_input( message, defaultVal ):
	if defaultVal:
		return raw_input( "%s [%s]:" % (message,defaultVal) ) or defaultVal
	else:
		return raw_input( "%s " % (message) )

print "----------------------------- Settings ---------------------------------------"

# INPUT-SETTINGS:

# forceCapture       - whether to force an image to be captured every forceCaptureTime seconds, values True or False
# forceCaptureTime   - seconds between captures (1 hour => 60 * 60) (5 minutes => 60 * 5)
# forceCaptureStart  - wheter to capture an image at start

optTimer = default_input("Capture by Time? (y/n)", "y")
if optTimer in ('y', 'Y', 'yes', 'YES', 's', 'S', 'si', 'SI'):
	forceCapture = True
	forceCaptureTime = int(default_input("Seconds between every capture", 60 * 5))
	forceCaptureStart = True
else:
	forceCapture = False
	forceCaptureTime = 0
	forceCaptureStart = False

# forceMotion        - whether to force motion detection, values True or False
# Threshold          - how much a pixel has to change by to be marked as "changed"
# Sensitivity        - how many changed pixels before capturing an image, needs to be higher if noisy view
# testDelay          - microseconds value for -t parameter at raspistill

optMotion = default_input("Capture by Motion? (y/n)", "y")
if optMotion in ('y', 'Y', 'yes', 'YES', 's', 'S', 'si', 'SI'):
	forceMotion = True
	threshold = int(default_input("Threshold", 50))
	sensitivity = int(default_input("Sensitivity", 50))
	testDelay = int(default_input("Delay for motion detection shooting", 1000))
else:
	forceMotion = False

if not(forceCapture) and not(forceMotion):
	print "Nothing to capture ;-("
	sys.exit()

# filepath           - location of folder to save photos
# filenamePrefix     - string that prefixes the file name for easier identification of files.
# saveWidth, saveHeight - image width & height to save
# saveDelay          - microseconds value for -t parameter at raspistill

filepath = default_input("Path", "/home/pi/picam")
filenamePrefix = default_input("Prefix", "img")
saveWidth = int(default_input("Image Width", 2592))
saveHeight = int(default_input("Image Height", 1944))
saveDelay = int(default_input("Delay for camera shooting", 5000))
extraSettings = default_input("Extra settings for raspistill", "-q 100")  # -ISO 200

# debugLevel         - 0 none. 1 capture & motion dectection msgs. 2 all.
# in debugLevel 2, a file debug.bmp is written to disk with marked changed pixel an with marked border of scan-area (should only be turned on while testing the parameters)

debugLevel = int(default_input("Debug level (0 none. 1 capture msgs. 2 all.", 1))

# END INPUT-SETTINGS:

# Delay per Raspistill
# -t 5000 si condicions de llum dolentes per donar temps a enfocar. -t 1000 pq no trigui tant a fer-se la foto quan hi ha motion.
# -t 1000 si no hi ha massa llum.  -t 200 per detecció més ràpida (si es pilla bé la foto. amb no gaire llum quedaria negra)

# settings of the photos to save
cameraSettings = "-t %i %s" % (saveDelay, extraSettings)

# settings of the photos to detect motion
if (forceMotion):
	testCameraSettings = "-t %i" % (testDelay)

# FIXED-SETTINGS:

# diskSpaceToReserve - Delete oldest images to avoid filling disk. How much byte to keep free on disk.
diskSpaceToReserve = 200 * 1024 * 1024 # Keep 200 mb free on disk

# Test-Image settings
testWidth = 100
testHeight = 75

# this is the default setting, if the whole image should be scanned for changed pixel
testAreaCount = 1
testBorders = [ [[1,testWidth],[1,testHeight]] ]  # [ [[start pixel on left side,end pixel on right side],[start pixel on top side,stop pixel on bottom side]] ]
# testBorders are NOT zero-based, the first pixel is 1 and the last pixel is testWith or testHeight

# with "testBorders", you can define areas, where the script should scan for changed pixel
# for example, if your picture looks like this:
#
#     ....XXXX
#     ........
#     ........
#
# "." is a street or a house, "X" are trees which move arround like crazy when the wind is blowing
# because of the wind in the trees, there will be taken photos all the time. to prevent this, your setting might look like this:

# testAreaCount = 2
# testBorders = [ [[1,50],[1,75]], [[51,100],[26,75]] ] # area y=1 to 25 not scanned in x=51 to 100

# even more complex example
# testAreaCount = 4
# testBorders = [ [[1,39],[1,75]], [[40,67],[43,75]], [[68,85],[48,75]], [[86,100],[41,75]] ]

# END FIXED-SETTINGS

print "------------------------------------------------------------------------------"

# Capture a small test image (for motion detection)
def captureTestImage(settings, width, height):
	command = "raspistill %s -w %s -h %s -e bmp -n -o -" % (settings, width, height)
	imageData = StringIO.StringIO()
	imageData.write(subprocess.check_output(command, shell=True))
	imageData.seek(0)
	im = Image.open(imageData)
	buffer = im.load()
	imageData.close()
	return im, buffer

# Save a full size image to disk
def saveImage(settings, width, height, diskSpaceToReserve):
	keepDiskSpaceFree(diskSpaceToReserve)
	time = datetime.now()
	filename = filepath + "/" + filenamePrefix + "-%04d%02d%02d-%02d%02d%02d.jpg" % (time.year, time.month, time.day, time.hour, time.minute, time.second)
	subprocess.call("raspistill %s -w %s -h %s -e jpg -n -o %s" % (settings, width, height, filename), shell=True)
	if debugLevel > 0:
		print "Captured %s" % filename

# Keep free space above given level
def keepDiskSpaceFree(bytesToReserve):
	if (getFreeSpace() < bytesToReserve):
		for filename in sorted(os.listdir(filepath + "/")):
			if filename.startswith(filenamePrefix) and filename.endswith(".jpg"):
				os.remove(filepath + "/" + filename)
				if debugLevel > 0:
					print "Deleted %s/%s to avoid filling disk" % (filepath,filename)
				if (getFreeSpace() > bytesToReserve):
					return

# Get available disk space
def getFreeSpace():
	st = os.statvfs(filepath + "/")
	du = st.f_bavail * st.f_frsize
	return du

# Get first image
if (forceCaptureStart):
	saveImage(cameraSettings, saveWidth, saveHeight, diskSpaceToReserve)

# Get first comparison image
if (forceMotion):
	image1, buffer1 = captureTestImage(testCameraSettings, testWidth, testHeight)

# Reset last capture time
lastCapture = time.time()

while (True):

	# Count changed pixels
	changedPixels = 0
	takePicture = False

	# Check motion capture
	if (forceMotion):
		# Get comparison image
		image2, buffer2 = captureTestImage(testCameraSettings, testWidth, testHeight)

		if (debugLevel > 1): # in debug mode, save a bitmap-file with marked changed pixels and with visible testarea-borders
			debugimage = Image.new("RGB",(testWidth, testHeight))
			debugim = debugimage.load()

		for z in xrange(0, testAreaCount): # = xrange(0,1) with default-values = z will only have the value of 0 = only one scan-area = whole picture
			for x in xrange(testBorders[z][0][0]-1, testBorders[z][0][1]): # = xrange(0,100) with default-values
				for y in xrange(testBorders[z][1][0]-1, testBorders[z][1][1]):   # = xrange(0,75) with default-values; testBorders are NOT zero-based, buffer1[x,y] are zero-based (0,0 is top left of image, testWidth-1,testHeight-1 is botton right)
					if (debugLevel > 1):
						debugim[x,y] = buffer2[x,y]
						if ((x == testBorders[z][0][0]-1) or (x == testBorders[z][0][1]-1) or (y == testBorders[z][1][0]-1) or (y == testBorders[z][1][1]-1)):
							# print "Border %s %s" % (x,y)
							debugim[x,y] = (0, 0, 255) # in debug mode, mark all border pixel to blue
					# Just check green channel as it's the highest quality channel
					pixdiff = abs(buffer1[x,y][1] - buffer2[x,y][1])
					if pixdiff > threshold:
						changedPixels += 1
						if (debugLevel > 1):
							debugim[x,y] = (0, 255, 0) # in debug mode, mark all changed pixel to green
					# Save an image if pixels changed
					if (changedPixels > sensitivity):
						takePicture = True # will shoot the photo later
					if ((debugLevel < 2) and (changedPixels > sensitivity)):
						break  # break the y loop
				if ((debugLevel < 2) and (changedPixels > sensitivity)):
					break  # break the x loop
			if ((debugLevel < 2) and (changedPixels > sensitivity)):
				break  # break the z loop

		if (debugLevel > 1):
			debugimage.save(filepath + "/debug.bmp") # save debug image as bmp
			print "debug.bmp saved, %s changed pixel" % changedPixels
		# else:
		#     print "%s changed pixel" % changedPixels

		if (debugLevel > 0) and (changedPixels > sensitivity):
			print "\aMotion detected"  # \a to beep at every motion detection (remove if not desired)

		# Swap comparison buffers
		image1 = image2
		buffer1 = buffer2

	# Check force capture
	if forceCapture:
		if time.time() - lastCapture > forceCaptureTime:
			takePicture = True

	if takePicture:
		lastCapture = time.time()
		saveImage(cameraSettings, saveWidth, saveHeight, diskSpaceToReserve)

python pipmovie.py

#!/usr/bin/python
# -*- coding: latin-1 -*-

# You need to install MENCODER to run this script
# type "sudo apt-get install mencoder" in a terminal window

import StringIO
import subprocess
import os

def default_input( message, defaultVal ):
	if defaultVal:
		return raw_input( "%s [%s]:" % (message,defaultVal) ) or defaultVal
	else:
		return raw_input( "%s " % (message) )

# filepath : On hi ha els fitxers jpg a tractar i on es desarà el vídeo
# filenamePrefix : Prefixe que tenen els jpg a tractar i que tindrà el vídeo
# widthHeight : Mida del vídeo. Ex: 640:480  800:600  1024:768  1296:972  1920:1080
# fps : Frames per Segon. Nombre de JPGs en un segon. Ex: 24  12  6  4  3

print "--------- Movie encoder from JPGs ---------"

filepath = default_input("Path", "/home/pi/picam")
filenamePrefix = default_input("Prefix", "img")
widthHeight = default_input("Width:Height", "1024:768")
fps = default_input("FPS", "12")

print "-------------------------------------------"

# calcular aspect radio segons mides enlloc de demanar-lo
ww, hh = widthHeight.split(":")
a0 = float(ww) / float(hh)
a1 = 4.0 / 3.0
a2 = 16.0 / 9.0
aspect = "16/9" if abs(a0 - a1) > abs(a0 - a2) else "4/3"

ordre = "ls " + filepath + "/" + filenamePrefix + "*.jpg > pipmovie.tmp"
exit_status = subprocess.call(ordre, shell=True)

print "Creating video timelapse. This will take a while ..."

ordre = "mencoder -nosound -ovc lavc -lavcopts vcodec=mpeg4:aspect=" + aspect + ":vbitrate=8000000 -vf scale=" + widthHeight + " -o " + filepath + "/" + filenamePrefix + "_timelapse.avi -mf type=jpeg:fps=" + fps + " mf://@pipmovie.tmp"
exit_status = subprocess.call(ordre, shell=True)

print "Created " + filepath + "/" + filenamePrefix + "_timelapse.avi"

exit_status = subprocess.call("rm pipmovie.tmp", shell=True)
M´agrada
1
Aquesta entrada s'ha publicat a Tecnologia i etiquetat amb , , , , . Afegiu a les adreces d'interès l'enllaç permanent.

Deixa un comentari

L'adreça electrònica no es publicarà Els camps necessaris estan marcats amb *