changeset 821:26daf35180ad

finished modification and demo script to replay synchronized video (with same frame rate)
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Wed, 22 Jun 2016 16:36:12 -0400
parents e73e7b644428
children 41558145e131
files python/cvutils.py python/metadata.py scripts/play-synced-video.py scripts/play-video.py
diffstat 4 files changed, 80 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/python/cvutils.py	Wed Jun 22 15:23:20 2016 -0400
+++ b/python/cvutils.py	Wed Jun 22 16:36:12 2016 -0400
@@ -146,8 +146,11 @@
         newCameraMatrix[1,2] = newImgSize[1]/2.
         return cv2.initUndistortRectifyMap(intrinsicCameraMatrix, array(distortionCoefficients), identity(3), newCameraMatrix, newImgSize, cv2.CV_32FC1)
 
-    def playVideo(filenames, windowNames = None, firstFrameNum = 0, frameRate = -1, interactive = False, printFrames = True, text = None, rescale = 1., step = 1):
+    def playVideo(filenames, windowNames = None, firstFrameNums = None, frameRate = -1, interactive = False, printFrames = True, text = None, rescale = 1., step = 1):
         '''Plays the video(s)'''
+        if len(filenames) == 0:
+            print('Empty filename list')
+            return
         if windowNames is None:
             windowNames = ['frame{}'.format(i) for i in xrange(len(filenames))]
         wait = 5
@@ -162,9 +165,10 @@
         if array([cap.isOpened() for cap in captures]).all():
             key = -1
             ret = True
-            frameNum = firstFrameNum
-            for cap in captures:
-                cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, firstFrameNum)
+            nFramesShown = 0
+            if firstFrameNums is not None:
+                for i in xrange(len(captures)):
+                    captures[i].set(cv2.cv.CV_CAP_PROP_POS_FRAMES, firstFrameNums[i])
             while ret and not quitKey(key):
                 rets = []
                 images = []
@@ -174,19 +178,18 @@
                     images.append(img)
                 if array(rets).all():
                     if printFrames:
-                        print('frame {0}'.format(frameNum))
-                    #if text is not None:
-                    #   cv2.putText(img, text, (10,50), cv2.FONT_HERSHEY_PLAIN, 1, cvRed)
-                    for i in xrange(1, len(filenames)):
-                        cvImshow(windowNames[i], images[i], rescale)
-                    cvImshow(windowNames[0], images[0], rescale) # cv2.imshow('frame', img)
+                        print('frame shown {0}'.format(nFramesShown))
+                    for i in xrange(len(filenames)):
+                        if text is not None:
+                            cv2.putText(images[i], text, (10,50), cv2.FONT_HERSHEY_PLAIN, 1, cvRed)
+                        cvImshow(windowNames[i], images[i], rescale) # cv2.imshow('frame', img)
                     key = cv2.waitKey(wait)
                     if saveKey(key):
                         cv2.imwrite('image-{}.png'.format(frameNum), img)
-                    frameNum += step
+                    nFramesShown += step
                     if step > 1:
-                        for cap in captures:
-                            cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, frameNum)
+                        for i in xrange(len(captures)):
+                            captures.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, firstFrameNums[i]+nFramesShown)
             cv2.destroyAllWindows()
         else:
             print('Video captures for {} failed'.format(filenames))
--- a/python/metadata.py	Wed Jun 22 15:23:20 2016 -0400
+++ b/python/metadata.py	Wed Jun 22 16:36:12 2016 -0400
@@ -1,9 +1,10 @@
 # from moving import Point
 
-from datetime import datetime
+from datetime import datetime, timedelta
 from os import path
+from math import floor
 
-from sqlalchemy import orm, create_engine, Column, Integer, Float, DateTime, String, ForeignKey, Boolean
+from sqlalchemy import orm, create_engine, Column, Integer, Float, DateTime, String, ForeignKey, Boolean, Interval
 from sqlalchemy.orm import relationship, backref, sessionmaker
 from sqlalchemy.ext.declarative import declarative_base
 
@@ -65,16 +66,18 @@
     resX = Column(Integer)
     resY = Column(Integer)
     frameRate = Column(Float)
