def getTimecodeSync(self, timecode, tcAttrName, attrs, fps, timecodeFps, timecodeMultiplier, offset=0): tcSyncTime = None if timecode is not None and tcAttrName in attrs and attrs[tcAttrName]: tcSyncTime = self.attr('timecode', atLocation=attrs[tcAttrName]) if tcSyncTime is not None: tcSyncValue = Timecode.TCFtoInt(tcSyncTime, fps) try: diff = Timecode.TCSub(tcSyncTime, timecode, timecodeFps) offset += Timecode.TCFtoInt(diff, timecodeFps) * timecodeMultiplier except Exception as e: self.logger.error('Error calculating timecode difference: %s' % str(e)) return tcSyncTime, -1 # import traceback # traceback.print_exc() return tcSyncTime, offset
def initialise(self, interface, attrs): directory = self.resolvePath(attrs['directory']) if not directory: return False prefix = attrs['prefix'] prefixFilename = self.resolvePath(attrs['prefixFilename']) if prefix and not prefixFilename: return calibration = attrs['calibration'] calibrationFilename = self.resolvePath(attrs['calibrationFilename']) calibrationLocation = self.resolvePath(attrs['calibrationLocation']) if calibration and (not calibrationFilename and not calibrationLocation): return False movieFilenames = [] try: for file in os.listdir(directory): if prefixFilename and not file.startswith(prefixFilename): continue if file.endswith('.avi') or file.endswith( '.mov') or file.endswith('mp4'): movieFilenames.append(os.path.join(directory, file)) except WindowsError as e: self.logger.error('Could not find videos: % s' % str(e)) if not movieFilenames: # TODO: Here we'll have to clear the cameras etc. return False # Windows will produce a wonky order, i.e. 1, 10, 11, .., 2, 3, .. # Use natural sorting to rectify movieFilenames.sort(key=self.alphaNumKey) self.camera_ids = [] self.camera_names = [] self.movies = [] self.mats = [] vheights = [] vwidths = [] timecodes = [] hasTimecode = False useTimecode = attrs['useTimecode'] if 'useTimecode' in attrs else True offset = attrs['offset'] if 'offsets' in attrs and attrs['offsets']: offsets = eval(attrs['offsets']) else: offsets = [offset] * len(movieFilenames) for ci, mf in enumerate(movieFilenames): self.logger.info('Loading MovieReader: %s' % mf) movieData = MovieReader.open_file(mf, audio=False, frame_offset=offsets[ci]) if movieData['vbuffer'] is not None: self.movies.append(movieData) self.timecodeOffsets.append(0) if 'timecode' in movieData and movieData['timecode']: hasTimecode = True timecodes.append(movieData['timecode']) # Make sure we have all the cameras before continuing if len(self.movies) != len(movieFilenames): self.logger.error('Could not load all movies in sequence') return # Make sure we have as many time codes as movies (if we have any) if hasTimecode and len(self.movies) != len(timecodes): self.logger.error('Not all movie files have a time code') return # See if we can get the offsets using the time codes if hasTimecode and useTimecode: print 'Video timecodes:', timecodes fps_all = [round(m['fps']) for m in self.movies] print 'FPS:', fps_all timecodeValues = [ Timecode.TCFtoInt(tc, fps) for tc, fps in zip(timecodes, fps_all) ] tcOrderDesc = [ timecodes.index(tc) for tc in sorted(timecodes, reverse=True) ] # Set the first offset to 0 firstTcIndex = tcOrderDesc[0] self.timecodeOffsets[firstTcIndex] = 0 largestTc = timecodes[firstTcIndex] offsetStartIndex = 1 # We can also get the timecode destination from an incoming location, e.g. 2D detections if 'timecodeLocation' in attrs and attrs['timecodeLocation']: tcSyncTime = interface.attr( 'timecode', atLocation=attrs['timecodeLocation']) if tcSyncTime is not None: tcSyncValue = Timecode.TCFtoInt(tcSyncTime, fps_all[0]) if tcSyncValue < timecodeValues[firstTcIndex]: self.logger.error( 'Sync timecode %s is smaller than video timecodes (%s).' % (tcSyncTime, largestTc)) return largestTc = tcSyncTime offsetStartIndex = 0 self.timecode = largestTc self.logger.info('Setting timecode to: %s' % (largestTc)) # Calculate the offset for each camera to get it up to speed with the target timecode # TODO: Replace hard coded timecode fps and multiplier timecodeFps, timecodeMultiplier = 25., 2. for tcInd in tcOrderDesc[offsetStartIndex:]: diff = Timecode.TCSub(largestTc, timecodes[tcInd], timecodeFps) self.timecodeOffsets[tcInd] = Timecode.TCFtoInt( diff, timecodeFps) * timecodeMultiplier if self.timecodeOffsets: print 'Video timecode offsets:', self.timecodeOffsets self.camera_ids = [ 'Camera %d' % ci for ci in xrange(len(movieFilenames)) ] self.movies = self.movies if not calibrationLocation: calibrationLocation = interface.root() if calibrationFilename or interface.hasAttr( 'mats', atLocation=calibrationLocation): if calibrationFilename: # TODO: Detect filetype, e.g. .cal and .xcp and handle accordingly try: self.mats, rawCalData = OptitrackReader.load_CAL( calibrationFilename) if not self.mats: return False except IOError as e: self.logger.error('Could not load calibration file: %s' % str(e)) return False else: self.mats = interface.attr('mats', atLocation=calibrationLocation) if not self.mats: self.logger.error('Could not find calibration mats: %s' % calibrationLocation) return False else: from GCore import Calibrate for ci, (cid, md) in enumerate(zip(self.camera_ids, self.movies)): if md is not None: self.mats.append( Calibrate.makeUninitialisedMat( ci, (md['vheight'], md['vwidth']))) for md in self.movies: vheights.append(md['vheight']) vwidths.append(md['vwidth']) Ps = interface.getPsFromMats(self.mats) self.attrs = { 'vheight': vheights, 'vwidth': vwidths, 'camera_ids': self.camera_ids, 'Ps': Ps, 'mats': self.mats, 'colour': eval(attrs['colour']) } if self.camera_names: self.attrs['camera_names'] = self.camera_names self.initialised = True return True