From 4bf4c483a457c30336cfd247db988b1d7bc25e66 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Sat, 22 Oct 2016 07:50:06 +0000 Subject: [PATCH] - improved PiCameraPipeline (added names for procs, better thread mgmt) - added VideoSource to piCameraPipeline git-svn-id: http://moon:8086/svn/software/trunk/projects/opencv@330 b431acfa-c32f-4a4a-93f1-934dc6c82436 --- lasertrack.py | 53 ++------------------ piCameraPipeline/PiCameraPipeline.py | 73 ++++++++++++++++++---------- piCameraPipeline/VideoSource.py | 51 +++++++++++++++++++ picam_test.py | 12 ++--- 4 files changed, 108 insertions(+), 81 deletions(-) create mode 100755 piCameraPipeline/VideoSource.py diff --git a/lasertrack.py b/lasertrack.py index c39338c..8fb84c4 100755 --- a/lasertrack.py +++ b/lasertrack.py @@ -21,6 +21,7 @@ from picamera import PiCamera from piCameraPipeline import PiCameraPipeline from piCameraPipeline.PiCameraPipeline import RgbProcess from piCameraPipeline.PiCameraPipeline import RgbProcessorAdapter +from piCameraPipeline.VideoSource import VideoSource width = 320 height = 240 @@ -42,52 +43,9 @@ framerate = args["framerate"] seconds = args["seconds"] with_ev3 = args["with_ev3"] -class VideoSource(RgbProcessorAdapter): - def __init__(self, fileName, resolution, framerate): - super(VideoSource, self).__init__(resolution) - self.fileName = fileName - self.resolution = resolution - self.framerate = framerate - self.thread = None - self.camera = None - self.cancel = False - if fileName == 'piCamera': - self.camera = PiCamera() - self.camera.resolution = (self.resolution[0], self.resolution[1]) - self.camera.framerate = self.framerate - else: - self.needRgbConversion = False - self.thread = Thread(target=self.fileReadThread) - - def fileReadThread(self): - pass - - def start(self): - if self.thread is not None: - self.thread.start() - pass - else: - self.camera.start_recording(self, format="bgr") - - def stop(self): - if self.thread is not None: - self.cancel = True - self.thread.join() - else: - self.camera.stop_recording() - - def fileReadThread(self): - cap = cv2.VideoCapture(self.fileName) - while not self.cancel: - ret, frame = cap.read() - if ret: - self.write(frame) - time.sleep(1.0/self.framerate) - - class MyRgbProcess(RgbProcess): - def __init__(self, brick, resolution=(320, 240, 3), numEntries=2, next=None): - super(MyRgbProcess, self).__init__(resolution, numEntries, next) + def __init__(self, name, resolution=(320, 240, 3), numEntries=2, next=None, brick=None): + super(MyRgbProcess, self).__init__(name, resolution, numEntries, next) self.brick = brick def process(self): @@ -145,7 +103,7 @@ if with_ev3: with ev3.EV3() as brick: videoSource = VideoSource(videoFile, (width,height,3), framerate) - proc = MyRgbProcess(brick, (240, 320, 3), 4) + proc = MyRgbProcess('MyRgbProcess', (240, 320, 3), 4, brick) videoSource.processorAdd(proc) videoSource.start() @@ -155,14 +113,13 @@ if with_ev3: proc.stop() else: videoSource = VideoSource(videoFile, (width,height,3), framerate) - proc = MyRgbProcess(None, (240, 320, 3), 4) + proc = MyRgbProcess('MyRgbProcess', (240, 320, 3), 4) videoSource.processorAdd(proc) videoSource.start() print("[INFO] sampling THREADED frames from `" + videoFile + "` at " + str(framerate) + " frame/s") time.sleep(seconds) videoSource.stop() - proc.stop() # do a bit of cleanup cv2.destroyAllWindows() diff --git a/piCameraPipeline/PiCameraPipeline.py b/piCameraPipeline/PiCameraPipeline.py index 3a079d6..87a9d47 100755 --- a/piCameraPipeline/PiCameraPipeline.py +++ b/piCameraPipeline/PiCameraPipeline.py @@ -60,8 +60,9 @@ class Fifo(object): return data class RgbProcess(Fifo): - def __init__(self, resolution=(320, 240, 3), numEntries=2, next=None): + def __init__(self, name='RgbProcess', resolution=(320, 240, 3), numEntries=2, next=None): super(RgbProcess, self).__init__((numEntries, ) + resolution) + self.name = name self.next = next self.thread = Thread(target=self.run) self.cancel = False @@ -74,9 +75,10 @@ class RgbProcess(Fifo): # called on first frame def onFirstFrame(self): - self.thread.start() - if self.next is not None: - self.next.onFirstFrame() + self.start() + + def onLastFrame(self): + self.stop() # may be overriden to process in caller context def onData(self, data): @@ -84,31 +86,46 @@ class RgbProcess(Fifo): # thread main loop def run(self): - self.fps = FPS().start() + self.started() while not self.cancel: self.process() self.frames += 1 + self.stopped() + + def started(self): + print(self.name + ": started()") + self.fps = FPS().start() + if self.next is not None: + self.next.onFirstFrame() + + def stopped(self): + self.fps.stop() + print(self.name + ": stopped()") + print(self.name + ": Processed " + str(self.frames) + " frames") + print(self.name + ": FPS = " + str(self.frames/self.fps.elapsed())) + # abstractmethod def process(self): pass + def start(self): + if not self.thread.is_alive(): + self.thread.start() + # should be called to exit thread def stop(self): - self.fps.stop() - print("Processed " + str(self.frames) + " frames") - print("FPS = " + str(self.frames/self.fps.elapsed())) - self.cancel = True - self.thread.join() - if self.next is not None: - self.next.stop() + if self.thread.is_alive(): + if self.next is not None: + self.next.stop() + self.cancel = True + self.thread.join() class RgbProcessorAdapter(object): def __init__(self, resolution=(320, 240, 3)): self.resolution = resolution self.frameSize = resolution[0]*resolution[1]*resolution[2] self.size = 0 - self.frames = 0 self.fps = None self.processors = [] self.needRgbConversion = True @@ -121,17 +138,14 @@ class RgbProcessorAdapter(object): return True def write(self, data): + if data is None: + return + if self.size == 0: for proc in self.processors: proc.onFirstFrame() self.fps = FPS().start() - - if data is None: - return - - self.size += len(data) - self.frames += len(data)/self.frameSize res = (self.resolution[0], self.resolution[1]) @@ -140,6 +154,8 @@ class RgbProcessorAdapter(object): else: rgbData = data + self.size += rgbData.size + for proc in self.processors: proc.onData(rgbData) @@ -149,16 +165,19 @@ class RgbProcessorAdapter(object): def flush(self): if self.size > 0: self.fps.stop() - print("Recorded " + str(self.frames) + " frames (" + str(self.size) + " bytes)") - print("FPS = " + str(self.frames/self.fps.elapsed())) + frames = self.size/self.frameSize + print("Recorded " + str(frames) + " frames (" + str(self.size) + " bytes)") + print("FPS = " + str(frames/self.fps.elapsed())) + for proc in self.processors: + proc.onLastFrame() if ("__main__" == __name__): import cv2 # Unit test with to threaded processors class MyRgbProcess1(RgbProcess): - def __init__(self, resolution=(320, 240, 3), numEntries=2, next=None): - super(MyRgbProcess1, self).__init__(resolution, numEntries, next) + def __init__(self, name, resolution=(320, 240, 3), numEntries=2, next=None): + super(MyRgbProcess1, self).__init__(name, resolution, numEntries, next) def process(self): data = self.read(1.0) @@ -173,8 +192,8 @@ if ("__main__" == __name__): self.next.write(cv2.cvtColor(gray_blurred,cv2.COLOR_GRAY2BGR)) class MyRgbProcess2(RgbProcess): - def __init__(self, resolution=(320, 240, 3), numEntries=2, next=None): - super(MyRgbProcess2, self).__init__(resolution, numEntries, next) + def __init__(self, name, resolution=(320, 240, 3), numEntries=2, next=None): + super(MyRgbProcess2, self).__init__(name, resolution, numEntries, next) def process(self): data = self.read(1.0) @@ -191,8 +210,8 @@ if ("__main__" == __name__): camera.resolution = (320, 240) camera.framerate = 60 frameDim = (camera.resolution[0], camera.resolution[1], 3) - myProcessor2 = MyRgbProcess2((240, 320, 3), 4) - myProcessor1 = MyRgbProcess1((240, 320, 3), 4, myProcessor2) + myProcessor2 = MyRgbProcess2('Process 2', (240, 320, 3), 4) + myProcessor1 = MyRgbProcess1('Process 1', (240, 320, 3), 4, myProcessor2) myRgbProcessorAdapter = RgbProcessorAdapter(frameDim) myRgbProcessorAdapter.processorAdd(myProcessor1) camera.start_recording(myRgbProcessorAdapter, format="bgr") diff --git a/piCameraPipeline/VideoSource.py b/piCameraPipeline/VideoSource.py new file mode 100755 index 0000000..f82f338 --- /dev/null +++ b/piCameraPipeline/VideoSource.py @@ -0,0 +1,51 @@ +import time +import cv2 +from threading import Thread +from picamera import PiCamera +from piCameraPipeline.PiCameraPipeline import RgbProcessorAdapter + +class VideoSource(RgbProcessorAdapter): + def __init__(self, fileName, resolution, framerate): + super(VideoSource, self).__init__(resolution) + self.fileName = fileName + self.resolution = resolution + self.framerate = framerate + self.thread = None + self.camera = None + self.cancel = False + if fileName == 'piCamera': + self.camera = PiCamera() + self.camera.resolution = (self.resolution[0], self.resolution[1]) + self.camera.framerate = self.framerate + else: + self.needRgbConversion = False + self.thread = Thread(target=self.fileReadThread) + + def fileReadThread(self): + pass + + def start(self): + if self.thread is not None: + if not self.thread.is_alive(): + self.thread.start() + else: + self.camera.start_recording(self, format="bgr") + + def stop(self): + if self.thread is not None: + if self.thread.is_alive(): + self.cancel = True + self.thread.join() + else: + self.camera.stop_recording() + + def fileReadThread(self): + cap = cv2.VideoCapture(self.fileName) + while not self.cancel: + ret, frame = cap.read() + if ret: + self.write(frame) + else: + break + time.sleep(1.0/self.framerate) + self.flush() diff --git a/picam_test.py b/picam_test.py index 9875ac9..25301ae 100755 --- a/picam_test.py +++ b/picam_test.py @@ -10,8 +10,8 @@ if ("__main__" == __name__): # Unit test with to threaded processors class MyRgbProcess1(RgbProcess): - def __init__(self, resolution=(320, 240, 3), numEntries=2, next=None): - super(MyRgbProcess1, self).__init__(resolution, numEntries, next) + def __init__(self, name, resolution=(320, 240, 3), numEntries=2, next=None): + super(MyRgbProcess1, self).__init__(name, resolution, numEntries, next) def process(self): data = self.read(1.0) @@ -26,8 +26,8 @@ if ("__main__" == __name__): self.next.write(cv2.cvtColor(gray_blurred,cv2.COLOR_GRAY2BGR)) class MyRgbProcess2(RgbProcess): - def __init__(self, resolution=(320, 240, 3), numEntries=2, next=None): - super(MyRgbProcess2, self).__init__(resolution, numEntries, next) + def __init__(self, name, resolution=(320, 240, 3), numEntries=2, next=None): + super(MyRgbProcess2, self).__init__(name, resolution, numEntries, next) def process(self): data = self.read(1.0) @@ -44,8 +44,8 @@ if ("__main__" == __name__): camera.resolution = (320, 240) camera.framerate = 60 frameDim = (camera.resolution[0], camera.resolution[1], 3) - myProcessor2 = MyRgbProcess2((240, 320, 3), 4) - myProcessor1 = MyRgbProcess1((240, 320, 3), 4, myProcessor2) + myProcessor2 = MyRgbProcess2('Process 2', (240, 320, 3), 4) + myProcessor1 = MyRgbProcess1('Process 1', (240, 320, 3), 4, myProcessor2) myRgbProcessorAdapter = RgbProcessorAdapter(frameDim) myRgbProcessorAdapter.processorAdd(myProcessor1) camera.start_recording(myRgbProcessorAdapter, format="bgr")