Update Video2Crops.py
Implemented value checks, code optimization and preparing to migrate functions to modules
This commit is contained in:
		
							parent
							
								
									16b60efe0b
								
							
						
					
					
						commit
						5ac83f6983
					
				
							
								
								
									
										266
									
								
								Video2Crops.py
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								Video2Crops.py
									
									
									
									
									
								
							@ -1,178 +1,34 @@
 | 
				
			|||||||
import cv2
 | 
					import cv2, os, sys, re, threading, webbrowser, screeninfo
 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import numpy as np
 | 
					import numpy as np
 | 
				
			||||||
import tkinter as tk
 | 
					import tkinter as tk
 | 
				
			||||||
from tkinterdnd2 import DND_FILES, TkinterDnD
 | 
					from tkinterdnd2 import DND_FILES, TkinterDnD
 | 
				
			||||||
from tkinter import filedialog, simpledialog, messagebox, scrolledtext
 | 
					from tkinter import filedialog, simpledialog, messagebox, scrolledtext
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import webbrowser
 | 
					 | 
				
			||||||
import screeninfo
 | 
					 | 
				
			||||||
import feedparser
 | 
					import feedparser
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import platform
 | 
					import platform
 | 
				
			||||||
import urllib.request
 | 
					import urllib.request
 | 
				
			||||||
import re
 | 
					from v2cmodules.toolbox import extract_version, debug, checkinifile, write2ini, extract_changelog, toggle_save, toggle_show
 | 
				
			||||||
from bs4 import BeautifulSoup
 | 
					from v2cmodules.common import *
 | 
				
			||||||
import threading
 | 
					 | 
				
			||||||
import configparser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEBUG = True
 | 
					 | 
				
			||||||
STARTING = True
 | 
					 | 
				
			||||||
INVALID_INI = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Debug function
 | 
					 | 
				
			||||||
