def start(self): signals.do_log('Processing...') from rvsearch.main import CoreProcess in_path = self.csvpath_input.text() # Path for csv input out_path = self.csvpath_output.text() # Path for csv output results = CoreProcess().main([in_path], out_path) return results
def change_path(self): """Change working directory to a temporary directory.""" if vconf.VERBOSE: signals.do_log(f'Running from {self.file_path}') Path(Path(self.file_path) / "rvidtmp/").mkdir(parents=True, exist_ok=True) os.chdir(self.file_path + "/rvidtmp") return None
def get_video(self, url) -> list[str, str]: """Gets the video infor from YouTube and then downloads it""" url = str(url) # TODO: if above 30min id, name, channel = self._get_info(url) if Path(id).exists(): if not vconf.QUIET: signals.do_log('** Skipping download, file already exists') else: response = self.download(url) return [id, name, channel, url]
def save_results(self, record_df, output, name): # TODO: maybe save in comparing? signals.do_log('Saving...') home_path = os.path.expanduser('~') if output: save_path = os.path.join(home_path, 'Documents', f'{output}.csv') else: save_path = os.path.join(home_path, 'Documents', f'{name}_results.csv') final_csv_name = save_csv(record_df, f'{save_path}') if not vconf.QUIET: signals.do_log(f'Results saved to {final_csv_name}') return None
def compare_videos_parallel(self, source_frames, source_fps, target_frames, target_fps): """Slice the frames to equal parts and multiprocess-compare them""" pool = mp.Pool() cpus = mp.cpu_count() n = int(len(source_frames) / cpus) subs = [ source_frames[i:i + n] for i in range(0, len(source_frames), n) ] args = [(sub, source_fps, target_frames, target_fps) for sub in subs] if vconf.VERBOSE: signals.do_log(f'you\'re having {cpus} process simultaneously') results = pool.starmap(self.compare_videos, args) ret = [] for res in results: if res: ret.append(res) return ret
def video_init(self, vid_info): """Extract the frames from video and get all the infos from downloader""" vid_meta = { 'path': vid_info[0], 'name': vid_info[1], 'channel': vid_info[2], 'url': vid_info[3] } vid = cv2.VideoCapture(vid_meta['path']) # TODO: GPU accelrate fps = vid.get(cv2.CAP_PROP_FPS) vid_meta['fps'] = fps # Save frames into RAM if not vconf.QUIET: signals.do_log( f"Loading video: {vid_meta['path']} -- {vid_meta['name']}") start = monotonic() frames = self.get_frames(vid, int(fps)) if vconf.VERBOSE: signals.do_log(f'Loading took {monotonic() - start} seconds') return frames, vid_meta
def record_similarity(record_df, timestamps, urls, names, channels): """When you find a similar video, save its information to a dataframe then give it back.""" # If you want to dynamically save .csv, init before recording if timestamps: signals.do_log(f'Found {len(timestamps[0])} similar frames') for thread_res in timestamps: for stamp in thread_res: # TODO: Sum near timestamps together m1, s1 = stamp[0][0], stamp[0][1] m2, s2 = stamp[1][0], stamp[1][1] score = stamp[2] # Not used, but can be info = {'Cmpl_url': f'{urls[0]}', 'Cmp_name': names[0], 'Cmp_chnl': channels[0], 'Source_url': f'{urls[1]}', 'Source_name': names[1], 'Source_chnl': channels[1], 'Cmp_TimeStamp': fix_time(f'{int(m1)}:{int(s1)}'), 'Source_TimeStamp': fix_time(f'{int(m2)}:{int(s2)}')} if vconf.VERBOSE: signals.do_log(str(info)) record_df = record_df.append(info, ignore_index=True) success = True else: signals.do_log(f'Found no similar frames') success = False return record_df, success
def _progress(d): """da progress""" if d['status'] == 'finished': signals.do_log('Done downloading.')
def error(self, msg): signals.do_log(msg)
def warning(self, msg): if vconf.VERBOSE: signals.do_log(msg)
def debug(self, msg): signals.do_log(msg)
def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(682, 665) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.csvpath_input = QtWidgets.QLineEdit(self.centralwidget) self.csvpath_input.setGeometry(QtCore.QRect(130, 40, 441, 21)) self.csvpath_input.setText("") self.csvpath_input.setObjectName("csvpath_input") self.csvpath = QtWidgets.QLabel(self.centralwidget) self.csvpath.setGeometry(QtCore.QRect(70, 40, 71, 20)) self.csvpath.setObjectName("csvpath") self.path_open = QtWidgets.QToolButton(self.centralwidget) self.path_open.setGeometry(QtCore.QRect(570, 40, 41, 22)) self.path_open.setObjectName("path_open") self.log = QtWidgets.QTextBrowser(self.centralwidget) self.log.setGeometry(QtCore.QRect(20, 160, 641, 141)) self.log.setObjectName("log") signals.connect(self.on_text_changed) self.start_button = QtWidgets.QPushButton(self.centralwidget) self.start_button.setGeometry(QtCore.QRect(250, 130, 80, 23)) self.start_button.setObjectName("start_button") self.stop_button = QtWidgets.QPushButton(self.centralwidget) self.stop_button.setGeometry(QtCore.QRect(350, 130, 80, 23)) self.stop_button.setObjectName("stop_button") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(20, 310, 57, 15)) font = QtGui.QFont() font.setPointSize(11) self.label.setFont(font) self.label.setObjectName("label") self.csvpath_2 = QtWidgets.QLabel(self.centralwidget) self.csvpath_2.setGeometry(QtCore.QRect(80, 70, 51, 20)) self.csvpath_2.setObjectName("csvpath_2") self.csvpath_output = QtWidgets.QLineEdit(self.centralwidget) self.csvpath_output.setGeometry(QtCore.QRect(130, 70, 441, 21)) self.csvpath_output.setText("") self.csvpath_output.setObjectName("csvpath_output") self.output = QtWidgets.QTableView(self.centralwidget) self.output.setGeometry(QtCore.QRect(20, 331, 641, 291)) self.output.setObjectName("output") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 682, 20)) self.menubar.setObjectName("menubar") self.menu = QtWidgets.QMenu(self.menubar) self.menu.setObjectName("menu") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setEnabled(True) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.actionExit = QtWidgets.QAction(MainWindow) self.actionExit.setObjectName("actionExit") self.actionOpen = QtWidgets.QAction(MainWindow) self.actionOpen.setObjectName("actionOpen") self.actionAbout = QtWidgets.QAction(MainWindow) self.actionAbout.setObjectName("actionAbout") self.menu.addAction(self.actionOpen) self.menu.addAction(self.actionAbout) self.menu.addSeparator() self.menu.addAction(self.actionExit) self.menubar.addAction(self.menu.menuAction()) # ============================================================================== # Importnant parts self.retranslateUi(MainWindow) self.path_open.clicked.connect(self.file_opener) # Open button self.actionOpen.triggered.connect(self.file_opener) # Open in menu self.actionExit.triggered.connect(self.exit_program) # Exit in menu self.start_button.clicked.connect(self.thread_start) # Start button self.stop_button.clicked.connect(self.thread_stop) # Stop button QtCore.QMetaObject.connectSlotsByName(MainWindow) self.start_button.setEnabled(False)
def thread_stop(self): signals.working = False signals.do_log('Terminating...') return None
def main(self, csv_path, output_path=""): """Main function: read csv, download videos, compare them, save results.""" signals.do_log('Started') record_df = init_record_file() self.change_path() if not csv_path[0]: signals.do_log('You have not provided any input') self.clean() os.chdir(self.currnt_path) exit() for csv in csv_path: csv = Path(self.currnt_path) / csv com_url, source_list = read_csv(csv) # Downloading try: cmp_file = self.downloader.get_video(com_url[0]) except youtube_dl.utils.DownloadError: signals.do_log('Compilation video is not available.') return {} for source_url in source_list: while signals.working: # Downloading try: src_file = self.downloader.get_video(source_url) except: signals.do_log( 'This video is not available. skipping...') break # Getting things ready if not vconf.QUIET: signals.do_log( 'Getting ready to start comparison process') frames_cmp, meta_cmp = self.video_init(cmp_file) frames_src, meta_src = self.video_init(src_file) if not vconf.QUIET: signals.do_log(f'Comparing source: {meta_src["name"]}') # Do the comparison if not signals.working: signals.do_log('Terminated') return None if not vconf.QUIET: signals.do_log( '**Comparing started**\nIt may take a few minutes...' ) time_stamps = self.compare_videos_parallel( frames_cmp, meta_cmp['fps'], frames_src, meta_src['fps']) if not vconf.QUIET: signals.do_log( f"Comparing {meta_cmp['path']} and {meta_src['path']} finished" ) record_df, success = record_similarity( record_df, time_stamps, [meta_cmp['url'], meta_src['url']], [meta_cmp['name'], meta_src['name']], [meta_cmp['channel'], meta_src['channel']]) if success: record_df = cluster_timestamps(record_df) self.save_results(record_df, output_path, meta_cmp["name"]) break if not vconf.QUIET: signals.do_log(f'====All done====') return record_df
def compare_videos(self, source_frames, source_fps, target_frames, target_fps): """Get two video object, start comparing them frame by frame. Linear Search algorithm.""" from rvsearch.signals import Signals as signals current_frame_s = 0 current_frame_t = 0 timestamps = [] if vconf.VERBOSE: start = monotonic() with _mutex: signals.do_log('Comparing started') for s_frame in source_frames: current_frame_s += source_fps # Go up 1 second current_frame_t = 0 # One target done, now reset for t_frame in target_frames: current_frame_t += target_fps # Go up 1 second score = self.compare_hash_frames(s_frame, t_frame, hash_len=12) if not signals.working: signals.do_log('Terminated') return timestamps if self.check_score(score, threshold=0.75): # Record its timestamp m1, s1 = divmod((current_frame_s / source_fps), 60) m2, s2 = divmod((current_frame_t / target_fps), 60) timestamps.append([[m1, s1], [m2, s2], score]) if not vconf.QUIET: signals.do_log( f'Compilation video: similarity found at {int(m1)}:{int(s1)}' ) if vconf.VERBOSE: signals.do_log(f'ter is: {signals.working}') with _mutex: signals.do_log(timestamps[-1]) signals.do_log("--- %s seconds ---" % (monotonic() - start)) break # First similarity in video, break if vconf.VERBOSE: signals.do_log("Elapsed time: --- %s seconds ---" % (monotonic() - start)) return timestamps