def get_meta_data(self, filename): """ Reads meta-data for a given cptv file. """ source_meta_filename = os.path.splitext(filename)[0] + ".txt" if os.path.exists(source_meta_filename): meta_data = tools.load_clip_metadata(source_meta_filename) tags = set() for record in meta_data["Tags"]: # skip automatic tags if record.get("automatic", False): continue else: tags.add(record["animal"]) tags = list(tags) if len(tags) == 0: tag = "no tag" elif len(tags) == 1: tag = tags[0] if tags[0] else "none" else: tag = "multi" meta_data["primary_tag"] = tag return meta_data else: return None
def process_file(self, filename): start = time.time() base_filename = os.path.splitext(os.path.basename(filename))[0] logging.info(f"processing %s", filename) destination_folder = self._get_dest_folder(base_filename) os.makedirs(destination_folder, mode=0o775, exist_ok=True) # delete any previous files tools.purge(destination_folder, base_filename + "*.mp4") # read metadata metadata_filename = os.path.join(os.path.dirname(filename), base_filename + ".txt") if not os.path.isfile(metadata_filename): logging.error("No meta data found for %s", metadata_filename) return metadata = tools.load_clip_metadata(metadata_filename) valid_tracks = self._filter_clip_tracks(metadata) if not valid_tracks: logging.error("No valid track data found for %s", filename) return clip = Clip(self.track_config, filename) clip.load_metadata( metadata, self.config.load.include_filtered_channel, self.config.load.tag_precedence, ) self.track_extractor.parse_clip(clip) # , self.config.load.cache_to_disk, self.config.use_opt_flow if self.track_config.enable_track_output: self._export_tracks(filename, clip) # write a preview if self.previewer: preview_filename = base_filename + "-preview" + ".mp4" preview_filename = os.path.join(destination_folder, preview_filename) self.previewer.create_individual_track_previews( preview_filename, clip) self.previewer.export_clip_preview(preview_filename, clip) if self.track_config.verbose: num_frames = len(clip.frame_buffer.frames) ms_per_frame = (time.time() - start) * 1000 / max(1, num_frames) self._log_message( "Tracks {}. Frames: {}, Took {:.1f}ms per frame".format( len(clip.tracks), num_frames, ms_per_frame))
def get_meta_data(self, filename): """ Reads meta-data for a given cptv file. """ source_meta_filename = os.path.splitext(filename)[0] + ".txt" if os.path.exists(source_meta_filename): meta_data = tools.load_clip_metadata(source_meta_filename) tags = list(set(record['animal'] for record in meta_data['Tags'])) if len(tags) == 0: tag = 'no tag' elif len(tags) == 1: tag = tags[0] if tags[0] else "none" else: tag = 'multi' meta_data["primary_tag"] = tag return meta_data else: return None
def process_file(self, full_path, **kwargs): """ Extract tracks from specific file, and assign given tag. :param full_path: path: path to CPTV file to be processed :param tag: the tag to assign all tracks from this CPTV files :returns the tracker object """ tag = kwargs["tag"] base_filename = os.path.splitext(os.path.split(full_path)[1])[0] cptv_filename = base_filename + ".cptv" logging.info(f"processing %s", cptv_filename) destination_folder = os.path.join(self.config.tracks_folder, tag.lower()) os.makedirs(destination_folder, mode=0o775, exist_ok=True) # delete any previous files tools.purge(destination_folder, base_filename + "*.mp4") # read additional information from hints file if cptv_filename in self.hints: print(cptv_filename) logging.info(self.hints[cptv_filename]) max_tracks = self.hints[cptv_filename] if max_tracks == 0: return else: max_tracks = self.config.tracking.max_tracks # load the track tracker = TrackExtractor(self.tracker_config) tracker.max_tracks = max_tracks tracker.tag = tag # by default we don't want to process the moving background images as it's too hard to get good tracks # without false-positives. tracker.reject_non_static_clips = True if self.disable_track_filters: tracker.track_min_delta = 0.0 tracker.track_min_mass = 0.0 tracker.track_min_offset = 0.0 tracker.reject_non_static_clips = False if self.disable_background_subtraction: tracker.disable_background_subtraction = True # read metadata meta_data_filename = os.path.splitext(full_path)[0] + ".txt" if os.path.exists(meta_data_filename): meta_data = tools.load_clip_metadata(meta_data_filename) tags = set( [ tag["animal"] for tag in meta_data["Tags"] if "automatic" not in tag or not tag["automatic"] ] ) # we can only handle one tagged animal at a time here. if len(tags) == 0: logging.warning(" - no tags in cptv files, ignoring.") return if len(tags) >= 2: # make sure all tags are the same logging.warning(" - mixed tags, can not process: %s", tags) return tracker.stats["confidence"] = meta_data["Tags"][0].get("confidence", 0.0) tracker.stats["trap"] = meta_data["Tags"][0].get("trap", "none") tracker.stats["event"] = meta_data["Tags"][0].get("event", "none") # clips tagged with false-positive sometimes come through with a null confidence rating # so we set it to 0.8 here. if ( tracker.stats["event"] in ["false-positive", "false positive"] and tracker.stats["confidence"] is None ): tracker.stats["confidence"] = 0.8 tracker.stats["cptv_metadata"] = meta_data else: self.log_warning( " - Warning: no tag metadata found for file - cannot use for machine learning." ) start = time.time() # save some additional stats tracker.stats["version"] = TrackExtractor.VERSION tracker.load(full_path) if not tracker.extract_tracks(): # this happens if the tracker rejected the video for some reason (i.e. too hot, or not static background). # we still need to make a record that we looked at it though. self.database.create_clip(os.path.basename(full_path), tracker) logging.warning(" - skipped (%s)", tracker.reject_reason) return tracker # assign each track the correct tag for track in tracker.tracks: track.tag = tag if self.enable_track_output: self.export_tracks(full_path, tracker, self.database) # write a preview if self.previewer: preview_filename = base_filename + "-preview" + ".mp4" preview_filename = os.path.join(destination_folder, preview_filename) self.previewer.create_individual_track_previews(preview_filename, tracker) self.previewer.export_clip_preview(preview_filename, tracker) if self.tracker_config.verbose: num_frames = len(tracker.frame_buffer.thermal) ms_per_frame = (time.time() - start) * 1000 / max(1, num_frames) self.log_message( "Tracks {}. Frames: {}, Took {:.1f}ms per frame".format( len(tracker.tracks), num_frames, ms_per_frame ) ) return tracker
def process_file(self, full_path, **kwargs): """ Extract tracks from specific file, and assign given tag. :param full_path: path: path to CPTV file to be processed :param tag: the tag to assign all tracks from this CPTV files :param create_preview_file: if enabled creates an MPEG preview file showing the tracking working. This process can be quite time consuming. :returns the tracker object """ tag = kwargs['tag'] base_filename = os.path.splitext(os.path.split(full_path)[1])[0] cptv_filename = base_filename + '.cptv' preview_filename = base_filename + '-preview' + '.mp4' stats_filename = base_filename + '.txt' destination_folder = os.path.join(self.out_folder, tag.lower()) stats_path_and_filename = os.path.join(destination_folder, stats_filename) # read additional information from hints file if cptv_filename in self.hints: max_tracks = self.hints[cptv_filename] if max_tracks == 0: return else: max_tracks = 10 # make destination folder if required try: os.stat(destination_folder) except: self.log_message(" Making path " + destination_folder) os.mkdir(destination_folder) # check if we have already processed this file if self.needs_processing(stats_path_and_filename): print("Processing {0} [{1}]".format(cptv_filename, tag)) else: return # delete any previous files tools.purge(destination_folder, base_filename + "*.mp4") # load the track tracker = TrackExtractor() tracker.max_tracks = max_tracks tracker.tag = tag tracker.verbose = self.verbose >= 2 tracker.high_quality_optical_flow = self.high_quality_optical_flow # by default we don't want to process the moving background images as it's too hard to get good tracks # without false-positives. tracker.reject_non_static_clips = True if self.disable_track_filters: tracker.track_min_delta = 0.0 tracker.track_min_mass = 0.0 tracker.track_min_offset = 0.0 tracker.reject_non_static_clips = False if self.disable_background_subtraction: tracker.disable_background_subtraction = True # read metadata meta_data_filename = os.path.splitext(full_path)[0] + ".txt" if os.path.exists(meta_data_filename): meta_data = tools.load_clip_metadata(meta_data_filename) tags = set([tag['animal'] for tag in meta_data['Tags'] if 'automatic' not in tag or not tag['automatic']]) # we can only handle one tagged animal at a time here. if len(tags) == 0: print(" - Warning, no tags in cptv files, ignoring.") return if len(tags)>= 2: # make sure all tags are the same print(" - Warning, mixed tags, can not process.",tags) return tracker.stats['confidence'] = meta_data['Tags'][0].get('confidence',0.0) tracker.stats['trap'] = meta_data['Tags'][0].get('trap','none') tracker.stats['event'] = meta_data['Tags'][0].get('event','none') # clips tagged with false-positive sometimes come through with a null confidence rating # so we set it to 0.8 here. if tracker.stats['event'] in ['false-positive', 'false positive'] and tracker.stats['confidence'] is None: tracker.stats['confidence'] = 0.8 tracker.stats['cptv_metadata'] = meta_data else: self.log_warning(" - Warning: no metadata found for file.") return start = time.time() # save some additional stats tracker.stats['version'] = CPTVTrackExtractor.VERSION tracker.load(full_path) if not tracker.extract_tracks(): # this happens if the tracker rejected the video for some reason (i.e. too hot, or not static background). # we still need to make a record that we looked at it though. self.database.create_clip(os.path.basename(full_path), tracker) print(" - skipped ({})".format(tracker.reject_reason)) return tracker # assign each track the correct tag for track in tracker.tracks: track.tag = tag if self.enable_track_output: tracker.export_tracks(self.database) # write a preview if self.enable_previews: self.export_mpeg_preview(os.path.join(destination_folder, preview_filename), tracker) time_per_frame = (time.time() - start) / len(tracker.frame_buffer) # time_stats = tracker.stats['time_per_frame'] self.log_message(" -tracks: {} {:.1f}sec - Time per frame: {:.1f}ms".format( len(tracker.tracks), sum(track.duration for track in tracker.tracks), time_per_frame * 1000 )) return tracker
def process_file(self, filename, cache=None, reuse_frames=None): """ Process a file extracting tracks and identifying them. :param filename: filename to process :param enable_preview: if true an MPEG preview file is created. """ base_filename = os.path.splitext(os.path.basename(filename))[0] meta_file = os.path.join(os.path.dirname(filename), base_filename + ".txt") if not os.path.exists(filename): raise Exception("File {} not found.".format(filename)) if not os.path.exists(meta_file): raise Exception("File {} not found.".format(meta_file)) meta_data = tools.load_clip_metadata(meta_file) logging.info("Processing file '{}'".format(filename)) cache_to_disk = ( cache if cache is not None else self.config.classify.cache_to_disk ) start = time.time() clip = Clip(self.config.tracking, filename) clip.set_frame_buffer( self.high_quality_optical_flow, cache_to_disk, self.config.use_opt_flow, True, ) clip.load_metadata( meta_data, self.config.load.tag_precedence, ) frames = [] with open(clip.source_file, "rb") as f: reader = CPTVReader(f) clip.set_res(reader.x_resolution, reader.y_resolution) clip.calculate_background(reader) f.seek(0) for frame in reader: if frame.background_frame: continue clip.add_frame( frame.pix, frame.pix - clip.background, ffc_affected=is_affected_by_ffc(frame), ) predictions_per_model = {} if self.model: prediction = self.classify_clip( clip, self.model, meta_data, reuse_frames=reuse_frames ) predictions_per_model[self.model.id] = prediction else: for model in self.config.classify.models: prediction = self.classify_clip( clip, model, meta_data, reuse_frames=reuse_frames ) predictions_per_model[model.id] = prediction if self.previewer: mpeg_filename = os.path.join( os.path.dirname(filename), base_filename + ".mp4" ) logging.info("Exporting preview to '{}'".format(mpeg_filename)) self.previewer.export_clip_preview( mpeg_filename, clip, list(predictions_per_model.values())[0] ) logging.info("saving meta data %s", meta_file) models = [self.model] if self.model else self.config.classify.models meta_data = self.save_metadata( meta_data, meta_file, clip, predictions_per_model, models, ) if cache_to_disk: clip.frame_buffer.remove_cache() return meta_data
def process_file(self, filename, classifier=None): start = time.time() base_filename = os.path.splitext(os.path.basename(filename))[0] logging.info(f"processing %s", filename) destination_folder = self._get_dest_folder(base_filename) # delete any previous files tools.purge(destination_folder, base_filename + "*.mp4") # read metadata metadata_filename = os.path.join( os.path.dirname(filename), base_filename + ".txt" ) if not os.path.isfile(metadata_filename): logging.error("No meta data found for %s", metadata_filename) return metadata = tools.load_clip_metadata(metadata_filename) if not self.reprocess and self.database.has_clip(str(metadata["id"])): logging.warning("Already loaded %s", filename) return valid_tracks = self._filter_clip_tracks(metadata) if not valid_tracks or len(valid_tracks) == 0: logging.error("No valid track data found for %s", filename) return clip = Clip(self.track_config, filename) clip.load_metadata( metadata, self.config.load.tag_precedence, ) tracker_versions = set( [ t.get("data", {}).get("tracker_version", 0) for t in metadata.get("Tracks", []) ] ) if len(tracker_versions) > 1: logginer.error( "Tracks with different tracking versions cannot process %s versions %s", filename, tracker_versions, ) return tracker_version = tracker_versions.pop() process_background = tracker_version < 10 logging.debug( "Processing background? %s tracker_versions %s", process_background, tracker_version, ) if not self.track_extractor.parse_clip( clip, process_background=process_background ): logging.error("No valid clip found for %s", filename) return # , self.config.load.cache_to_disk, self.config.use_opt_flow if self.track_config.enable_track_output: self._export_tracks(filename, clip) # write a preview if self.previewer: os.makedirs(destination_folder, mode=0o775, exist_ok=True) preview_filename = base_filename + "-preview" + ".mp4" preview_filename = os.path.join(destination_folder, preview_filename) self.previewer.create_individual_track_previews(preview_filename, clip) self.previewer.export_clip_preview(preview_filename, clip) if self.config.verbose: num_frames = len(clip.frame_buffer.frames) ms_per_frame = (time.time() - start) * 1000 / max(1, num_frames) self._log_message( "Tracks {}. Frames: {}, Took {:.1f}ms per frame".format( len(clip.tracks), num_frames, ms_per_frame ) )