+    frameRateTimeUnit = Column(String, default = 's')
     undistort = Column(Boolean)
     intrinsicCameraMatrixStr = Column(String)
     distortionCoefficientsStr = Column(String)
     undistortedImageMultiplication = Column(Float)
     
-    def __init__(self, name, resX, resY, frameRate, trackingConfigurationFilename = None, undistort = None, intrinsicCameraMatrix = None, distortionCoefficients = None, undistortedImageMultiplication = None):
+    def __init__(self, name, resX, resY, frameRate, frameRateTimeUnit = 's', trackingConfigurationFilename = None, undistort = None, intrinsicCameraMatrix = None, distortionCoefficients = None, undistortedImageMultiplication = None):
         self.name = name
         self.resX = resX
         self.resY = resY
         self.frameRate = frameRate
+        self.frameRateTimeUnit = frameRateTimeUnit
         self.undistort = False
 
         if trackingConfigurationFilename is not None:
@@ -92,7 +95,8 @@
             self.undistortedImageMultiplication = undistortedImageMultiplication
 
         # populate the db
-        if self.intrinsicCameraMatrix is not None and self.distortionCoefficients is not None:
+        if hasattr(self, 'intrinsicCameraMatrix') and self.intrinsicCameraMatrix is not None\
+        and hasattr(self, 'distortionCoefficients') and self.distortionCoefficients is not None:
             self.intrinsicCameraMatrixStr = ' '.join('{}'.format(x) for x in self.intrinsicCameraMatrix.flatten('C'))
             self.distortionCoefficientsStr = ' '.join('{}'.format(x)for x in self.distortionCoefficients)
 
@@ -107,6 +111,7 @@
 class CameraView(Base):
     __tablename__ = 'camera_views'
     idx = Column(Integer, primary_key=True)
+    description = Column(String)
     homographyFilename = Column(String) # path to homograph filename, relative to the site name
     siteIdx = Column(Integer, ForeignKey('sites.idx'))
     cameraTypeIdx = Column(Integer, ForeignKey('camera_types.idx'))
@@ -114,9 +119,10 @@
     trackingConfigurationFilename = Column(String) # path to configuration .cfg file, relative to site name
 
     site = relationship("Site", backref=backref('sites', order_by = idx))
-    camera = relationship('CameraType', backref=backref('camera_views', order_by = idx))
+    cameraType = relationship('CameraType', backref=backref('camera_views', order_by = idx))
 
-    def __init__(self, homographyFilename, site, cameraType, trackingConfigurationFilename):
+    def __init__(self, description, homographyFilename, site, cameraType, trackingConfigurationFilename):
+        self.description = description
         self.homographyFilename = homographyFilename
         self.site = site
         self.cameraType = cameraType
@@ -167,8 +173,7 @@
     idx = Column(Integer, primary_key=True)
     name = Column(String) # path relative to the the site name
     startTime = Column(DateTime)
-    duration = Column(Float) # video sequence duration
-    durationUnit = Column(String, default = 's')
+    duration = Column(Interval) # video sequence duration
     databaseFilename = Column(String) # path relative to the the site name
     siteIdx = Column(Integer, ForeignKey('sites.idx'))
     cameraViewIdx = Column(Integer, ForeignKey('camera_views.idx'))
@@ -177,7 +182,8 @@
     cameraView = relationship("CameraView", backref=backref('video_sequences', order_by = idx))
 
     def __init__(self, name, startTime, duration, site, cameraView, databaseFilename = None):
-        'startTime is passed as string in utils.datetimeFormat, eg 2011-06-22 10:00:39'
+        '''startTime is passed as string in utils.datetimeFormat, eg 2011-06-22 10:00:39
+        duration is a timedelta object'''
         self.name = name
         self.startTime = datetime.strptime(startTime, datetimeFormat)
         self.duration = duration
@@ -192,7 +198,16 @@
         else:
             return self.name
 
