def parse_clip(self, clip, process_background=False): """ Loads a cptv file, and prepares for track extraction. """ self.tracking_time = None start = time.time() clip.set_frame_buffer( self.high_quality_optical_flow, self.cache_to_disk, self.use_opt_flow, self.keep_frames, ) with open(clip.source_file, "rb") as f: reader = CPTVReader(f) clip.set_res(reader.x_resolution, reader.y_resolution) if clip.from_metadata: for track in clip.tracks: track.crop_regions() camera_model = None if reader.model: camera_model = reader.model.decode() clip.set_model(camera_model) # if we have the triggered motion threshold should use that # maybe even override dynamic threshold with this value if reader.motion_config: motion = yaml.safe_load(reader.motion_config) temp_thresh = motion.get("triggeredthresh") if temp_thresh: clip.temp_thresh = temp_thresh video_start_time = reader.timestamp.astimezone(Clip.local_tz) clip.set_video_stats(video_start_time) clip.calculate_background(reader) with open(clip.source_file, "rb") as f: reader = CPTVReader(f) for frame in reader: if not process_background and frame.background_frame: continue self.process_frame(clip, frame.pix, is_affected_by_ffc(frame)) if not clip.from_metadata: self.apply_track_filtering(clip) if self.calc_stats: clip.stats.completed(clip.current_frame, clip.res_y, clip.res_x) self.tracking_time = time.time() - start return True
def main(): args = parse_args() init_logging() config = Config.load_from_file(args.config_file) thermal_config = ThermalConfig.load_from_file(args.thermal_config_file) print("detecting on " + args.cptv) with open(args.cptv, "rb") as f: reader = CPTVReader(f) headers = HeaderInfo( res_x=reader.x_resolution, res_y=reader.y_resolution, fps=9, brand="", model="", frame_size=reader.x_resolution * reader.y_resolution * 2, pixel_bits=16, ) motion_detector = MotionDetector( thermal_config, config.tracking.motion_config.dynamic_thresh, None, headers) for i, frame in enumerate(reader): motion_detector.process_frame(frame)
def test_round_trip_header_defaults(): buf = BytesIO() w = CPTVWriter(buf) w.write_header() w.close() buf.seek(0, 0) r = CPTVReader(buf) assert r.version == 2 assert r.x_resolution == 160 assert r.y_resolution == 120 assert not r.device_name assert not r.device_id assert (datetime.now(tz=timezone.utc) - r.timestamp) < timedelta(minutes=1) assert r.latitude == 0 assert r.longitude == 0 assert r.accuracy == 0 assert r.altitude == 0 assert r.fps == 0 assert r.model is None assert r.brand is None assert r.firmware is None assert r.camera_serial == 0 assert r.background_frames == 0
def test_round_trip_header(): buf = BytesIO() w = CPTVWriter(buf) w.timestamp = datetime(2018, 7, 6, 5, 4, 3, tzinfo=timezone.utc) w.device_name = b"hello" w.device_id = 42 w.latitude = 142.3 w.longitude = -39.2 w.loc_timestamp = datetime(2018, 9, 6, 5, 4, 3, tzinfo=timezone.utc) w.preview_secs = 3 w.motion_config = b"blob" w.accuracy = 20 w.altitude = 200 w.fps = 30 w.model = b"ultra" w.brand = b"laser" w.firmware = b"killer" w.camera_serial = 221 back_frame = random_frame(60, 30) back_frame.background_frame = True w.background_frame = back_frame w.write_header() for i in range(10): frame = random_frame(60, 30) w.write_frame(frame) w.close() buf.seek(0, 0) r = CPTVReader(buf) assert r.version == 2 assert r.x_resolution == 160 assert r.y_resolution == 120 assert r.timestamp == w.timestamp assert r.device_name == w.device_name assert r.device_id == w.device_id assert r.latitude == approx(w.latitude, 0.000001) assert r.longitude == approx(w.longitude, 0.000001) assert r.loc_timestamp == w.loc_timestamp assert r.preview_secs == w.preview_secs assert r.motion_config == w.motion_config assert r.accuracy == w.accuracy assert r.altitude == w.altitude assert r.fps == w.fps assert r.model == w.model assert r.brand == w.brand assert r.firmware == w.firmware assert r.camera_serial == w.camera_serial assert r.background_frames == 1 count = 0 for frame in r: if count == 0: assert frame.background_frame else: assert frame.background_frame == False count += 1 assert count == 11
def test_read_v2(): filename = str(data_dir / "v2.cptv") with open(filename, "rb") as f: r = CPTVReader(f) assert r.version == 2 assert r.device_id == 44 assert r.device_name == b"nz99" assert r.timestamp == datetime(2018, 9, 6, 9, 21, 25, 774768, timezone.utc) assert r.x_resolution == 160 assert r.y_resolution == 120 assert r.preview_secs == 1 assert r.motion_config == b"motion" assert r.latitude == 0 assert r.longitude == 0 assert r.loc_timestamp == 0 assert r.altitude == 0 assert r.fps == 0 assert r.model is None assert r.brand is None assert r.firmware is None assert r.camera_serial == 0 count = 0 for frame in r: count += 1 assert frame.time_on is not None assert frame.last_ffc_time is not None assert frame.pix.min() > 2500 assert frame.pix.max() < 3200 assert count == 100
def test_lat_lon(): filename = str(data_dir / "v2-latlon.cptv") with open(filename, "rb") as f: r = CPTVReader(f) assert r.version == 2 assert r.latitude == approx(-36.943634) assert r.longitude == approx(174.661544)
def test_round_trip_header(): buf = BytesIO() w = CPTVWriter(buf) w.timestamp = datetime(2018, 7, 6, 5, 4, 3, tzinfo=timezone.utc) w.device_name = b"hello" w.device_id = 42 w.latitude = 142.2 w.longitude = -39.2 w.preview_secs = 3 w.motion_config = b"blob" w.write_header() w.close() buf.seek(0, 0) r = CPTVReader(buf) assert r.version == 2 assert r.x_resolution == 160 assert r.y_resolution == 120 assert r.timestamp == w.timestamp assert r.device_name == w.device_name assert r.device_id == w.device_id assert r.latitude == w.latitude assert r.longitude == w.longitude assert r.preview_secs == w.preview_secs assert r.motion_config == w.motion_config
def parse_cptv(cptv_file, config, thermal_config_file, preview_type): with open(cptv_file, "rb") as f: reader = CPTVReader(f) headers = HeaderInfo( res_x=reader.x_resolution, res_y=reader.y_resolution, fps=9, brand=reader.brand.decode() if reader.brand else None, model=reader.model.decode() if reader.model else None, frame_size=reader.x_resolution * reader.y_resolution * 2, pixel_bits=16, serial="", firmware="", ) thermal_config = ThermalConfig.load_from_file(thermal_config_file, headers.model) pi_classifier = PiClassifier( config, thermal_config, headers, thermal_config.motion.run_classifier, 0, preview_type, ) for frame in reader: if frame.background_frame: pi_classifier.motion_detector.background = frame.pix continue frame.received_at = time.time() pi_classifier.process_frame(frame) pi_classifier.disconnected()
def load(self, filename): """ Loads a cptv file, and prepares for track extraction. """ self.source_file = filename self.reader = CPTVReader(open(filename, 'rb')) local_tz = pytz.timezone('Pacific/Auckland') self.video_start_time = self.reader.timestamp.astimezone(local_tz) self.stats.update(self.get_video_stats())
def send_cptv(filename, socket): """ Loads a cptv file, and prepares for track extraction. """ with open(filename, "rb") as f: reader = CPTVReader(f) for i, frame in enumerate(reader): f = np.uint16(frame.pix).byteswap() socket.sendall(f) print("sending frame {}".format(i))
def make_mp4(cptv_file, output_folder, colormap): with open(cptv_file, "rb") as f: reader = CPTVReader(f) # make name and folder for recording mp4_name = reader.timestamp.strftime("%Y%m%d-%H%M%S") + ".mp4" #devicename = reader.devicename devicename = "to-be-added" if devicename == None: devicename = "NO_DEVICE_NAME" else: mp4_name = devicename + "_" + mp4_name mp4_dir = join(output_folder, devicename) os.makedirs(mp4_dir, exist_ok=True) mp4_file = join(mp4_dir, mp4_name) print(mp4_file) FRAME_SCALE = 4.0 NORMALISATION_SMOOTH = 0.95 HEAD_ROOM = 25 auto_min = None auto_max = None mpeg = MPEGCreator(mp4_file) for frame in reader: if (auto_min == None): auto_min = np.min(frame[0]) auto_max = np.max(frame[0]) thermal_min = np.min(frame[0]) thermal_max = np.max(frame[0]) auto_min = NORMALISATION_SMOOTH * auto_min + ( 1 - NORMALISATION_SMOOTH) * (thermal_min - HEAD_ROOM) auto_max = NORMALISATION_SMOOTH * auto_max + ( 1 - NORMALISATION_SMOOTH) * (thermal_max + HEAD_ROOM) # sometimes we get an extreme value that throws off the autonormalisation, so if there are values outside # of the expected range just instantly switch levels if thermal_min < auto_min or thermal_max > auto_max: auto_min = thermal_min auto_max = thermal_max thermal_image = convert_heat_to_img(frame[0], colormap, auto_min, auto_max) thermal_image = thermal_image.resize( (int(thermal_image.width * FRAME_SCALE), int(thermal_image.height * FRAME_SCALE)), Image.BILINEAR) mpeg.next_frame(np.asarray(thermal_image)) mpeg.close()
def update_metadata(recording, api): with open(str(recording["filename"]), "rb") as f: reader = CPTVReader(f) metadata = {} metadata["recordingDateTime"] = reader.timestamp.isoformat() # TODO Add device name when it can be processed on api server # metadata["device_name"] = reader.device_name count = 0.0 for _ in reader: count += 1 metadata["duration"] = round(count / FRAME_RATE) complete = not conf.do_classify api.update_metadata(recording, metadata, complete)
def load(self, source, max_frames=None): """ Load frames from a CPTV file. :param source: source file :param max_frames: maximum number of frames to load, None will load the whole video (default) """ reader = CPTVReader(source) self.frames = [] for i, (frame, offset) in enumerate(reader): self.frames.append(frame.copy()) if max_frames is not None and i >= max_frames: break self.video_start_time = reader.timestamp
def main(): logging.root.removeHandler(absl.logging._absl_handler) absl.logging._warn_preinit_stderr = False init_logging() args = parse_args() config = Config.load_from_file() thermal_config = ThermalConfig.load_from_file() proccesor = None if thermal_config.motion.run_classifier: classifier = get_classifier(config) proccesor = PiClassifier(config, thermal_config, classifier) else: proccesor = MotionDetector( config.res_x, config.res_y, thermal_config, config.tracking.dynamic_thresh, CPTVRecorder(thermal_config), ) if args.cptv: with open(args.cptv, "rb") as f: reader = CPTVReader(f) for frame in reader: proccesor.process_frame(frame) proccesor.disconnected() return service = SnapshotService(proccesor) try: os.unlink(SOCKET_NAME) except OSError: if os.path.exists(SOCKET_NAME): raise sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) sock.bind(SOCKET_NAME) sock.listen(1) while True: logging.info("waiting for a connection") connection, client_address = sock.accept() logging.info("connection from %s", client_address) try: handle_connection(connection, proccesor) finally: # Clean up the connection connection.close()
def load(self, filename): """ Loads a cptv file, and prepares for track extraction. """ self.source_file = filename with open(filename, "rb") as f: reader = CPTVReader(f) local_tz = pytz.timezone('Pacific/Auckland') self.video_start_time = reader.timestamp.astimezone(local_tz) self.preview_secs = reader.preview_secs self.stats.update(self.get_video_stats()) # we need to load the entire video so we can analyse the background. frames = ([frame.pix for frame in reader]) self.frame_buffer.thermal = frames edge = self.config.edge_pixels self.crop_rectangle = Rectangle(edge, edge, reader.x_resolution - 2 * edge, reader.y_resolution - 2 * edge)
def check_frames(frames): buf = BytesIO() w = CPTVWriter(buf) w.write_header() for frame in frames: w.write_frame(frame) w.close() buf.seek(0, 0) r = CPTVReader(buf) count = 0 for in_frame, out_frame in zip(frames, r): assert in_frame == out_frame count += 1 assert count == len(frames)
def parse_clip(self, clip): """ Loads a cptv file, and prepares for track extraction. """ clip.set_frame_buffer( self.config.high_quality_optical_flow, self.cache_to_disk, self.use_opt_flow, self.keep_frames, ) with open(clip.source_file, "rb") as f: reader = CPTVReader(f) clip.set_res(reader.x_resolution, reader.y_resolution) video_start_time = reader.timestamp.astimezone(Clip.local_tz) clip.num_preview_frames = ( reader.preview_secs * clip.frames_per_second - self.config.ignore_frames) clip.set_video_stats(video_start_time) # we need to load the entire video so we can analyse the background. if clip.background_is_preview and clip.num_preview_frames > 0: for frame in reader: self.process_frame(clip, frame.pix, is_affected_by_ffc(frame)) if clip.on_preview(): logging.warn("Clip is all preview frames") if clip.background is None: logging.warn("Clip only has ffc affected frames") return False clip._set_from_background() self._process_preview_frames(clip) else: clip.background_is_preview = False self.process_frames(clip, [frame for frame in reader]) if not clip.from_metadata: self.apply_track_filtering(clip) if self.calc_stats: clip.stats.completed(clip.frame_on, clip.res_y, clip.res_x) return True
def test_round_trip_header_defaults(): buf = BytesIO() w = CPTVWriter(buf) w.write_header() w.close() buf.seek(0, 0) r = CPTVReader(buf) assert r.version == 2 assert r.x_resolution == 160 assert r.y_resolution == 120 assert not r.device_name assert not r.device_id assert (datetime.now(tz=timezone.utc) - r.timestamp) < timedelta(minutes=1) assert r.latitude == 0 assert r.longitude == 0
def parse_cptv(cptv_file, config, thermal_config): with open(cptv_file, "rb") as f: reader = CPTVReader(f) headers = HeaderInfo( res_x=reader.x_resolution, res_y=reader.y_resolution, fps=9, brand="", model="", frame_size=reader.x_resolution * reader.y_resolution * 2, pixel_bits=16, ) processor = get_processor(config, thermal_config, headers) for frame in reader: processor.process_frame(frame) processor.disconnected()
def update_metadata(conf, recording, api): with open(str(recording["filename"]), "rb") as f: reader = CPTVReader(f) metadata = {} metadata["recordingDateTime"] = reader.timestamp.isoformat() if reader.latitude != 0 and reader.longitude != 0: metadata["location"] = (reader.latitude, reader.longitude) if reader.preview_secs: metadata["additionalMetadata"] = {"previewSecs": reader.preview_secs} count = 0 for _ in reader: count += 1 metadata["duration"] = round(count / FRAME_RATE) recording["duration"] = metadata["duration"] complete = not conf.do_classify api.update_metadata(recording, metadata, complete)
def test_read_v1(): filename = str(data_dir / "v1.cptv") with open(filename, "rb") as f: r = CPTVReader(f) assert r.version == 1 assert r.device_name == b"livingsprings03" assert r.timestamp == datetime(2018, 9, 6, 9, 21, 25, 774768, timezone.utc) assert r.x_resolution == 160 assert r.y_resolution == 120 assert r.preview_secs == 0 assert r.motion_config is None count = 0 for frame in r: count += 1 assert frame.time_on is None assert frame.last_ffc_time is None assert count == 100
def main(): args = parse_args() init_logging() config = Config.load_from_file() thermal_config = ThermalConfig.load_from_file() location_config = LocationConfig.load_from_file() res_x = config.res_x res_y = config.res_y print("detecting on " + args.cptv) motion_detector = MotionDetector( res_x, res_y, thermal_config.motion, location_config, thermal_config.recorder, config.tracking.dynamic_thresh, None, ) with open(args.cptv, "rb") as f: reader = CPTVReader(f) for i, frame in enumerate(reader): motion_detector.process_frame(frame)
import sys from cptv import CPTVReader filename = sys.argv[1] with open(filename, "rb") as f: reader = CPTVReader(f) print("version:", reader.version) print("device:", reader.device_name) print("time:", reader.timestamp) print("dims:", reader.x_resolution, reader.y_resolution) print("preview secs:", reader.preview_secs) print("motion config:", reader.motion_config) for frame in reader: print( "t:", frame.time_on, "ffc:", frame.last_ffc_time, "min:", frame.pix.min(), "max:", frame.pix.max(), )
def process_cptv_file(cptv_file, output_folder, copy, delete, colormap): with open(cptv_file, "rb") as f: reader = CPTVReader(f) # make name and folder for recording fileName = reader.timestamp.strftime("%Y%m%d-%H%M%S") raw_name = fileName + ".cptv" mp4_name = fileName + ".mp4" mp4_temp = join(tempfile.gettempdir(), mp4_name) devicename = reader.device_name if not devicename: devicename = "NO_DEVICE_NAME" else: raw_name = devicename + "_" + raw_name mp4_name = devicename + "_" + mp4_name raw_dir = join(output_folder, devicename, "raw-cptv") mp4_dir = join(output_folder, devicename, "cptv-processed") os.makedirs(mp4_dir, exist_ok=True) raw_file = join(raw_dir, raw_name) mp4_file = join(mp4_dir, mp4_name) print(mp4_file) if copy: os.makedirs(raw_dir, exist_ok=True) copy_file(cptv_file, raw_file) FRAME_SCALE = 4.0 NORMALISATION_SMOOTH = 0.95 HEAD_ROOM = 25 auto_min = None auto_max = None mpeg = MPEGCreator(mp4_temp) for frame in reader: if (auto_min == None): auto_min = np.min(frame[0]) auto_max = np.max(frame[0]) thermal_min = np.min(frame[0]) thermal_max = np.max(frame[0]) auto_min = NORMALISATION_SMOOTH * auto_min + ( 1 - NORMALISATION_SMOOTH) * (thermal_min - HEAD_ROOM) auto_max = NORMALISATION_SMOOTH * auto_max + ( 1 - NORMALISATION_SMOOTH) * (thermal_max + HEAD_ROOM) # sometimes we get an extreme value that throws off the autonormalisation, so if there are values outside # of the expected range just instantly switch levels if thermal_min < auto_min or thermal_max > auto_max: auto_min = thermal_min auto_max = thermal_max thermal_image = convert_heat_to_img(frame[0], colormap, auto_min, auto_max) thermal_image = thermal_image.resize( (int(thermal_image.width * FRAME_SCALE), int(thermal_image.height * FRAME_SCALE)), Image.BILINEAR) mpeg.next_frame(np.asarray(thermal_image)) mpeg.close() copy_file(mp4_temp, mp4_file) os.remove(mp4_temp) if copy and delete: os.remove(cptv_file)
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
#!/usr/bin/python3 import sys import pytz from cptv import CPTVReader local_tz = pytz.timezone('Pacific/Auckland') reader = CPTVReader(open(sys.argv[1], "rb")) print(reader.timestamp.astimezone(local_tz)) for i, (frame, offset) in enumerate(reader): print(i, offset, frame.min(), frame.max())