diff --git a/Video2Crops.py b/Video2Crops.py index 41504c8..9abbed1 100644 --- a/Video2Crops.py +++ b/Video2Crops.py @@ -234,6 +234,11 @@ def process_video(video_filenames, main_window): def check_duplicate(ref_frame, frame): global first_frame + + min_possible_mse = 0 + # Calculate the maximum possible MSE (when images are completely different) + max_possible_mse = 255**2 * roi_size * roi_size + if duplicate_threshold > 0 and not first_frame: frame_is_duplicate = True # Calculate the Absolute Difference between the frames @@ -241,16 +246,23 @@ def process_video(video_filenames, main_window): # Calculate the Mean Squared Error (MSE) between the frames mse = np.mean(frame_diff) + normalized_mse = 100 * (1 - (mse - min_possible_mse) / (max_possible_mse - min_possible_mse)) - if mse > duplicate_threshold: + if normalized_mse > duplicate_threshold: # The frame is significantly different, store it ref_frame = frame.copy() # Process the frame here or store it for later processing frame_is_duplicate = False + debug('DEBUG', f"Frame is duplicate: {frame_is_duplicate}, MSE: {mse}, Normalized MSE: {normalized_mse}") return frame_is_duplicate, mse, ref_frame else: - return False, 0000, frame + if first_frame: + debug('DEBUG', f"Frame is the first frame, storing as initial reference") + first_frame = False + else: + debug('DEBUG', f"First Frame Duplicate check is disabled, ignoring!") + return False, 0, frame corner1 = (-1, -1) corner2 = (-1, -1) @@ -390,13 +402,13 @@ def process_video(video_filenames, main_window): cv2.destroyAllWindows() debug('DEBUG', f"Destroyed all current CV2 windows") write_log(f"Video2Crops run complete!\nSaved {counter} frames.\nDiscarded {discarded_frames} frames.") - first_frame = False processing = False # Create a function to add files to the selected files Listbox def add_files(): + global processing_button selected_files = selected_files_listbox.get(0, tk.END) - file_paths = filedialog.askopenfilenames(filetypes=[("Video Files", "*.mp4 *.avi *.mov")]) + file_paths = filedialog.askopenfilenames(title='Select Video File(s)', filetypes=[("Video Files", FILE_TYPES)]) if file_paths: for file_path in file_paths: if file_path not in selected_files: @@ -404,17 +416,26 @@ def add_files(): debug('DEBUG', f"Queued video file: {file_path}") else: debug('DEBUG', f"Attempted to queue already-queued file \'{file_path}\'") + selected_files = selected_files_listbox.get(0, tk.END) + if len(selected_files) > 0: + processing_button.config(state='normal') # Create a button to remove the selected file from the Listbox def remove_file(): + global processing_button selected_index = selected_files_listbox.curselection() if selected_index: selected_files_listbox.delete(selected_index) debug('DEBUG', f"Removed file from queue: {selected_index}") + selected_files = selected_files_listbox.get(0, tk.END) + if len(selected_files) <= 0: + processing_button.config(state='disabled') # Create a function to clear the queue def clear_queue(): + global processing_button selected_files_listbox.delete(0, tk.END) + processing_button.config(state='disabled') debug('DEBUG', f"Cleared the queue") # Create a function to cancel processing @@ -448,25 +469,29 @@ def process_files(): # Define dynamic process/cancel button def process_cancel_toggle(): debug('DEBUG', f"Toggling process/cancel button state") - global processing, processing_thread, processing_button_state, selected_files - if processing_button_state == "start": - if not processing: - processing = True - selected_files = selected_files_listbox.get(0, tk.END) - processing_thread = threading.Thread(target=process_video, args=(selected_files, root)) - processing_thread.daemon = True - processing_thread.start() - processing_button.config(text="Cancel") - processing_button_state = "cancel" - elif processing_button_state == "cancel": - if processing: - processing = False - if processing_thread.is_alive(): - processing_thread.join() - processing_button.config(text="Process Files") - processing_button_state = "start" + global processing, processing_thread, selected_files + if not processing: + toggle_process_button(False) + processing = True + selected_files = selected_files_listbox.get(0, tk.END) + processing_thread = threading.Thread(target=process_video, args=(selected_files, root)) + processing_thread.daemon = True + processing_thread.start() + elif processing: + toggle_process_button(True) + processing = False + if processing_thread.is_alive(): + processing_thread.join() debug('DEBUG', f"Processing/Cancel button state switch finished") +async def toggle_process_button(enable=bool): + """True = Set state to 'Process Files' | False = Set state to 'Cancel'""" + global processing_button + if enable: + processing_button.config(text="Process Files") + else: + processing_button.config(text="Cancel") + # Create a function to open the preferences window def open_preferences(): debug('DEBUG', f"Opening preferences window") @@ -647,7 +672,7 @@ remove_file_button = tk.Button(root, text="Remove File", command=remove_file) remove_file_button.pack(padx=20, pady=5) # Create a button to process the selected files -processing_button = tk.Button(root, text="Process Files", command=process_cancel_toggle) +processing_button = tk.Button(root, text="Process Files", command=process_cancel_toggle, state='disabled') processing_button.pack(padx=20, pady=5) debug('DEBUG', 'UI generated, running main loop')