Raspberry Pi Python Open CV Photo Booth - python

I'm working on making a photo booth running on a Raspberry Pi using Python and OpenCV. I found a great code example of how to capture images from a web cam here. http://joseph-soares.com/programacao/python/python-opencv-salvar-imagem-da-camera-no-disco/
The code that is provided alone works perfect on both the Pi and on my Windows PC. When I start adding code to this I'm having issues. I can no longer see what the web cam is seeing on the Pi and on Windows it is hit or miss. They are both capturing pictures though. In Windows it will actually display the image taken in the frame. If I revert back to the original code, again it works just fine.
I'm basically using a loop to give a count down before the picture is taken and flashing a light on the Arduino to represent the digit output that will be added in. I originally thought it was a memory issue on the Pi but it not working on my desktop is making me think otherwise. Any help would be appreciated.
import time, cv
from datetime import datetime
import pyfirmata
import serial
#board = pyfirmata.Arduino('/dev/ttyACM0')
board = pyfirmata.Arduino('COM7')
#arduino.open()
# start an iterator thread so
# serial buffer doesn't overflow
iter8 = pyfirmata.util.Iterator(board)
iter8.start()
greenLED = board.get_pin('d:13:o')
debug = 1
def captureImage():
snapped = cv.QueryFrame(capture)
cv.SaveImage(datetime.now().strftime('%Y%m%d_%Hh%Mm%Ss%f') + '.jpg', snapped)
if __name__ == '__main__':
capture = cv.CaptureFromCAM(0)
cv.NamedWindow('Webcam')
try:
while True:
for n in range(0, 4):
frame = cv.QueryFrame(capture)
cv.ShowImage('Webcam', frame)
cv.WaitKey(10)
if debug: print "count down"
for i in range (0, 5):
print i
greenLED.write(1)
time.sleep(1)
greenLED.write(0)
time.sleep(0.2)
i+=1
greenLED.write(1)
time.sleep(0.2)
print "Say Cheese"
captureImage()
greenLED.write(0)
if debug: print "Waiting for 5 seconds"
time.sleep(5)
n+=1
break
capture = None
cv.DestroyAllWindows()
board.exit()
except KeyboardInterrupt:
cv.DestroyAllWindows()
board.exit()

Related

Screen Blanking/Reducing back light on RPI while running a Kivy App