def debug(state, message):
 | 
					 | 
				
			||||||
    if DEBUG:
 | 
					 | 
				
			||||||
        allowed_states = ['LOG EVENT', 'DEBUG', 'ERROR', 'INFO']
 | 
					 | 
				
			||||||
        if state in allowed_states:
 | 
					 | 
				
			||||||
            debug_message = f"[{state}] {message}"
 | 
					 | 
				
			||||||
            print(f"{debug_message}")
 | 
					 | 
				
			||||||
            debugfile_path = os.path.join(os.getcwd(), 'debugfile.txt')
 | 
					 | 
				
			||||||
            with open(debugfile_path, 'a') as debugfile:
 | 
					 | 
				
			||||||
                log_debug_message = f"{datetime.now().strftime('%m-%d_%H:%M:%S')} | [{state}] {message}\n"
 | 
					 | 
				
			||||||
                debugfile.write(log_debug_message)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            raise ValueError(f"Invalid state '{state}'. Please choose from {', '.join(allowed_states)}.")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def fetch_ini():
 | 
					 | 
				
			||||||
    global INVALID_INI
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        with open(config_file, 'w') as file:
 | 
					 | 
				
			||||||
                config_url = 'https://git.rolfsvaag.no/frarol96/Video2Crops/raw/branch/main/config.ini'
 | 
					 | 
				
			||||||
                with urllib.request.urlopen(config_url) as config_template:
 | 
					 | 
				
			||||||
                    config_template = config_template.read().decode("utf-8")
 | 
					 | 
				
			||||||
                    file.write(config_template)
 | 
					 | 
				
			||||||
                    tk.messagebox.showinfo(
 | 
					 | 
				
			||||||
                        "Reset config file",
 | 
					 | 
				
			||||||
                        f"Config file has been reset"
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    debug('DEBUG', f"Config file was reset to default")
 | 
					 | 
				
			||||||
                    INVALID_INI = False
 | 
					 | 
				
			||||||
    except:
 | 
					 | 
				
			||||||
        print(f"[ERROR] Unable to fetch the missing config.ini file!\nCheck directory permissions and network connectivity!")
 | 
					 | 
				
			||||||
        exit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Setup config file
 | 
					 | 
				
			||||||
config_file = 'config.ini'
 | 
					 | 
				
			||||||
config = configparser.ConfigParser()
 | 
					 | 
				
			||||||
if not os.path.exists(config_file):
 | 
					 | 
				
			||||||
    fetch_ini()
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
config.read(config_file)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def checkinifile():
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Check for the presence of sections and keys
 | 
					 | 
				
			||||||
    expected_sections = ['settings', 'debug']
 | 
					 | 
				
			||||||
    expected_settings_keys = {
 | 
					 | 
				
			||||||
        'frame_step': int,
 | 
					 | 
				
			||||||
        'contrast_threshold': float,
 | 
					 | 
				
			||||||
        'duplicate_threshold': int,
 | 
					 | 
				
			||||||
        'save_output': bool,
 | 
					 | 
				
			||||||
        'show_playback': bool,
 | 
					 | 
				
			||||||
        'roi_size': int,
 | 
					 | 
				
			||||||
        'window_size': str
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    expected_debug_keys = {
 | 
					 | 
				
			||||||
        'debug_output': bool
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    CHECK_SECTIONS = True
 | 
					 | 
				
			||||||
    CHECK_KEYS = True
 | 
					 | 
				
			||||||
    CHECK_VALUES = False
 | 
					 | 
				
			||||||
    global INVALID_INI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Check for the presence of sections
 | 
					 | 
				
			||||||
    if CHECK_SECTIONS:
 | 
					 | 
				
			||||||
        for section in expected_sections:
 | 
					 | 
				
			||||||
            if section not in config.sections():
 | 
					 | 
				
			||||||
                INVALID_INI = True
 | 
					 | 
				
			||||||
                print(f"Missing section: [{section}]")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Check for the presence of keys and their types in the 'settings' section
 | 
					 | 
				
			||||||
    if 'settings' in config.sections():
 | 
					 | 
				
			||||||
        for key, expected_type in expected_settings_keys.items():
 | 
					 | 
				
			||||||
            if key not in config['settings']:
 | 
					 | 
				
			||||||
                if CHECK_KEYS:
 | 
					 | 
				
			||||||
                    INVALID_INI = True
 | 
					 | 
				
			||||||
                    print(f"Missing key '{key}' in [settings] section")
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                value = config.get('settings', key)
 | 
					 | 
				
			||||||
                if not isinstance(value, expected_type):
 | 
					 | 
				
			||||||
                    if CHECK_VALUES:
 | 
					 | 
				
			||||||
                        INVALID_INI = True
 | 
					 | 
				
			||||||
                        print(f"Value of '{key}' in [settings] section is not of expected type {expected_type}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Check for the presence of keys and their types in the 'debug' section
 | 
					 | 
				
			||||||
    if 'debug' in config.sections():
 | 
					 | 
				
			||||||
        for key, expected_type in expected_debug_keys.items():
 | 
					 | 
				
			||||||
            if key not in config['debug']:
 | 
					 | 
				
			||||||
                if CHECK_KEYS:
 | 
					 | 
				
			||||||
                    INVALID_INI = True
 | 
					 | 
				
			||||||
                    print(f"Missing key '{key}' in [debug] section")
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                value = config.get('debug', key)
 | 
					 | 
				
			||||||
                if not isinstance(value, expected_type):
 | 
					 | 
				
			||||||
                    if CHECK_VALUES:
 | 
					 | 
				
			||||||
                        INVALID_INI = True
 | 
					 | 
				
			||||||
                        print(f"Value of '{key}' in [debug] section is not of expected type {expected_type}")
 | 
					 | 
				
			||||||
    if INVALID_INI:
 | 
					 | 
				
			||||||
        fetch_ini()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
checkinifile()
 | 
					checkinifile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
debug('INFO', "Video2Crops starting ...")
 | 
					debug('INFO', "Video2Crops starting ...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# START INITIATING PROGRAM VARIABLES FROM HERE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Constants
 | 
					 | 
				
			||||||
v2cv = [1, 2, 0]
 | 
					 | 
				
			||||||
v2cversion1 = v2cv[0]
 | 
					 | 
				
			||||||
v2cversion2 = v2cv[1]
 | 
					 | 
				
			||||||
v2cversion3 = v2cv[2]
 | 
					 | 
				
			||||||
v2cversion = f"{v2cversion1}.{v2cversion2}.{v2cversion3}"
 | 
					 | 
				
			||||||
processing = False
 | 
					 | 
				
			||||||
processing_button_state = "start"
 | 
					 | 
				
			||||||
# Load ini settings
 | 
					 | 
				
			||||||
frame_step = config.get('settings', 'frame_step')
 | 
					 | 
				
			||||||
contrast_threshold = config.get('settings', 'contrast_threshold')
 | 
					 | 
				
			||||||
duplicate_threshold = config.get('settings', 'duplicate_threshold')
 | 
					 | 
				
			||||||
SAVE = config.get('settings', 'save_output')
 | 
					 | 
				
			||||||
SHOW = config.get('settings', 'show_playback')
 | 
					 | 
				
			||||||
roi_size = config.get('settings', 'roi_size')
 | 
					 | 
				
			||||||
tksize = config.get('settings', 'window_size')
 | 
					 | 
				
			||||||
DEBUG = config.get('debug', 'debug_output')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def write2ini(section, variable, value):
 | 
					 | 
				
			||||||
    config.set(section, str(variable), str(value))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Save the changes to the INI file
 | 
					 | 
				
			||||||
    with open(config_file, 'w') as configfile:
 | 
					 | 
				
			||||||
        config.write(configfile)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Define the RSS feed URL
 | 
					 | 
				
			||||||
rss_feed_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases.rss"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Create the root window
 | 
					# Create the root window
 | 
				
			||||||
root = TkinterDnD.Tk()
 | 
					root = TkinterDnD.Tk()
 | 
				
			||||||
root.title("Video2Crops")
 | 
					root.title("Video2Crops")
 | 
				
			||||||
root.geometry(tksize)
 | 
					root.geometry(tksize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Function to clear the queue
 | 
					 | 
				
			||||||
def clear_queue():
 | 
					 | 
				
			||||||
    selected_files_listbox.delete(0, tk.END)
 | 
					 | 
				
			||||||
    debug('DEBUG', 'Cleared work queue')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def open_changelog_window():
 | 
					def open_changelog_window():
 | 
				
			||||||
    debug('DEBUG', 'Loading changelog window')
 | 
					    debug('DEBUG', 'Loading changelog window')
 | 
				
			||||||
    changelog_window = tk.Toplevel(root)
 | 
					    changelog_window = tk.Toplevel(root)
 | 
				
			||||||
    changelog_window.title("Video2Crops Changelog")
 | 
					    changelog_window.title("Video2Crops Changelog")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        debug('DEBUG', f"Reading the RSS feed from: {rss_feed_url}")
 | 
					        debug('DEBUG', f"Reading the RSS feed from: {RSS_FEED_URL}")
 | 
				
			||||||
        # Parse the RSS feed
 | 
					        # Parse the RSS feed
 | 
				
			||||||
        feed = feedparser.parse(rss_feed_url)
 | 
					        feed = feedparser.parse(RSS_FEED_URL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not feed.entries:
 | 
					        if not feed.entries:
 | 
				
			||||||
            scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
 | 
					            scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
 | 
				
			||||||
@ -195,9 +51,9 @@ def open_changelog_window():
 | 
				
			|||||||
            if version_str is not None and changelog_md is not None:
 | 
					            if version_str is not None and changelog_md is not None:
 | 
				
			||||||
                version_info = f"# v{version_str}"
 | 
					                version_info = f"# v{version_str}"
 | 
				
			||||||
                print(version_str)
 | 
					                print(version_str)
 | 
				
			||||||
                if str(version_str) == str(v2cversion) and entry_num == 1:
 | 
					                if str(version_str) == str(V2CVERSION) and entry_num == 1:
 | 
				
			||||||
                    version_info += "(up to date)"
 | 
					                    version_info += "(up to date)"
 | 
				
			||||||
                elif str(version_str) == str(v2cversion):
 | 
					                elif str(version_str) == str(V2CVERSION):
 | 
				
			||||||
                    version_info += "(current)"
 | 
					                    version_info += "(current)"
 | 
				
			||||||
                elif entry_num == 1:
 | 
					                elif entry_num == 1:
 | 
				
			||||||
                    version_info += "(latest)"
 | 
					                    version_info += "(latest)"
 | 
				
			||||||
@ -222,44 +78,18 @@ def open_changelog_window():
 | 
				
			|||||||
        scrolled_text.insert(tk.END, f"An error occurred: {str(e)}")
 | 
					        scrolled_text.insert(tk.END, f"An error occurred: {str(e)}")
 | 
				
			||||||
        debug('ERROR', f"An error occured while processing changelog:\n{str(e)}\n")
 | 
					        debug('ERROR', f"An error occured while processing changelog:\n{str(e)}\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def extract_version(title):
 | 
					 | 
				
			||||||
    # Use regular expressions to extract the version number from the title
 | 
					 | 
				
			||||||
    match = re.search(r'(\d+\.\d+\.\d+)', title)
 | 
					 | 
				
			||||||
    if match:
 | 
					 | 
				
			||||||
        return match.group(1)
 | 
					 | 
				
			||||||
    return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def extract_changelog(description):
 | 
					 | 
				
			||||||
    # Parse the HTML content using BeautifulSoup
 | 
					 | 
				
			||||||
    soup = BeautifulSoup(description, 'html.parser')
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    # Find all changelog sections
 | 
					 | 
				
			||||||
    changelog_sections = soup.find_all('h3', id='user-content-changelog')
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    changelogs = []
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    for changelog_section in changelog_sections:
 | 
					 | 
				
			||||||
        # Extract the changelog text as plain text
 | 
					 | 
				
			||||||
        changelog_text = changelog_section.find_next('ul').get_text()
 | 
					 | 
				
			||||||
        changelogs.append(changelog_text.strip())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Join the changelogs into a single string
 | 
					 | 
				
			||||||
    changelog_str = '\n'.join(changelogs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return changelog_str if changelog_str else None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Function for updating Video2Crops
 | 
					# Function for updating Video2Crops
 | 
				
			||||||
def check_for_updates():
 | 
					def check_for_updates():
 | 
				
			||||||
    debug('DEBUG', f"Checking for updates")
 | 
					    debug('DEBUG', f"Checking for updates")
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        # Parse the RSS feed
 | 
					        # Parse the RSS feed
 | 
				
			||||||
        feed = feedparser.parse(rss_feed_url)
 | 
					        feed = feedparser.parse(RSS_FEED_URL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not feed.entries and not STARTING:
 | 
					        if not feed.entries and not STARTING:
 | 
				
			||||||
            response = tk.messagebox.showinfo(
 | 
					            response = tk.messagebox.showinfo(
 | 
				
			||||||
                "No updates found",
 | 
					                "No updates found",
 | 
				
			||||||
                f"Latest version: ({v2cversion})\n"
 | 
					                f"Latest version: ({V2CVERSION})\n"
 | 
				
			||||||
                f"Current version: ({v2cversion})\n"
 | 
					                f"Current version: ({V2CVERSION})\n"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            debug('DEBUG', f"No updates found")
 | 
					            debug('DEBUG', f"No updates found")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
@ -271,8 +101,8 @@ def check_for_updates():
 | 
				
			|||||||
        if latest_version_str is None and not STARTING:
 | 
					        if latest_version_str is None and not STARTING:
 | 
				
			||||||
            tk.messagebox.showinfo(
 | 
					            tk.messagebox.showinfo(
 | 
				
			||||||
                "No update Available",
 | 
					                "No update Available",
 | 
				
			||||||
                f"Latest version: ({v2cversion})\n"
 | 
					                f"Latest version: ({V2CVERSION})\n"
 | 
				
			||||||
                f"Current version: ({v2cversion})\n"
 | 
					                f"Current version: ({V2CVERSION})\n"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            debug('DEBUG', f"No updates available")
 | 
					            debug('DEBUG', f"No updates available")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
@ -281,12 +111,12 @@ def check_for_updates():
 | 
				
			|||||||
        latest_version = tuple(map(int, latest_version_str.split('.')))
 | 
					        latest_version = tuple(map(int, latest_version_str.split('.')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Compare the versions
 | 
					        # Compare the versions
 | 
				
			||||||
        if latest_version > (v2cversion1, v2cversion2, v2cversion3):
 | 
					        if latest_version > (V2CV[0], V2CV[1], V2CV[2]):
 | 
				
			||||||
            # Prompt the user for an update
 | 
					            # Prompt the user for an update
 | 
				
			||||||
            response = tk.messagebox.askyesno(
 | 
					            response = tk.messagebox.askyesno(
 | 
				
			||||||
                "Update Available",
 | 
					                "Update Available",
 | 
				
			||||||
                f"A newer version ({latest_version_str}) is available.\n"
 | 
					                f"A newer version ({latest_version_str}) is available.\n"
 | 
				
			||||||
                f"Current version: ({v2cversion})\n"
 | 
					                f"Current version: ({V2CVERSION})\n"
 | 
				
			||||||
                "Do you want to update?"
 | 
					                "Do you want to update?"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            debug('DEBUG', f"Found new update, prompting user")
 | 
					            debug('DEBUG', f"Found new update, prompting user")
 | 
				
			||||||
@ -338,8 +168,8 @@ def check_for_updates():
 | 
				
			|||||||
            if not STARTING:
 | 
					            if not STARTING:
 | 
				
			||||||
                tk.messagebox.showinfo(
 | 
					                tk.messagebox.showinfo(
 | 
				
			||||||
                    "No update Available",
 | 
					                    "No update Available",
 | 
				
			||||||
                    f"Latest version: ({v2cversion})\n"
 | 
					                    f"Latest version: ({V2CVERSION})\n"
 | 
				
			||||||
                    f"Current version: ({v2cversion})\n"
 | 
					                    f"Current version: ({V2CVERSION})\n"
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            debug('DEBUG', f"No update available")
 | 
					            debug('DEBUG', f"No update available")
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
@ -403,6 +233,8 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
        return np.std(grayscale)
 | 
					        return np.std(grayscale)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def check_duplicate(ref_frame, frame):
 | 
					    def check_duplicate(ref_frame, frame):
 | 
				
			||||||
 | 
					        global first_frame
 | 
				
			||||||
 | 
					        if duplicate_threshold > 0 and not first_frame:
 | 
				
			||||||
            frame_is_duplicate = True
 | 
					            frame_is_duplicate = True
 | 
				
			||||||
            # Calculate the Absolute Difference between the frames
 | 
					            # Calculate the Absolute Difference between the frames
 | 
				
			||||||
            frame_diff = cv2.absdiff(ref_frame, frame)
 | 
					            frame_diff = cv2.absdiff(ref_frame, frame)
 | 
				
			||||||
@ -417,6 +249,8 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
                frame_is_duplicate = False
 | 
					                frame_is_duplicate = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return frame_is_duplicate, mse, ref_frame
 | 
					            return frame_is_duplicate, mse, ref_frame
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return False, 0000, frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    corner1 = (-1, -1)
 | 
					    corner1 = (-1, -1)
 | 
				
			||||||
    corner2 = (-1, -1)
 | 
					    corner2 = (-1, -1)
 | 
				
			||||||
@ -429,6 +263,9 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
            debug('DEBUG', f"Breaking loop due to processing = {processing}")
 | 
					            debug('DEBUG', f"Breaking loop due to processing = {processing}")
 | 
				
			||||||
            break
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        global first_frame
 | 
				
			||||||
 | 
					        first_frame = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        main_window.title(f"Video Processing App")
 | 
					        main_window.title(f"Video Processing App")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        video_file_src = videofilename
 | 
					        video_file_src = videofilename
 | 
				
			||||||
@ -477,9 +314,6 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
            cv2.namedWindow(vp_title, cv2.WINDOW_NORMAL)
 | 
					            cv2.namedWindow(vp_title, cv2.WINDOW_NORMAL)
 | 
				
			||||||
            cv2.resizeWindow(vp_title, initial_width, initial_height)
 | 
					            cv2.resizeWindow(vp_title, initial_width, initial_height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Store the first frame as reference for the duplicate check
 | 
					 | 
				
			||||||
        ret, reference_frame = cap.read()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
            ret, frame = cap.read()
 | 
					            ret, frame = cap.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -502,6 +336,9 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
            test = cv2.rectangle(frame.copy(), (x1 - 1, y2 - 1), (x2 + 1, y1 + 1), (255, 0, 0), 4)
 | 
					            test = cv2.rectangle(frame.copy(), (x1 - 1, y2 - 1), (x2 + 1, y1 + 1), (255, 0, 0), 4)
 | 
				
			||||||
            cropped_image = frame[y1:y2, x1:x2]
 | 
					            cropped_image = frame[y1:y2, x1:x2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if first_frame:
 | 
				
			||||||
 | 
					                reference_frame = cropped_image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if SHOW:
 | 
					            if SHOW:
 | 
				
			||||||
                cv2.imshow(vp_title, test)
 | 
					                cv2.imshow(vp_title, test)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -520,7 +357,7 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
                frame_is_duplicate, frame_duplicate_mse, reference_frame = check_duplicate(reference_frame, cropped_image)
 | 
					                frame_is_duplicate, frame_duplicate_mse, reference_frame = check_duplicate(reference_frame, cropped_image)
 | 
				
			||||||
                frame_value = calc_value(cropped_image)
 | 
					                frame_value = calc_value(cropped_image)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if frame_value >= contrast_threshold and not frame_is_duplicate:
 | 
					                if (frame_value >= contrast_threshold or contrast_threshold == 0) and not frame_is_duplicate:
 | 
				
			||||||
                    cropped_image_status = "Accepted"
 | 
					                    cropped_image_status = "Accepted"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    filename = videofilename_base + '_' + str(counter).zfill(4) + ".png"
 | 
					                    filename = videofilename_base + '_' + str(counter).zfill(4) + ".png"
 | 
				
			||||||
@ -552,12 +389,10 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
        cap.release()
 | 
					        cap.release()
 | 
				
			||||||
        cv2.destroyAllWindows()
 | 
					        cv2.destroyAllWindows()
 | 
				
			||||||
        debug('DEBUG', f"Destroyed all current CV2 windows")
 | 
					        debug('DEBUG', f"Destroyed all current CV2 windows")
 | 
				
			||||||
 | 
					 | 
				
			||||||
        write_log(f"Video2Crops run complete!\nSaved {counter} frames.\nDiscarded {discarded_frames} frames.")
 | 
					        write_log(f"Video2Crops run complete!\nSaved {counter} frames.\nDiscarded {discarded_frames} frames.")
 | 
				
			||||||
    
 | 
					        first_frame = False
 | 
				
			||||||
    processing = False
 | 
					    processing = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Create a function to add files to the selected files Listbox
 | 
					# Create a function to add files to the selected files Listbox
 | 
				
			||||||
def add_files():
 | 
					def add_files():
 | 
				
			||||||
    file_paths = filedialog.askopenfilenames(filetypes=[("Video Files", "*.mp4 *.avi *.mov")])
 | 
					    file_paths = filedialog.askopenfilenames(filetypes=[("Video Files", "*.mp4 *.avi *.mov")])
 | 
				
			||||||
@ -606,7 +441,6 @@ def process_files():
 | 
				
			|||||||
        messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
					        messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
    debug('DEBUG', f"File processing done")
 | 
					    debug('DEBUG', f"File processing done")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Define dynamic process/cancel button
 | 
					# Define dynamic process/cancel button
 | 
				
			||||||
def process_cancel_toggle():
 | 
					def process_cancel_toggle():
 | 
				
			||||||
    debug('DEBUG', f"Toggling process/cancel button state")
 | 
					    debug('DEBUG', f"Toggling process/cancel button state")
 | 
				
			||||||
@ -629,7 +463,6 @@ def process_cancel_toggle():
 | 
				
			|||||||
            processing_button_state = "start"
 | 
					            processing_button_state = "start"
 | 
				
			||||||
    debug('DEBUG', f"Processing/Cancel button state switch finished")
 | 
					    debug('DEBUG', f"Processing/Cancel button state switch finished")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Create a function to open the preferences window
 | 
					# Create a function to open the preferences window
 | 
				
			||||||
def open_preferences():
 | 
					def open_preferences():
 | 
				
			||||||
    debug('DEBUG', f"Opening preferences window")
 | 
					    debug('DEBUG', f"Opening preferences window")
 | 
				
			||||||
@ -637,11 +470,11 @@ def open_preferences():
 | 
				
			|||||||
    preferences_window.title("Preferences")
 | 
					    preferences_window.title("Preferences")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def change_frame_step():
 | 
					    def change_frame_step():
 | 
				
			||||||
 | 
					        global frame_step
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            new_frame_step = simpledialog.askinteger("Frame Step", "Enter the new frame step:")
 | 
					            new_frame_step = simpledialog.askinteger("Frame Step", "Enter the new frame step:", initialvalue=frame_step, minvalue=1)
 | 
				
			||||||
            if new_frame_step is not None:
 | 
					            if new_frame_step is not None:
 | 
				
			||||||
                frame_step_var.set(str(new_frame_step))
 | 
					                frame_step_var.set(str(new_frame_step))
 | 
				
			||||||
                global frame_step
 | 
					 | 
				
			||||||
                frame_step = new_frame_step
 | 
					                frame_step = new_frame_step
 | 
				
			||||||
                write2ini('settings', 'frame_step', new_frame_step)
 | 
					                write2ini('settings', 'frame_step', new_frame_step)
 | 
				
			||||||
                debug('DEBUG', f"Set frame stepping to {frame_step}")
 | 
					                debug('DEBUG', f"Set frame stepping to {frame_step}")
 | 
				
			||||||
@ -649,8 +482,9 @@ def open_preferences():
 | 
				
			|||||||
            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def change_contrast_threshold():
 | 
					    def change_contrast_threshold():
 | 
				
			||||||
 | 
					        global contrast_threshold
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            new_contrast_threshold = simpledialog.askfloat("Contrast Threshold", "Enter the new value threshold:")
 | 
					            new_contrast_threshold = simpledialog.askfloat("Contrast Threshold", "Enter the new value threshold:", initialvalue=contrast_threshold, minvalue=0)
 | 
				
			||||||
            if new_contrast_threshold is not None:
 | 
					            if new_contrast_threshold is not None:
 | 
				
			||||||
                contrast_threshold_var.set(str(new_contrast_threshold))
 | 
					                contrast_threshold_var.set(str(new_contrast_threshold))
 | 
				
			||||||
                write2ini('settings', 'contrast_threshold', new_contrast_threshold)
 | 
					                write2ini('settings', 'contrast_threshold', new_contrast_threshold)
 | 
				
			||||||
@ -659,8 +493,9 @@ def open_preferences():
 | 
				
			|||||||
            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def change_duplicate_threshold():
 | 
					    def change_duplicate_threshold():
 | 
				
			||||||
 | 
					        global duplicate_threshold
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            new_duplicate_threshold = simpledialog.askfloat("Duplicate Threshold", "Enter the new value threshold:")
 | 
					            new_duplicate_threshold = simpledialog.askfloat("Duplicate Threshold", "Enter the new value threshold:", initialvalue=duplicate_threshold, minvalue=0, maxvalue=100)
 | 
				
			||||||
            if new_duplicate_threshold is not None:
 | 
					            if new_duplicate_threshold is not None:
 | 
				
			||||||
                duplicate_threshold_var.set(str(new_duplicate_threshold))
 | 
					                duplicate_threshold_var.set(str(new_duplicate_threshold))
 | 
				
			||||||
                write2ini('settings', 'duplicate_threshold', new_duplicate_threshold)
 | 
					                write2ini('settings', 'duplicate_threshold', new_duplicate_threshold)
 | 
				
			||||||
@ -668,27 +503,10 @@ def open_preferences():
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def toggle_save():
 | 
					 | 
				
			||||||
        if not processing:
 | 
					 | 
				
			||||||
            global SAVE
 | 
					 | 
				
			||||||
            SAVE = not SAVE
 | 
					 | 
				
			||||||
            write2ini('settings', 'save_output', SAVE)
 | 
					 | 
				
			||||||
            debug('DEBUG', f"Saving output: {SAVE}")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def toggle_show():
 | 
					 | 
				
			||||||
        if not processing:
 | 
					 | 
				
			||||||
            global SHOW
 | 
					 | 
				
			||||||
            SHOW = not SHOW
 | 
					 | 
				
			||||||
            write2ini('settings', 'show_playback', SHOW)
 | 
					 | 
				
			||||||
            debug('DEBUG', f"Displaying output: {SHOW}")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def apply_roi_size():
 | 
					    def apply_roi_size():
 | 
				
			||||||
 | 
					        global roi_size
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            new_roi_size = simpledialog.askinteger("ROI Size", "Enter the new ROI size:")
 | 
					            new_roi_size = simpledialog.askinteger("ROI Size", "Enter the new ROI size:", initialvalue=roi_size, minvalue=1)
 | 
				
			||||||
            if new_roi_size is not None:
 | 
					            if new_roi_size is not None:
 | 
				
			||||||
                roi_size_var.set(str(new_roi_size))
 | 
					                roi_size_var.set(str(new_roi_size))
 | 
				
			||||||
                write2ini('settings', 'roi_size', new_roi_size)
 | 
					                write2ini('settings', 'roi_size', new_roi_size)
 | 
				
			||||||
@ -708,6 +526,9 @@ def open_preferences():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    contrast_threshold_label = tk.Label(preferences_window, text="Contrast Threshold:")
 | 
					    contrast_threshold_label = tk.Label(preferences_window, text="Contrast Threshold:")
 | 
				
			||||||
    contrast_threshold_label.grid(row=1, column=0, padx=20, pady=5)
 | 
					    contrast_threshold_label.grid(row=1, column=0, padx=20, pady=5)
 | 
				
			||||||
 | 
					    if contrast_threshold == float(0):
 | 
				
			||||||
 | 
					        contrast_threshold_value = tk.Label(preferences_window, text="Disabled")
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
        contrast_threshold_value = tk.Label(preferences_window, textvariable=contrast_threshold_var)
 | 
					        contrast_threshold_value = tk.Label(preferences_window, textvariable=contrast_threshold_var)
 | 
				
			||||||
    contrast_threshold_value.grid(row=1, column=1, padx=20, pady=5)
 | 
					    contrast_threshold_value.grid(row=1, column=1, padx=20, pady=5)
 | 
				
			||||||
    contrast_threshold_button = tk.Button(preferences_window, text="Edit", command=change_contrast_threshold)
 | 
					    contrast_threshold_button = tk.Button(preferences_window, text="Edit", command=change_contrast_threshold)
 | 
				
			||||||
@ -715,6 +536,9 @@ def open_preferences():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    duplicate_threshold_label = tk.Label(preferences_window, text="Duplicate Threshold:")
 | 
					    duplicate_threshold_label = tk.Label(preferences_window, text="Duplicate Threshold:")
 | 
				
			||||||
    duplicate_threshold_label.grid(row=2, column=0, padx=20, pady=5)
 | 
					    duplicate_threshold_label.grid(row=2, column=0, padx=20, pady=5)
 | 
				
			||||||
 | 
					    if duplicate_threshold == float(0):
 | 
				
			||||||
 | 
					        duplicate_threshold_value = tk.Label(preferences_window, text="Disabled")
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
        duplicate_threshold_value = tk.Label(preferences_window, textvariable=duplicate_threshold_var)
 | 
					        duplicate_threshold_value = tk.Label(preferences_window, textvariable=duplicate_threshold_var)
 | 
				
			||||||
    duplicate_threshold_value.grid(row=2, column=1, padx=20, pady=5)
 | 
					    duplicate_threshold_value.grid(row=2, column=1, padx=20, pady=5)
 | 
				
			||||||
    duplicate_threshold_button = tk.Button(preferences_window, text="Edit", command=change_duplicate_threshold)
 | 
					    duplicate_threshold_button = tk.Button(preferences_window, text="Edit", command=change_duplicate_threshold)
 | 
				
			||||||
@ -795,11 +619,11 @@ info_menu = tk.Menu(menu, tearoff=0, disabledforeground="#000")
 | 
				
			|||||||
menu.add_cascade(label="Info", menu=info_menu)
 | 
					menu.add_cascade(label="Info", menu=info_menu)
 | 
				
			||||||
info_menu.add_command(label="Project Folder", command=lambda: webbrowser.open(os.getcwd()))
 | 
					info_menu.add_command(label="Project Folder", command=lambda: webbrowser.open(os.getcwd()))
 | 
				
			||||||
info_menu.add_separator()
 | 
					info_menu.add_separator()
 | 
				
			||||||
info_menu.add_command(label="About", command=lambda: webbrowser.open("https://git.rolfsvaag.no/frarol96/Video2Crops/wiki"))
 | 
					info_menu.add_command(label="About", command=lambda: webbrowser.open(WIKI_URL))
 | 
				
			||||||
info_menu.add_command(label=f"Check for updates", command=check_for_updates)
 | 
					info_menu.add_command(label=f"Check for updates", command=check_for_updates)
 | 
				
			||||||
info_menu.add_command(label=f"View Changelog", command=open_changelog_window)
 | 
					info_menu.add_command(label=f"View Changelog", command=open_changelog_window)
 | 
				
			||||||
info_menu.add_separator()
 | 
					info_menu.add_separator()
 | 
				
			||||||
info_menu.add_command(state="disabled", label=f"Version: {str(v2cversion)}")
 | 
					info_menu.add_command(state="disabled", label=f"Version: {str(V2CVERSION)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a label for selected files
 | 
					# Create a label for selected files
 | 
				
			||||||
label = tk.Label(root, text="Selected Files:")
 | 
					label = tk.Label(root, text="Selected Files:")
 | 
				
			||||||
@ -808,7 +632,7 @@ label.pack(padx=20, pady=5)
 | 
				
			|||||||
# Create a Listbox to display the selected files
 | 
					# Create a Listbox to display the selected files
 | 
				
			||||||
selected_files_listbox = tk.Listbox(root, selectmode=tk.SINGLE, exportselection=0)
 | 
					selected_files_listbox = tk.Listbox(root, selectmode=tk.SINGLE, exportselection=0)
 | 
				
			||||||
selected_files_listbox.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)
 | 
					selected_files_listbox.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)
 | 
				
			||||||
 | 
					DEBUG
 | 
				
			||||||
# Create a button to select and add files to the Listbox
 | 
					# Create a button to select and add files to the Listbox
 | 
				
			||||||
select_files_button = tk.Button(root, text="Select Files", command=add_files)
 | 
					select_files_button = tk.Button(root, text="Select Files", command=add_files)
 | 
				
			||||||
select_files_button.pack(padx=20, pady=5)
 | 
					select_files_button.pack(padx=20, pady=5)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user