-
+    def containsInstant(self, instant):
+        'instant is a datetime'
+        return self.startTime <= instant and self.startTime+self.duration
+        
+    def getFrameNum(self, instant):
+        'Warning, there is no check of correct time units'
+        if self.containsInstant(instant):
+            return int(floor((instant-self.startTime).seconds*self.cameraView.cameraType.frameRate))
+        else:
+            return None
 
 # add class for Analysis: foreign key VideoSequenceId, dataFilename, configFilename (get the one from camera view by default), mask? (no, can be referenced in the tracking cfg file)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/play-synced-video.py	Wed Jun 22 16:36:12 2016 -0400
@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+
+import sys, argparse, os.path
+import cvutils, utils
+from metadata import createDatabase, Site, VideoSequence
+from datetime import datetime, timedelta
+
+parser = argparse.ArgumentParser(description='The program displays several views of the same site synchronously.')
+parser.add_argument('-i', dest = 'metadataFilename', help = 'name of the metadata file', required = True)
+parser.add_argument('-n', dest = 'siteId', help = 'site id or site name', required = True)
+parser.add_argument('-t', dest = 'startTime', help = 'time to start playing (format %Y-%m-%d %H:%M:%S, eg 2011-06-22 10:00:39)', required = True)
+parser.add_argument('--fps', dest = 'frameRate', help = 'approximate frame rate to replay', default = -1, type = float)
+parser.add_argument('-r', dest = 'rescale', help = 'rescaling factor for the displayed image', default = 1., type = float)
+parser.add_argument('-s', dest = 'step', help = 'display every s image', default = 1, type = int)
+
+args = parser.parse_args()
+
+session = createDatabase(args.metadataFilename)
+
+if str.isdigit(args.siteId):
+    site = session.query(Site).filter(Site.idx == int(args.siteId)).first()
+else:
+    site = session.query(Site).filter(Site.description.like('%'+args.siteId+'%')).first()
+
+if site is None:
+    print('Site {} was not found in {}. Exiting'.format(args.siteId, args.metadataFilename))
+    sys.exit()
+
+dirname = os.path.split(args.metadataFilename)[0]
+
+startTime = datetime.strptime(args.startTime, utils.datetimeFormat)
+videoSequences = session.query(VideoSequence).filter(VideoSequence.site == site).filter(VideoSequence.startTime <= startTime).all()
+videoSequences = [v for v in videoSequences if v.containsInstant(startTime)]
+filenames = [dirname+os.path.sep+v.getVideoSequenceFilename() for v in videoSequences]
+firstFrameNums = [v.getFrameNum(startTime) for v in videoSequences]
+
+cvutils.playVideo(filenames, [v.cameraView.description for v in videoSequences], firstFrameNums, args.frameRate, rescale = args.rescale, step = args.step)
--- a/scripts/play-video.py	Wed Jun 22 15:23:20 2016 -0400
+++ b/scripts/play-video.py	Wed Jun 22 16:36:12 2016 -0400
@@ -7,7 +7,7 @@
 parser = argparse.ArgumentParser(description='The program displays the video.')
 parser.add_argument('-i', dest = 'videoFilename', help = 'name of the video file', required = True)
 parser.add_argument('-f', dest = 'firstFrameNum', help = 'number of first frame number to display', default = 0, type = int)
-parser.add_argument('--fps', dest = 'frameRate', help = 'approximate frame rate to replay', type = float)
+parser.add_argument('--fps', dest = 'frameRate', help = 'approximate frame rate to replay', default = -1, type = float)
 parser.add_argument('-r', dest = 'rescale', help = 'rescaling factor for the displayed image', default = 1., type = float)
 parser.add_argument('-s', dest = 'step', help = 'display every s image', default = 1, type = int)
 
@@ -17,8 +17,4 @@
 if args.firstFrameNum is not None:
     firstFrameNum = args.firstFrameNum
 
-frameRate = -1
-if args.frameRate is not None:
-    frameRate = args.frameRate
-
-cvutils.playVideo([args.videoFilename], None, firstFrameNum, frameRate, rescale = args.rescale, step = args.step)
+cvutils.playVideo([args.videoFilename], None, [firstFrameNum], args.frameRate, rescale = args.rescale, step = args.step)