I have a kivy app that has multiple screens and widgets. I am using a motion sensor to check if there is motion and if there isn't any motion detected for 1 minute then the rpi reduces the screen's backlight or blanks the screen. Im not sure where I should place the rpi code. does it go inside the App class ?
Pir Module that works really well.
import time
from gpiozero import MotionSensor
import board
import adafruit_dht
import subprocess
def turn_on():
CONTROL = "vcgencmd"
CONTROL_UNBLANK = [CONTROL, "display_power", "1"] subprocess.call(CONTROL_UNBLANK)
def turn_off():
CONTROL = "vcgencmd"
CONTROL_BLANK = [CONTROL, "display_power", "0"] subprocess.call(CONTROL_BLANK)
pir = MotionSensor(4)
dhtDevice = adafruit_dht.DHT22(board.D23)
while True:
if pir.motion_detected:
turn_on()
print("Motion Detected!")
try:
#Print the values to the serial port
temperature_c = dhtDevice.temperature
temperature_f = temperature_c * (9 / 5) + 32
humidity = dhtDevice.humidity
print("Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format(temperature_f, temperature_c, humidity))
except RuntimeError as error:
# Errors happen fairly often, keep going
print(error.args[0])
time.sleep(2.0)
continue
except Exception as error:
dhtDevice.exit()
raise error
time.sleep(60.0)
print("sleeping for 1 minute")
else:
turn_off()
print("No Motion Detected!")
So I think I found a proper solution. The idea would be to use threading on the rpi. Its pretty simple to be honest. Here is the link to some code that has a gui but also uses a pir sensor to sense motion and adjust back lighting on the screen.
https://github.com/norrisredhi/kivy/blob/norrisredhi-patch-1/Kivyapp_with_threading_RPI.py

Is there a way to use Python to read and process a camera's frame then save it to a file. Without using libraries such as OpenCV?

This is what I have at the moment but its getting really slow read times. It takes about 6-8 seconds alone just to read the frame with opencv and for my project I need to be able to get a picture at specific intervals as it reads a pressure transducer. Is there a way to make this program faster with cv2 or can is there a way using arrays or what not to do this much quicker.
import cv2
import timeit
def main(): #Define camera function
start = timeit.default_timer() #Starts a runtime timer
hud_Cam = cv2.VideoCapture(0) #Call camera resource
gauge_Cam = cv2.VideoCapture(1)
rval, hud_Img = hud_Cam.read() #Read/Grab camera frame
rval, gauge_Img = gauge_Cam.read()
stop = timeit.default_timer() #Stops a runtime timer
print('Time: ', stop - start) #Calculate runtime timer
start1 = timeit.default_timer() #Starts a runtime timer
hud_name = ('HudPicture0.jpg') #Initialize file names
gauge_name = ('GaugePicture0.jpg')
cv2.imwrite(hud_name, hud_Img) #Write camera frame to file names
cv2.imwrite(gauge_name, gauge_Img)
print("Hud Picture written!") #Log to console
print("Gauge Picture written!")
hud_Cam.release() #Release camera resource to clear memory
gauge_Cam.release()
stop1 = timeit.default_timer() #Stops a runtime timer
print('Time: ', stop1 - start1) #Calculate runtime timer```
As I understand your question, you want to be able to take two images from two cameras as soon as possible after Labview requests it.
On Linux or macOS, I would start capturing continuously as soon as possible and then signal the capturing process using Unix signals. Unfortunately, you are using Windows and signals don't work so well there. So, I am going to use the filesystem to signal - if Labview wants a picture taken, it just creates a file with or without content called capture.txt and that makes the Python process save the current image. There are other more sophisticated methods, but this demonstrates the concept and as you learn more, you may replace the signalling mechanism with a write on a socket, or an MQTT message or something else.
I put the two cameras in two independent threads so they can work in parallel, i.e. faster.
#!/usr/bin/env python3
import cv2
import threading
import logging
from pathlib import Path
def capture(stream, path):
"""Capture given stream and save to file on demand"""
# Start capturing to RAM
logging.info(f'[captureThread]: starting stream {stream}')
cam = cv2.VideoCapture(stream, cv2.CAP_DSHOW)
while True:
# Read video continuously
_, im = cam.read()
# Check if Labview wants it
if CaptureFile.exists():
# Intermediate filename
temp = Path(f'saving-{stream}.jpg')
# Save image with temporary name
cv2.imwrite(str(temp), im)
# Rename so Labview knows it is complete and not still being written
temp.rename(path)
logging.info(f'[captureThread]: image saved')
break
logging.info(f'[captureThread]: done')
if __name__=="__main__":
# Set up logging - advisable when threading
format = "%(asctime)s: %(message)s"
logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
logging.info("[MAIN]: starting")
# Labview will create this file when a capture is required, ensure not there already
CaptureFile = Path('capture.txt')
CaptureFile.unlink(True)
# Create a thread for each camera and start them
HUDthread = threading.Thread(target=capture, args=(0, Path('HUD.jpg')))
Gaugethread = threading.Thread(target=capture, args=(1, Path('Gauge.jpg')))
HUDthread.start()
Gaugethread.start()
# Wait for both to exit
HUDthread.join()
Gaugethread.join()
logging.info("[MAIN]: done")
This got the time down to, Time:2.066412200000059
import timeit
start = timeit.default_timer()
import cv2
hud_Cam = cv2.VideoCapture(0, cv2.CAP_DSHOW) #Call camera resource
gauge_Cam = cv2.VideoCapture(1, cv2.CAP_DSHOW)
def main(hud_Cam, gauge_Cam, start): #Define camera function
while True:
rval, hud_Img = hud_Cam.read() #Read/Grab camera frame
rval, gauge_Img = gauge_Cam.read()
hud_name = ('HudPicture0.jpg') #Initialize file names
gauge_name = ('GaugePicture0.jpg')
cv2.imwrite(hud_name, hud_Img) #Write camera frame to file names
cv2.imwrite(gauge_name, gauge_Img)
print("Hud Picture written!") #Log to console
print("Gauge Picture written!")
hud_Cam.release() #Release camera resource to clear memory
gauge_Cam.release()
stop = timeit.default_timer()
print('Time: ', stop - start) #Calculate runtime timer
break
# =============================================================================
while True:
img1 = cv2.imread(hud_name)
img2 = cv2.imread(gauge_name)
cv2.imshow("Hud Image", img1)
cv2.imshow("Gauge Image", img2)
k = cv2.waitKey(60)
if k%256 == 27:
cv2.destroyAllWindows()
break
main(hud_Cam, gauge_Cam, start) #Call camera function```

Python - Multiprocessing with function

I am fairly new to Python and Computer Vision but I've managed to setup a basic script to open my CCTV camera's at home. It works great but I have a slight issue is is that it loops over each camera one after the other, so instead of the camera image updating every second, it updates around every 5 seconds - the time it takes to complete a loop.
What I want is to be able to use multiprocessing / multi threading so that when I call the function for each of my cameras it open ins a new pool, running parallel to each other, therefore updating each camera every second.
As you can see in the below code I call each of the cameras using the same function, but with different argurments. I've read up on Process and have tried a few variations but I don't seem to be able to get it working.
I'm sure its a simple one and it would be great if someone could point me in the right direction.
Here's the code:
# import libraries
from threading import Thread
import imutils
import cv2, time
import argparse
import numpy as np
import datetime
from imutils.video import WebcamVideoStream
from multiprocessing import Process
stream = WebcamVideoStream('rtsp://mylink1./' ).start() # To open any valid video file stream/network stream. In this case 'test.mp4' file.
stream2 = WebcamVideoStream('rtsp://mylink2./' ).start()
stream3 = WebcamVideoStream('rtsp://mylink3./' ).start()
stream4 = WebcamVideoStream('rtsp://mylink4./' ).start()
stream5 = WebcamVideoStream('rtsp://mylink5./' ).start()
def checkimage(stream,camname):
global lastmessage
try:
frame = stream.read()
(h, w) = frame.shape[:2]
cv2.imshow(camname, frame)
print('[INFO]Checked ' + str(camname) + ' at ' + datetime.datetime.now().strftime("%H:%M:%S") + '...')
except AttributeError:
pass
# infinite loop
while True:
checkimage(stream,"Back Door Camera")
checkimage(stream2,"Conservatory Camera")
checkimage(stream3,"Front Door Camera")
checkimage(stream4,"Garage Camera")
checkimage(stream5,"Shed Camera")
key = cv2.waitKey(1) & 0xFF
# check for 'q' key-press
if key == ord("q"):
if 'q' key-pressed break out
break
cv2.destroyAllWindows()
# close output window
stream.stop()
# safely close video stream.
Thanks in advance!
Chris

Is there a better way to trap key, Cttl-c and kill in threaded Python OpenCV program?

I have a python 3.7 and OpenCV 3 programming running on windows 10 and raspberry pi. The program has three threads. The first thread is a main loop, the second thread is a camera, and the third thread is writing videos and images.
I searched many references and put together what I think is a way to catch the correct "stop" keyboard signals. I tested on win10 and RPI using the threading packages and it appears to work. I am wondering if there is a more approriate way to handle keyboard input? I had to put the keyboard handler in main as I wasn't able to catch it in the wile loop. The following example doesn't show threading but in my larger program it worked also.
import signal
import cv2
class main():
def __init__(self):
self.stop_by_signal = False
return
def signal_term_handler(self,signal, frame):
self.stop_by_signal = True
return
def run(self):
signal.signal(signal.SIGTERM, self.signal_term_handler)
self.cap = cv2.VideoCapture(0)
print("starting")
while True:
# read cameras and display
ret, frame = self.cap.read()
if not ret: break
cv2.imshow('frame',frame)
# look for kill, ctl-c or cv2.waitkey to stop
if self.stop_by_signal == True:
print("main: stopping loop via kill")
break
key = cv2.waitKey(1) & 0xFF
if key == ord('q') or key == 27:
print("main: cv2 esc or q entered to stop")
break
elif key != 255:
print('key: %s' % [chr(key)])
self.cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
try:
main = main()
main.run()
except KeyboardInterrupt:
main.cap.release()
print("__main:__ ctl-c entered to stop.")
Looks like it is working well. For the kill command, it works without the -9 option.

Python OpenCV: Returning cvBridge Image from ROS

I recently just started OpenCv so apologies for any dumb questions.
So my ultimate aim is to stream video from my ZED camera on to a VR headset & I've not had any luck in Unity or UnReal as relevant plugins fail to work on my Linux machine. I need to be able to isolate the dual cameras from my ZED device but right now only ROS allows me to access the individual topics of either cam.
So I used a ZED wrapper to publish image data on to a ROS node and found code to interact with ROS messages from here.
The code works perfectly and I am able to display a stream of images captured on my monitor. But my issue is how do I basically save these images into a buffer/queue?
I modified the example to code to try to return the images converted by cvBridge but I'm not having any luck getting the returned image to show on screen. Think it may be because Image is initialised to None at first and so cv2.imshow() cannot display an empty picture. But how do I check if the rest of the images are being returned correctly? Here is my code:
import cv2
import rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError
from Queue import Queue
class ImageConverter(object):
def __init__(self, object):
self.topic=object
self.bridge= CvBridge()
self.image_queue = Queue(maxsize=100)
self.image_sub = rospy.Subscriber(self.topic, Image, self.callback, queue_size=100)
self.image=None
def callback(self, data):
try:
cv_image=self.bridge.imgmsg_to_cv2(data, "bgr8")
except CvBridgeError as e:
print(e)
else:
self.image = cv_image
self.image_queue.put(cv_image)
def get_image(self):
try:
image = self.image_queue.get(block=False)
except:
image = self.image
cv2.imshow("Image window", image)
cv2.waitKey(3)
def subscribe(position):
ic= ImageConverter(position)
ic.get_image()
rospy.init_node('image_converter', anonymous=True)
try:
rospy.spin()
except KeyboardInterrupt:
print ("goodbye")
cv2.destroyAllWindows()
I have had such a difficult time trying to figure out how to do this all so any help would be very much appreciated. Thanks!
Your call back function has the following code:
try:
cv_image=self.bridge.imgmsg_to_cv2(data, "bgr8")
except CvBridgeError as e:
print(e)
else:
self.image = cv_image
self.image_queue.put(cv_image)
I think your else statement is a typo and may not be getting executed.

Categories