# -*- coding: utf-8 -*- import argparse import cProfile import logging import pstats import cv2 from tqdm import tqdm import imgcomparison import mediaoutput import timeline from analyzer import Analyzer from slides import Slide class InfiniteCounter(object): """ InfiniteCounter is a class that represents a counter that will return the next number indefinitely. When the user calls count() return the current number. Then it will increment the current number by the specified steps. """ def __init__(self, start=0, step=1): """ Default Initializer :param start: the starting value of the counter :param step: the amount that should be added at each step """ self.current = start self.step = step def increment(self): self.current += self.step def count(self): """ The count method yields the current number and then increments the current number by the specified step in the default initializer :return: the successor from the previous number """ while True: yield self.current self.current += self.step class Detector(Analyzer): def __init__(self, device, outpath=None, fileformat=".png"): cap = cv2.VideoCapture(sanitize_device(device)) self.sequence = timeline.Timeline(cap) self.writer = mediaoutput.NullWriter() if outpath is not None: self.writer = mediaoutput.TimestampImageWriter(self.sequence.fps, outpath, fileformat) self.comparator = imgcomparison.AbsDiffHistComparator(0.97) def detect_slides(self): frames = [] name_getter = mediaoutput.TimestampImageWriter(self.sequence.fps) with tqdm(total=self.sequence.len, desc='Detecting Slides: ') as pbar: for i, frame in self.check_transition(): if frame is not None: if i % 10 == 0: logging.info(f"{i} Slides detected") frames.append(Slide(name_getter.next_name([i]), frame)) pbar.update(1) self.sequence.release_stream() return frames def check_transition(self): prev_frame = self.sequence.next_frame() self.writer.write(prev_frame, 0) yield 0, prev_frame frame_counter = InfiniteCounter() for frame_count in frame_counter.count(): frame = self.sequence.next_frame() if frame is None: break elif not self.comparator.are_same(prev_frame, frame): while True: if self.comparator.are_same(prev_frame, frame): break prev_frame = frame frame = self.sequence.next_frame() frame_counter.increment() self.writer.write(frame, frame_count) yield frame_count, frame prev_frame = frame yield frame_count, None def analyze(self): for i, frame in self.check_transition(): time = mediaoutput.TimestampImageWriter(self.sequence.fps).next_name([i]) yield Slide(time, frame) def sanitize_device(device): """returns device id if device can be converted to an integer""" try: return int(device) except (TypeError, ValueError): return device if __name__ == "__main__": Parser = argparse.ArgumentParser(description="Slide Detector") Parser.add_argument("-d", "--device", help="video device number or path to video file") Parser.add_argument("-o", "--outpath", help="path to output video file", default="slides/", nargs='?') Parser.add_argument("-f", "--fileformat", help="file format of the output images e.g. '.jpg'", default=".jpg", nargs='?') Args = Parser.parse_args() def run(): detector = Detector(Args.device, Args.outpath, Args.fileformat) detector.detect_slides() cProfile.run('run()', 'profiling_stats.prof') p = pstats.Stats('profiling_stats.prof') p.sort_stats('cumulative').print_stats(10)