0

I am trying to save 10 seconds of buffered video using Python, in particular '.h264' format.

In order to do so, I have been using a PiCamera connected to a Raspberry Pi and the script shown below. The main road block I am facing right now is that instead of saving the file directly to a location [stream.copy_to(str(time)+'.h264')] I would like to save it to a variable in order to perform certain operations (e.g. change video resolution) before finally saving it. Any idea how this can be achieve?

Thanks in advance!

import time
import io
import os 
import picamera
import datetime as dt
from PIL import Image
import cv2 

#obtain current time
def return_currentTime():
    return dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

#trigger event declaration
def motion_detected():
    while True:
        print ("Trigger event(y)?")
        trigger = input ()
        if trigger =="y":
            time = return_currentTime()
            print ("Buffering...")
            stream.copy_to(str(time)+'.h264')           
           
        else: 
           camera.stop_recording()
           break
        
#countdown timer 
def countdown (t):
    while t:
        mins, secs = divmod (t,60)
        timer = '{:02d}:{:02d}'.format(mins, secs)
        print(timer, end="\r")
        time.sleep(1)
        t-=1
    print('Buffer available!')
   

camera = picamera.PiCamera()

camera.resolution = (640, 480)

stream = picamera.PiCameraCircularIO(camera, seconds = 5)

#code will work using h264 as format
camera.start_recording (stream, format = 'h264')
countdown(5)
motion_detected()
Bert
  • 29
  • 5

1 Answers1

0

I don't have a Raspberry Pi, but I have an idea of how you can do it.

    1. I used VideoStream which also supports PiCamera so you can use the below code.
    • stream = VideoStream(usePiCamera=False,
                       resolution=(640, 480),
                       framerate=32).start()
      
      # Wait for a two-second for warming the webcam.
      time.sleep(2.0)
      
    1. Start getting frames
    • while True:
          frame = stream.read()
          countdown(5)
          motion_detected()
      
    1. Modify motion_detected() for saving frames.

      while True:
          frame = stream.read()
          countdown(5)
          motion_detected(frame)
      
    1. Now we need to store frames either using array or dictionary.
    • Dictionary is faster than the array. (source)

    • We need to initialize a global dictionary on top of the project file.

      import time
      import datetime as dt
      from imutils.video import VideoStream
      
      dictionary = {}
      count = 0
      
    • We need to modify the motion_detected method, we start by initializing the input parameter

      # trigger event declaration
      def motion_detected(input_frame):
      
    • Second, we define the global variables inside motion_detected

      # trigger event declaration
      def motion_detected(input_frame):
          global dictionary
          global count
      
    • Unfortunately, VideoStream object has no copy_to attribute, therefore I have to directly assign frame to the dictionary:

      def motion_detected(input_frame):
          global dictionary
          global count
          while True:
              print("Trigger event(y)?")
              trigger = input()
              if trigger == "y":
                  current_time = return_current_time()
                  print("Buffering...")
                  # stream.copy_to(str(current_time) + '.h264')
                  dictionary[count] = input_frame
                  count += 1
                  if count == 10:
                      print("\n10 frames are stored\n")
               else:
                   stream.stop()
           break
      
    1. Now we can perform certain operations like detecting edges.
    • while True:
          frame = stream.read()
          countdown(5)
          motion_detected(frame)
      
          for stored_frame in dictionary.values():
              result = cv2.Canny(image=stored_frame,
                                 threshold1=50,
                                 threshold2=100)
      
    1. Output:
    • enter image description here
    1. Saving the frames
    • To save the frames, you need to enumerate over the stored frames.

      • for count, stored_frame in enumerate(dictionary.values()):
        
    • Then, apply your operation:

      • for count, stored_frame in enumerate(dictionary.values()):
            result = cv2.Canny(image=stored_frame,
                               threshold1=50,
                               threshold2=100)
        
    • Save it to a folder.

      •  for count, stored_frame in enumerate(dictionary.values()):
             result = cv2.Canny(image=stored_frame,
                                threshold1=50,
                                threshold2=100)
        
              cv2.imwrite("output/frame_{}.png".format(count), result)
        
    • If you want to loop through multiple times, the above code won't work. In this case, you need to iniitialize loop above the while loop.

      • counter = 0
        
        while True:
            frame = stream.read()
            countdown(5)
            motion_detected(frame)
        
            for stored_frame in dictionary.values():
                result = cv2.Canny(image=stored_frame,
                                   threshold1=50,
                                   threshold2=100)
        
                cv2.imwrite("output/frame_{}.png".format(counter), result)
             counter += 1
        

Code:


import cv2
import time
import datetime as dt
from imutils.video import VideoStream

dictionary = {}
count = 0


# obtain current time
def return_current_time():
    return dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')


# trigger event declaration
def motion_detected(input_frame):
    global dictionary
    global count
    while True:
        print("Trigger event(y)?")
        trigger = input()
        if trigger == "y":
            current_time = return_current_time()
            print("Buffering...")
            # stream.copy_to(str(current_time) + '.h264')
            dictionary[count] = input_frame
            count += 1
            if count == 10:
                print("\n10 frames are stored\n")
        else:
            stream.stop()
            break


# countdown timer
def countdown(t):
    while t:
        mins, secs = divmod(t, 60)
        timer = '{:02d}:{:02d}'.format(mins, secs)
        print(timer, end="\r")
        time.sleep(1)
        t -= 1
    print('Buffer available!')


stream = VideoStream(usePiCamera=False,
                     resolution=(640, 480),
                     framerate=32).start()

time.sleep(2.0)

counter = 0

while True:
    frame = stream.read()
    countdown(5)
    motion_detected(frame)

    for stored_frame in dictionary.values():
        result = cv2.Canny(image=stored_frame,
                           threshold1=50,
                           threshold2=100)

        cv2.imwrite("output/frame_{}.png".format(counter), result)
        counter += 1
Ahmet
  • 7,527
  • 3
  • 23
  • 47
  • my apologies for the late response. I checked your code and unfortunately no output is being generated. Also, is the video buffer function maintained? I don't think we are keeping it under your code. – Bert Sep 13 '20 at 16:04
  • Did you debug? maybe the frame is coming null. I've tested the code, if you look at the 6th step, I have an example output. – Ahmet Sep 13 '20 at 16:12
  • Yes, I did. I ran it using raspberry pi and pi camera. No outputs whatever... – Bert Sep 13 '20 at 19:50
  • What is the output of the frame? Is it null or image? – Ahmet Sep 13 '20 at 20:11
  • Nada...it outputs nothing :/ – Bert Sep 13 '20 at 23:03