Initial Before and Afters

  1. ffmpeg.exe -hide_banner -y -i lazarus_man_the_95_pilot_part2_4trk_2997i_Q0034521_20230519_new_out.mov -map v:0 -vf dedot -c:v prores -c:a copy outtest.mov

  1. Alchemist 29.97 to 23.98

  2. ESRGAN uprez

    python inference_realesrgan.py -n RealESRGAN_x4plus -i infile --outscale 3.5 --face_enhance

  3. Wrap and audio synce per WB delivery spec

    ??????????? WIP


6/6/23

ffmpeg.exe -hide_banner -y -i input.mov -vf dedot=ct=0:tc=1 -c:v prores_ks -profile:v 3 -c:a copy output.mov


6/13/23

### Last edit 10:30am 06/13/23 - moved Queue process external to the class

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue

queue = Queue()

class Watcher:

    

    def __init__(self, directory):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        

    def run(self):
        event_handler = FileSystemEventHandler()
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        
        print("File created:", event.src_path)
        input_path = event.src_path
        output_path = input_path.replace("\wip", "\out")
        ## HARD coded res values. Could be better.
        width = 1920
        height = 1080
        
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -c:v prores_ks -profile:v 3 -vf dedot=ct=0:tc=1,tvai_up=model=prob-3:scale=4,scale={}:{} {}".format(input_path, width, height, output_path)
        print(ffmpeg_command)
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        print (queue)
        print("############################################################<- QUEUE")
        while not queue.empty():
            ffmpeg_command = queue.get()
            time.sleep(30)
            print ("running comand")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print("command done")

        
        

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)

if __name__ == "__main__":
    directory = sys.argv[1]
    watcher = Watcher(directory)
    watcher.run()

06/16/23

### Last edit 01:07pm 06/16/23 v8 - added padding and 4x3 matte and hyper parameters

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue

queue = Queue()

class Watcher:

    

    def __init__(self, directory):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        

    def run(self):
        event_handler = FileSystemEventHandler()
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        
        print("File created:", event.src_path)
        input_path = event.src_path
        output_path = input_path.replace("\wip", "\out")
        ## HARD coded res values. Could be better.
        width = 1920
        height = 1080
        
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -c:a copy -c:v prores_ks -profile:v 3 -filter_complex dedot=ct=0:tc=1,tvai_up=model=prob-3:scale=4::w=3840:h=2160:preblur=50:noise=30:details=50:halo:20:blur=10:compression=0:estimate=20,scale={}:{}:out_color_matrix=bt709:flags=lanczos,crop=trunc({}*1.33333):{},pad={}:{}:-1:-1:color=black {}".format(input_path, width, height, height, height, width, height, output_path)
        print(ffmpeg_command)
        if queue.empty():
            print ("Initializing, processing clip")
            time.sleep(5000)
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        items = iter(queue.queue)
        print (list(items))
        print("############################################################<- QUEUE")
        #Delay for Alchemist to finish 
        
        while not queue.empty():
            ffmpeg_command = queue.get()
            
            print ("processing clip")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print(">>>>>>>>>>>>>>Render Done<<<<<<<<<<<<<")

        
        

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)

if __name__ == "__main__":
    directory = sys.argv[1]
    watcher = Watcher(directory)
    watcher.run()

06/26/23 - nlmean addition to ffmpeg command

ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i D:\Alchemist\out_2398_sd\AMERICAN_KICKBOXER_PART_1_245469C_15sec_2398.mov -pix_fmt yuv422p10le -c:a copy -c:v prores -profile:v 3 -vf dedot=rainbows:lt=1.0:tl=0.0,nlmeans=1:5:5:3:3,tvai_up=model=prob-3:scale=0:w=1920:h=1080:preblur=1:noise=0.05:details=0.5:halo=0.2:blur=0.1:compression=0.12:prenoise=0.02:blend=0.2:device=0:vram=1:instances=1,scale=1920:1080:out_color_matrix=bt709:flags=lanczos D:\Alchemist\out_2398_sd\AMERICAN_KICKBOXER_PART_1_245469C_15sec_2398_tvai_test.mov


06/30/23 - SamurAI_v09.py, grain and LUT addition

### Last edit 10:00am 06/29/23 v9 - worked in denoise, grain, and LUT command

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue

queue = Queue()

class Watcher:

    

    def __init__(self, directory):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        

    def run(self):
        event_handler = FileSystemEventHandler()
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        
        print("File created:", event.src_path)
        input_path = event.src_path
        output_path = input_path.replace("\wip", "\out")
        ## HARD coded values. Could be better.
        width = 1920
        height = 1080
        lut = "samurai_unity_709_2_709_33x33_v01.cube"
        
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -flush_packets 1 -sws_flags spline+accurate_rnd+full_chroma_int  -pix_fmt yuv422p10le -c:a copy -c:v prores -profile:v 3 -vf dedot,nlmeans=1:7:7:3:3,tvai_up=model=prob-3:scale=0:w={}:h={}:preblur=0:noise=0.2:details=0.25:halo=0:blur=0.2:compression=0:estimate=20:prenoise=0.03:device=0:vram=1:instances=1,noise=c0s=25:c0f=t+u,scale={}:{}:out_color_matrix=bt709:flags=lanczos,lut3d={},crop=trunc({}*1.33333):{},pad={}:{}:-1:-1:color=black {}".format(input_path,width,height,width,height,lut,height,height,width,height,output_path)
        print(ffmpeg_command)
        if queue.empty():
            print ("Initializing, processing clip")
            time.sleep(10)
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        items = iter(queue.queue)
        print (list(items))
        print("############################################################<- QUEUE")
        #Delay for Alchemist to finish 
        
        while not queue.empty():
            ffmpeg_command = queue.get()
            
            print ("processing clip")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print(">>>>>>>>>>>>>>Render Done<<<<<<<<<<<<<")

        
        

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)

if __name__ == "__main__":
    directory = sys.argv[1]
    watcher = Watcher(directory)
    watcher.run()

7/11 - Gui addition

### Last edit 5:00pm 07/06/23 v12 - gui refined

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue
import tkinter as tk
import shutil

## initialize the queue
queue = Queue()

##Define usage
def usage():
    print("Usage: python samurAI_v10.py [options]")
    print("Options: watch_folder_path height width LUT_path")
    print("-h, --help: Show this help message")
    print("-v, --version: Show the version number")



class Watcher:

    

    def __init__(self, directory, xaxis, yaxis, lutpath ):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        
        ##JD Vars
        self.xaxis = xaxis
        self.yaxis = yaxis
        self.lutpath = lutpath
        
   
        
        

    def run(self, xaxis, yaxis, lutpath):
        event_handler = FileSystemEventHandler()
        ## this is the important one
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        
        print("File created:", event.src_path)
        input_path = event.src_path
        output_path = input_path.replace("\wip", "\out")
        ## HARD coded values. Could be better.
        width = self.xaxis
        height = self.yaxis
        
        ##copy and set lut        
        print(self.lutpath)
        lutname = os.path.basename(self.lutpath)
        if not os.path.exists(os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname)):
            shutil.copyfile(self.lutpath, os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname))
        lut = lutname
                
        
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -flush_packets 1 -sws_flags spline+accurate_rnd+full_chroma_int  -pix_fmt yuv422p10le -c:a copy -c:v prores -profile:v 3 -vf dedot,nlmeans=1:7:7:3:3,tvai_up=model=prob-3:scale=0:w={}:h={}:preblur=0:noise=0.2:details=0.25:halo=0:blur=0.2:compression=0:estimate=20:prenoise=0.03:device=0:vram=1:instances=1,noise=c0s=25:c0f=t+u,scale={}:{}:out_color_matrix=bt709:flags=lanczos,lut3d={},crop=trunc({}*1.33333):{},pad={}:{}:-1:-1:color=black {}".format(input_path,width,height,width,height,lut,height,height,width,height,output_path)
        print(ffmpeg_command)
        ## delay before processing
        if queue.empty():
            print ("Initializing, processing clip")
            time.sleep(5000)
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        items = iter(queue.queue)
        print (list(items))
        print("############################################################<- QUEUE")
        #Delay for Alchemist to finish 
        
        while not queue.empty():
            ffmpeg_command = queue.get()
            
            print ("processing clip")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print(">>>>>>>>>>>>>>Render Done<<<<<<<<<<<<<")

        
        

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)
        
######################################################################################start main
class WatcherGUI(tk.Frame):
    ##change these for deployment
    default_watch_directory = r"N:\engineering\alchemist\samurAI_watchfolders\wip"
    default_lutpath = r"C:\Program Files\Topaz Labs LLC\Topaz Video AI\samurai_unity_709_2_709_33x33_v01.cube"
    default_width = 1920
    default_height = 1080
    
    def __init__(self, master):
        super().__init__(master)
        self.master.title("SamurAI")
        
        # Set the window background to dark grey
        #self.master.config(bg="#323232")
        
        self.grid_columnconfigure(1, minsiz=700)
        # Create the widgets
        self.directory_label = tk.Label(self, text="Directory:")
        self.directory_entry = tk.Entry(self)
        self.directory_entry.insert(0, self.default_watch_directory)
        self.directory_entry.pack_propagate(False)
        self.directory_label.grid(row=0, column=0, sticky="w")
        self.directory_entry.grid(row=0, column=1, columnspan=4, sticky="ew")
        
        self.xaxis_label = tk.Label(self, text="X-axis:")
        self.xaxis_entry = tk.Entry(self)
        self.xaxis_entry.insert(0, self.default_width)
        self.xaxis_entry.pack_propagate(True)
        self.xaxis_label.grid(row=1, column=0, sticky="w")
        self.xaxis_entry.grid(row=1, column=1, sticky="w")
        
        self.yaxis_label = tk.Label(self, text="Y-axis:")
        self.yaxis_entry = tk.Entry(self)
        self.yaxis_entry.insert(0, self.default_height)
        self.yaxis_entry.pack_propagate(True)
        self.yaxis_label.grid(row=2, column=0, sticky="w")
        self.yaxis_entry.grid(row=2, column=1, sticky="w")
        
        self.lutpath_label = tk.Label(self, text="LUT path:")
        self.lutpath_entry = tk.Entry(self, state="normal")
        self.lutpath_entry.insert(0, self.default_lutpath)
        self.lutpath_entry.pack_propagate(True)
        self.lutpath_label.grid(row=3, column=0, sticky="w")
        self.lutpath_entry.grid(row=3, column=1, columnspan=4, sticky="ew")
        
        self.run_button = tk.Button(self, text="Run", command=self.run)
        self.run_button.config(background="red")
        self.run_button.grid(row=4, column=0, columnspan=2, sticky="ew")
        
        # Set the default size of the window
        self.master.geometry("800x150")

    def run(self):
        directory = self.directory_entry.get()
        xaxis = self.xaxis_entry.get()
        yaxis = self.yaxis_entry.get()
        lutpath = self.lutpath_entry.get()

        if lutpath is None:
            lutpath = self.default_lutpath
        if directory is None:
            directory = self.default_watch_directory

        watcher = Watcher(directory, xaxis, yaxis, lutpath)
        watcher.run(xaxis, yaxis, lutpath)

def main():
    root = tk.Tk()
    gui = WatcherGUI(root)
    gui.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

7/14 - Animation mode added

### Last edit 5:00pm 07/12/23 v16 - added animation button

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue
import tkinter as tk
import shutil

## initialize the queue
queue = Queue()

##Define usage
def usage():
    print("Usage: python samurAI_v10.py [options]")
    print("Options: watch_folder_path height width LUT_path")
    print("-h, --help: Show this help message")
    print("-v, --version: Show the version number")



class Watcher:

    

    def __init__(self, directory, xaxis, yaxis, lutpath, animation ):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        
        ##JD Vars
        self.xaxis = xaxis
        self.yaxis = yaxis
        self.lutpath = lutpath
        self.animation = animation
   
        
        

    def run(self, xaxis, yaxis, lutpath, animation):
        event_handler = FileSystemEventHandler()
        ## this is the important one
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        
        print("File created:", event.src_path)
        ##FFMPEG command variables
        input_path = event.src_path
        output_path = input_path.replace("\wip", "\out")
        temp_output = output_path.replace(".mov", "temp.mov")
        width = self.xaxis
        height = self.yaxis
        animation_toggle = self.animation
        
        ##copy and set lut        
        print(self.lutpath)
        lutname = os.path.basename(self.lutpath)
        if not os.path.exists(os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname)):
            shutil.copyfile(self.lutpath, os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname))
        lut = lutname
                
        
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -flush_packets 1 -sws_flags spline+accurate_rnd+full_chroma_int  -pix_fmt yuv422p10le -c:a copy -c:v prores -profile:v 3 -vf dedot,nlmeans=1:7:7:3:3,dedot=ct=0:tc=1,tvai_up=model=prob-3:scale=0:w={}:h={}:preblur=0:noise=0.2:details=0.25:halo=0:blur=0.2:compression=0:estimate=20:prenoise=0.03:device=0:vram=1:instances=1,noise=c0s=25:c0f=t+u,scale={}:{}:out_color_matrix=bt709:flags=lanczos,lut3d={},crop=trunc({}*1.33333):{},pad={}:{}:-1:-1:color=black {}".format(input_path,width,height,width,height,lut,height,height,width,height,output_path)
        
        if animation_toggle == True:
            print(animation_toggle)
            ffmpeg_command = "ffmpeg -hide_banner -nostdin -y -strict 2 -i {} -sws_flags spline+accurate_rnd+full_chroma_int -color_trc 1 -colorspace 1 -color_primaries 1 -filter_complex dedot,nlmeans=1:7:7:3:3,dedot=ct=0:tc=1,tvai_up=model=iris-1:scale=0:w={}:h={}:preblur=1:noise=1:details=0.4:halo=0.2:blur=0.35:compression=0:device=0:vram=1:instances=1,scale=w={}:h={}:flags=lanczos:threads=0,scale=out_color_matrix=bt709,lut3d={},crop=trunc({}*1.33333):{},pad={}:{}:-1:-1:color=black,split=2[out1][out2] -map [out1] -c:v prores_ks -profile:v 3 -vendor apl0 -quant_mat hq -bits_per_mb 1350 -pix_fmt yuv422p10le {} -map [out2] -c:v prores_ks -profile:v 3 -vendor apl0 -quant_mat hq -bits_per_mb 1350 -pix_fmt yuv422p10le -map 0:a -c:a copy {}".format(input_path,width,height,width,height,lut,height,height,width,height,temp_output,output_path)
            
            
        print(ffmpeg_command)
        
        ## delay before processing ---------------------------------------------
        if queue.empty():
            print ("Initializing, processing clip")
            time.sleep(10)
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        items = iter(queue.queue)
        print (list(items))
        print("############################################################<- QUEUE")
        #Delay for Alchemist to finish 
        
        while not queue.empty():
            ffmpeg_command = queue.get()
            
            print ("processing clip")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print(">>>>>>>>>>>>>>Render Done<<<<<<<<<<<<<")

        
        

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)
        
######################################################################################start main
class WatcherGUI(tk.Frame):
    ##change these for deployment
    default_watch_directory = r"N:\engineering\alchemist\samurAI_watchfolders\wip"
    default_lutpath = r"C:\Program Files\Topaz Labs LLC\Topaz Video AI\samurai_unity_709_2_709_33x33_v01.cube"
    default_width = 1920
    default_height = 1080
    
    def __init__(self, master):
        super().__init__(master)
        self.master.title("SamurAI")
        
        # Set the window background to dark grey
        #self.master.config(bg="#323232")
        
        self.grid_columnconfigure(1, minsiz=700)
        # Create the widgets
        self.directory_label = tk.Label(self, text="Directory:")
        self.directory_entry = tk.Entry(self)
        self.directory_entry.insert(0, self.default_watch_directory)
        self.directory_entry.pack_propagate(False)
        self.directory_label.grid(row=0, column=0, sticky="w")
        self.directory_entry.grid(row=0, column=1, columnspan=4, sticky="ew")
        
        self.xaxis_label = tk.Label(self, text="X-axis:")
        self.xaxis_entry = tk.Entry(self)
        self.xaxis_entry.insert(0, self.default_width)
        self.xaxis_entry.pack_propagate(True)
        self.xaxis_label.grid(row=1, column=0, sticky="w")
        self.xaxis_entry.grid(row=1, column=1, sticky="w")
        
        self.yaxis_label = tk.Label(self, text="Y-axis:")
        self.yaxis_entry = tk.Entry(self)
        self.yaxis_entry.insert(0, self.default_height)
        self.yaxis_entry.pack_propagate(True)
        self.yaxis_label.grid(row=2, column=0, sticky="w")
        self.yaxis_entry.grid(row=2, column=1, sticky="w")
        
        self.lutpath_label = tk.Label(self, text="LUT path:")
        self.lutpath_entry = tk.Entry(self, state="normal")
        self.lutpath_entry.insert(0, self.default_lutpath)
        self.lutpath_entry.pack_propagate(True)
        self.lutpath_label.grid(row=3, column=0, sticky="w")
        self.lutpath_entry.grid(row=3, column=1, columnspan=4, sticky="ew")
        
        # Add the checkbox
        self.animation_var = tk.BooleanVar()
        self.animation_checkbox = tk.Checkbutton(self, text="Animation", variable=self.animation_var)
        self.animation_checkbox.grid(row=4, column=0, sticky="w")
        
        self.run_button = tk.Button(self, text="Run", command=self.run)
        self.run_button.config(background="red")
        self.run_button.grid(row=5, column=0, columnspan=2, sticky="ew")
        
        
        # Set the default size of the window
        self.master.geometry("800x150")

    def run(self):
        directory = self.directory_entry.get()
        xaxis = self.xaxis_entry.get()
        yaxis = self.yaxis_entry.get()
        lutpath = self.lutpath_entry.get()
        animation = self.animation_var.get()

        if lutpath is None:
            lutpath = self.default_lutpath
        if directory is None:
            directory = self.default_watch_directory

        watcher = Watcher(directory, xaxis, yaxis, lutpath, animation)
        watcher.run(xaxis, yaxis, lutpath, animation)
        
def main():
    root = tk.Tk()
    gui = WatcherGUI(root)
    gui.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

Samurai V17 final

### Last edit 5:00pm 07/12/23 v16 - added animation button

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue
import tkinter as tk
import shutil

## initialize the queue
queue = Queue()

##Define usage
def usage():
    print("Usage: python samurAI_v10.py [options]")
    print("Options: watch_folder_path height width LUT_path")
    print("-h, --help: Show this help message")
    print("-v, --version: Show the version number")



class Watcher:

    

    def __init__(self, directory, xaxis, yaxis, lutpath, animation, a_ratio):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        
        ##JD Vars
        self.xaxis = xaxis
        self.yaxis = yaxis
        self.lutpath = lutpath
        self.animation = animation
        self.a_ratio = a_ratio
   
        
        

    def run(self, xaxis, yaxis, lutpath, animation, a_ratio):
        event_handler = FileSystemEventHandler()
        ## this is the important one
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        
        print("File created:", event.src_path)
        ##FFMPEG command variables
        input_path = event.src_path
        output_path = input_path.replace("\wip", "\out")
        temp_output = output_path.replace(".mov", "temp.mov")
        width = self.xaxis
        height = self.yaxis
        crop = self.a_ratio
        animation_toggle = self.animation
        
        ##copy and set lut        
        print(self.lutpath)
        lutname = os.path.basename(self.lutpath)
        if not os.path.exists(os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname)):
            shutil.copyfile(self.lutpath, os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname))
        lut = lutname
                
        
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -flush_packets 1 -sws_flags spline+accurate_rnd+full_chroma_int  -pix_fmt yuv422p10le -c:a copy -c:v prores -profile:v 3 -vf dedot,nlmeans=1:7:7:3:3,dedot=ct=0:tc=1,tvai_up=model=prob-3:scale=0:w={}:h={}:preblur=0:noise=0.2:details=0.25:halo=0:blur=0.2:compression=0:estimate=20:prenoise=0.03:device=0:vram=1:instances=1,noise=c0s=25:c0f=t+u,scale={}:{}:out_color_matrix=bt709:flags=lanczos,lut3d={},crop=trunc({}*{}):{},pad={}:{}:-1:-1:color=black {}".format(input_path,width,height,width,height,lut,height,crop,height,width,height,output_path)
        
        if animation_toggle == True:
            print(animation_toggle)
            ffmpeg_command = "ffmpeg -hide_banner -nostdin -y -strict 2 -i {} -sws_flags spline+accurate_rnd+full_chroma_int -color_trc 1 -colorspace 1 -color_primaries 1 -filter_complex dedot,nlmeans=1:7:7:3:3,dedot=ct=0:tc=1,tvai_up=model=iris-1:scale=0:w={}:h={}:preblur=1:noise=1:details=0.4:halo=0.2:blur=0.35:compression=0:device=0:vram=1:instances=1,scale=w={}:h={}:flags=lanczos:threads=0,scale=out_color_matrix=bt709,lut3d={},noise=c0s=25:c0f=t+u,crop=trunc({}*{}):{},pad={}:{}:-1:-1:color=black,split=2[out1][out2] -map [out1] -c:v prores_ks -profile:v 3 -vendor apl0 -quant_mat hq -bits_per_mb 1350 -pix_fmt yuv422p10le {} -map [out2] -c:v prores_ks -profile:v 3 -vendor apl0 -quant_mat hq -bits_per_mb 1350 -pix_fmt yuv422p10le -map 0:a -c:a copy {}".format(input_path,width,height,width,height,lut,height,crop,height,width,height,temp_output,output_path)
            
            
        print(ffmpeg_command)
        
        ## delay before processing ---------------------------------------------
        if queue.empty():
            print ("Initializing, processing clip")
            
            ## logic for waiting until Alchemist is done
            last_modified_time = os.path.getmtime(input_path)
            current_time = time.time()

            while current_time - last_modified_time < 60:
                time.sleep(10)
                current_time = time.time()
                last_modified_time = os.path.getmtime(input_path)
            
        ## queue the command after a minute of not growing    
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        items = iter(queue.queue)
        print (list(items))
        print("############################################################<- QUEUE")
        #iter through the Queue
        
        while not queue.empty():
            ffmpeg_command = queue.get()
            
            print ("processing clip")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print(">>>>>>>>>>>>>>Render Done<<<<<<<<<<<<<")
            if os.path.exists(temp_output):
                os.remove(temp_output)

        
        

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)
        
######################################################################################start main
class WatcherGUI(tk.Frame):
    ##change these for deployment
    default_watch_directory = r"N:\engineering\alchemist\samurAI_watchfolders\wip"
    default_lutpath = r"C:\Program Files\Topaz Labs LLC\Topaz Video AI\samurai_unity_709_2_709_33x33_v01.cube"
    default_width = 1920
    default_height = 1080
    default_a_ratio = 1.33333333333
    
    def __init__(self, master):
        super().__init__(master)
        self.master.title("SamurAI")
        
        # Set the window background to dark grey
        #self.master.config(bg="#323232")
        
        self.grid_columnconfigure(1, minsiz=700)
        # Create the widgets
        self.directory_label = tk.Label(self, text="Watch Directory:")
        self.directory_entry = tk.Entry(self)
        self.directory_entry.insert(0, self.default_watch_directory)
        self.directory_entry.pack_propagate(False)
        self.directory_label.grid(row=0, column=0, sticky="w")
        self.directory_entry.grid(row=0, column=1, columnspan=4, sticky="ew")
        
        self.xaxis_label = tk.Label(self, text="Output X-axis:")
        self.xaxis_entry = tk.Entry(self)
        self.xaxis_entry.insert(0, self.default_width)
        self.xaxis_entry.pack_propagate(True)
        self.xaxis_label.grid(row=1, column=0, sticky="w")
        self.xaxis_entry.grid(row=1, column=1, sticky="w")
        
        self.yaxis_label = tk.Label(self, text="Output Y-axis:")
        self.yaxis_entry = tk.Entry(self)
        self.yaxis_entry.insert(0, self.default_height)
        self.yaxis_entry.pack_propagate(True)
        self.yaxis_label.grid(row=2, column=0, sticky="w")
        self.yaxis_entry.grid(row=2, column=1, sticky="w")
        
        self.a_ratio_label = tk.Label(self, text="Original Ratio:")
        self.a_ratio_entry = tk.Entry(self)
        self.a_ratio_entry.insert(0, self.default_a_ratio)
        self.a_ratio_entry.pack_propagate(True)
        self.a_ratio_label.grid(row=3, column=0, sticky="w")
        self.a_ratio_entry.grid(row=3, column=1, sticky="w")
        
        self.lutpath_label = tk.Label(self, text="LUT path:")
        self.lutpath_entry = tk.Entry(self, state="normal")
        self.lutpath_entry.insert(0, self.default_lutpath)
        self.lutpath_entry.pack_propagate(True)
        self.lutpath_label.grid(row=4, column=0, sticky="w")
        self.lutpath_entry.grid(row=4, column=1, columnspan=4, sticky="ew")
        
        # Add the checkbox
        self.animation_var = tk.BooleanVar()
        self.animation_checkbox = tk.Checkbutton(self, text="Animation", variable=self.animation_var)
        self.animation_checkbox.grid(row=5, column=0, sticky="w")
        
        self.run_button = tk.Button(self, text="Run", command=self.run)
        self.run_button.config(background="red")
        self.run_button.grid(row=6, column=0, columnspan=2, sticky="ew")
        
        
        # Set the default size of the window
        self.master.geometry("800x180")

    def run(self):
        directory = self.directory_entry.get()
        xaxis = self.xaxis_entry.get()
        yaxis = self.yaxis_entry.get()
        lutpath = self.lutpath_entry.get()
        animation = self.animation_var.get()
        a_ratio = self.a_ratio_entry.get()

        if lutpath is None:
            lutpath = self.default_lutpath
        if directory is None:
            directory = self.default_watch_directory

        watcher = Watcher(directory, xaxis, yaxis, lutpath, animation, a_ratio)
        watcher.run(xaxis, yaxis, lutpath, animation, a_ratio)
        
def main():
    root = tk.Tk()
    gui = WatcherGUI(root)
    gui.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

V23 smoke noise black artifact NG

### Last edit 12:15pm 09/06/23 v23 - reverting denoise to nlmeans=1:7:7:3:3

import time
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
from subprocess import Popen
from queue import Queue
import tkinter as tk
import shutil
import datetime
import logging
import io

## initialize the queue
queue = Queue()

##Define date_add
def date_add(filename):
  """
  This function adds the current date to the given filename and reconstructs the full path.

  Args:
    filename: The original filename.

  Returns:
    The new filename with the current date prepended to the head of the file and the full path reconstructed.
  """

  # Get the current date in the format "YYYYMMDD".
  current_date = datetime.datetime.now().strftime("%Y%m%d")

  # Split the filename into the base path and the file name.
  base_path, file_name = os.path.split(filename)

  # Prepend the current date to the file name.
  new_filename = f"{current_date}_{file_name}"

  # Reconstruct the full path with the new filename.
  full_path = os.path.join(base_path, new_filename)

  return full_path


##This module converts a movie path to a dpx one    
def modify_path(path):
  """Modifies the given path to the desired path."""
  folder_path = path.rsplit('.', 1)[0]
  last_directory = os.path.split(path)[1].rsplit('.', 1)[0]
  new_path = os.path.join(folder_path, last_directory + ".%07d.dpx")
  ## make the directory
  print ("new_path")
  print (new_path)
  
  if not os.path.exists(folder_path):
    os.makedirs(folder_path)
  return new_path
  

class Watcher:

    

    def __init__(self, directory, xaxis, yaxis, lutpath, animation, a_ratio, dpx, noise, proc_xaxis, proc_yaxis, sharpen, denoise):
        self.directory = directory
        self.observer = Observer()
        self.last_size = {}
        self.last_size_stopped = {}
        
        ##JD Vars
        self.xaxis = xaxis
        self.yaxis = yaxis
        self.proc_xaxis = proc_xaxis
        self.proc_yaxis = proc_yaxis
        self.lutpath = lutpath
        self.animation = animation
        self.a_ratio = a_ratio
        self.dpx = dpx
        self.noise = noise
        self.sharpen = sharpen
        self.denoise = denoise
        
        

    def run(self, xaxis, yaxis, lutpath, animation, a_ratio, dpx, noise, proc_xaxis, proc_yaxis, sharpen, denoise):
        event_handler = FileSystemEventHandler()
        ## this is the important one
        event_handler.on_created = self.on_created
        event_handler.on_deleted = self.on_deleted
        event_handler.on_copied = self.on_copied
        event_handler.on_size_changed = self.on_size_changed
        event_handler.on_finished_rendering = self.on_finished_rendering

        self.observer.schedule(event_handler, self.directory, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.observer.stop()
            print("Exiting...")

    ## this is the bit that does the most work.
    def on_created(self, event):
        # Create a logger
        logger = logging.getLogger(__name__)

        # Set the log level
        logger.setLevel(logging.INFO)

        # Create a file handler
        handler = logging.FileHandler(r"N:\engineering\alchemist\samurAI_watchfolders\samurai_support_files\log\samurai_log.txt")

        # Set the log format
        formatter = logging.Formatter('%(asctime)s - %(message)s')
        handler.setFormatter(formatter)

        # Add the handler to the logger
        logger.addHandler(handler)

        # Log a message
        #logger.info(sys.stdout)

        
        print("File created:", event.src_path)
        ##FFMPEG command variables
        input_path = event.src_path
        output_path = date_add(input_path.replace("\wip", "\out"))
        temp_output = output_path.replace(".mov", "temp.mov")
        
        width = self.xaxis
        height = self.yaxis
        crop = self.a_ratio
        animation_toggle = self.animation
        noise = self.noise
        sharpen = self.sharpen
        proc_xaxis = self.proc_xaxis
        proc_yaxis = self.proc_yaxis
        denoise = self.denoise
        
        
        ##copy and set lut        
        print(self.lutpath)
        lutname = os.path.basename(self.lutpath)
        if not os.path.exists(os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname)):
            shutil.copyfile(self.lutpath, os.path.join("C:\Program Files\Topaz Labs LLC\Topaz Video AI", lutname))
        lut = lutname
                
        ## live action logic
        ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -flush_packets 1 -sws_flags spline+accurate_rnd+full_chroma_int  -pix_fmt yuv422p10le -c:a copy -c:v prores -profile:v 3 -vf dedot=ct=0:tc=1,unsharp=3:3:{}:3:3:0.0,tvai_up=model=prob-3:scale=0:w={}:h={}:preblur=0:noise=0.2:details=0.25:halo=0:blur=0.2:compression=0:estimate=20:prenoise=0.03:device=0:vram=1:instances=1,scale={}:{}:out_color_matrix=bt709:flags=lanczos,lut3d={},noise=c0s={}:c0f=t,crop=trunc({}*{}):{},pad={}:{}:-1:-1:color=black {}".format(input_path,sharpen,proc_xaxis,proc_yaxis,width,height,lut,noise,height,crop,height,width,height,output_path)
        if self.dpx == True:
            dpx_output_path = modify_path(output_path)
            ffmpeg_command = "ffmpeg.exe -hide_banner -nostdin -y -strict 2 -hwaccel auto -i {} -flush_packets 1 -sws_flags spline+accurate_rnd+full_chroma_int -pix_fmt yuv422p10le -vf dedot=ct=0:tc=1,unsharp=3:3:{}:3:3:0.0,tvai_up=model=prob-3:scale=0:w={}:h={}:preblur=0:noise=0.2:details=0.25:halo=0:blur=0.2:compression=0:estimate=20:prenoise=0.03:device=0:vram=1:instances=1,scale={}:{}:out_color_matrix=bt709:flags=lanczos,lut3d={},noise=c0s={}:c0f=t,crop=trunc({}*{}):{},pad={}:{}:-1:-1:color=black -c:v dpx -pix_fmt gbrp10le -start_number 1 {}".format(input_path,sharpen,proc_xaxis,proc_yaxis,width,height,lut,noise,height,crop,height,width,height,dpx_output_path)
        
        ## animation logic
        if animation_toggle == True:
            print(animation_toggle)
            ffmpeg_command = "ffmpeg -hide_banner -nostdin -y -strict 2 -i {} -sws_flags spline+accurate_rnd+full_chroma_int -pix_fmt yuv422p10le -color_trc 1 -colorspace 1 -color_primaries 1 -filter_complex dedot=ct=0:tc=1,unsharp=3:3:{}:3:3:0.0,tvai_up=model=iris-1:scale=0:w={}:h={}:preblur=1:noise=1:details=0.4:halo=0.2:blur=0.35:compression=0:device=0:vram=1:instances=1,scale=w={}:h={}:flags=lanczos:threads=0,scale=out_color_matrix=bt709,lut3d={},noise=c0s={}:c0f=t,crop=trunc({}*{}):{},pad={}:{}:-1:-1:color=black,split=2[out1][out2] -map [out1] -c:v prores_ks -profile:v 3 -vendor apl0 -quant_mat hq -bits_per_mb 1350 -pix_fmt yuv422p10le {} -map [out2] -c:v prores_ks -profile:v 3 -vendor apl0 -quant_mat hq -bits_per_mb 1350 -pix_fmt yuv422p10le -map 0:a -c:a copy {}".format(input_path,sharpen,proc_xaxis,proc_yaxis,width,height,lut,noise,height,crop,height,width,height,temp_output,output_path)
            if self.dpx == True:
                print(self.dpx)
                ffmpeg_command = "ffmpeg -hide_banner -nostdin -y -strict 2 -i {} -sws_flags spline+accurate_rnd+full_chroma_int -pix_fmt yuv422p10le -color_trc 1 -colorspace 1 -color_primaries 1 -filter_complex dedot=ct=0:tc=1,unsharp=3:3:{}:3:3:0.0,tvai_up=model=iris-1:scale=0:w={}:h={}:preblur=1:noise=1:details=0.4:halo=0.2:blur=0.35:compression=0:device=0:vram=1:instances=1,scale=w={}:h={}:flags=lanczos:threads=0,scale=out_color_matrix=bt709,lut3d={},noise=c0s={}:c0f=t,crop=trunc({}*{}):{},pad={}:{}:-1:-1:color=black -c:v dpx -pix_fmt gbrp10le -start_number 1 {}".format(input_path,sharpen,proc_xaxis,proc_yaxis,width,height,lut,noise,height,crop,height,width,height,dpx_output_path)
        

        if denoise == True:
            ffmpeg_command = ffmpeg_command.replace("unsharp=3:3:{}:3:3:0.0","nlmeans=1:7:7:3:3,dedot,unsharp=3:3:{}:3:3:0.0")
        print(ffmpeg_command)
        
        ## delay before processing ---------------------------------------------
        if queue.empty():
            print ("Initializing, processing clip")
            
            ## logic for waiting until Alchemist is done
            last_modified_time = os.path.getmtime(input_path)
            current_time = time.time()

            while current_time - last_modified_time < 60:
                time.sleep(10)
                current_time = time.time()
                last_modified_time = os.path.getmtime(input_path)
            
        ## queue the command after a minute of not growing    
        queue.put(ffmpeg_command)
        print("############################################################<- QUEUE")
        items = iter(queue.queue)
        print (list(items))
        print("############################################################<- QUEUE")
        #iter through the Queue
        
        while not queue.empty():
            ffmpeg_command = queue.get()
            
            print ("processing clip")
            ## use this if you want a new window
            #subprocess.Popen(ffmpeg_command, creationflags=subprocess.CREATE_NEW_CONSOLE)
            subprocess.run(ffmpeg_command)
            print(">>>>>>>>>>>>>>Render Done<<<<<<<<<<<<<")
            
            # Get the current time.
            current_time = str(datetime.datetime.now())

            # Print the current time.
            print(output_path+" finished @ "+current_time)
            logger.info(output_path+" finished @ "+current_time+" rendered @ "+width+"X"+height)
            if os.path.exists(temp_output):
                os.remove(temp_output)

       

    def on_deleted(self, event):
        print("File deleted:", event.src_path)

    def on_copied(self, event):
        print("Copy done:", event.src_path)

    def on_size_changed(self, event):
        if event.src_path in Watcher.last_size:
            old_size = Watcher.last_size[event.src_path]
            new_size = event.src_size
            if new_size > old_size:
                print("File is growing:", event.src_path)
            else:
                if event.src_path not in Watcher.last_size_stopped:
                    Watcher.last_size_stopped[event.src_path] = event.src_size
                    print("File stopped growing:", event.src_path)
                del Watcher.last_size[event.src_path]
        Watcher.last_size[event.src_path] = event.src_size

    def on_finished_rendering(self, event):
        print("File finished rendering:", event.src_path)
        
######################################################################################start main
class WatcherGUI(tk.Frame):
    ##change these for deployment
    default_watch_directory = r"N:\engineering\alchemist\samurAI_watchfolders\wip"
    default_lutpath = r"C:\Program Files\Topaz Labs LLC\Topaz Video AI\samurai_unity_709_2_709_33x33_v01.cube"
    default_width = 1920
    default_height = 1080
    default_proc_width = 3840
    default_proc_height = 2160
    default_a_ratio = 1.333333333
    default_noise = 20
    default_sharpen = 0.0
    
    
    def __init__(self, master):
        super().__init__(master)
        self.master.title("SamurAI v23")
        
        # Set the window background to dark grey
        #self.master.config(bg="#323232")
        
        self.grid_columnconfigure(1, minsiz=700)
        # Create the widgets
        self.directory_label = tk.Label(self, text="Watch Directory:")
        self.directory_entry = tk.Entry(self)
        self.directory_entry.insert(0, self.default_watch_directory)
        self.directory_entry.pack_propagate(False)
        self.directory_label.grid(row=0, column=0, sticky="w")
        self.directory_entry.grid(row=0, column=1, columnspan=4, sticky="ew")
        
        self.xaxis_label = tk.Label(self, text="Output X-axis:")
        self.xaxis_entry = tk.Entry(self)
        self.xaxis_entry.insert(0, self.default_width)
        self.xaxis_entry.pack_propagate(True)
        self.xaxis_label.grid(row=1, column=0, sticky="w")
        self.xaxis_entry.grid(row=1, column=1, sticky="w")
        
        self.yaxis_label = tk.Label(self, text="Output Y-axis:")
        self.yaxis_entry = tk.Entry(self)
        self.yaxis_entry.insert(0, self.default_height)
        self.yaxis_entry.pack_propagate(True)
        self.yaxis_label.grid(row=2, column=0, sticky="w")
        self.yaxis_entry.grid(row=2, column=1, sticky="w")
        
        self.proc_xaxis_label = tk.Label(self, text="AI X-axis:")
        self.proc_xaxis_entry = tk.Entry(self)
        self.proc_xaxis_entry.insert(0, self.default_proc_width)
        self.proc_xaxis_entry.pack_propagate(True)
        self.proc_xaxis_label.grid(row=3, column=0, sticky="w")
        self.proc_xaxis_entry.grid(row=3, column=1, sticky="w")
        
        self.proc_yaxis_label = tk.Label(self, text="AI Y-axis:")
        self.proc_yaxis_entry = tk.Entry(self)
        self.proc_yaxis_entry.insert(0, self.default_proc_height)
        self.proc_yaxis_entry.pack_propagate(True)
        self.proc_yaxis_label.grid(row=4, column=0, sticky="w")
        self.proc_yaxis_entry.grid(row=4, column=1, sticky="w")
        
        self.a_ratio_label = tk.Label(self, text="Original Ratio:")
        self.a_ratio_entry = tk.Entry(self)
        self.a_ratio_entry.insert(0, self.default_a_ratio)
        self.a_ratio_entry.pack_propagate(True)
        self.a_ratio_label.grid(row=5, column=0, sticky="w")
        self.a_ratio_entry.grid(row=5, column=1, sticky="w")
        
        self.lutpath_label = tk.Label(self, text="LUT path:")
        self.lutpath_entry = tk.Entry(self, state="normal")
        self.lutpath_entry.insert(0, self.default_lutpath)
        self.lutpath_entry.pack_propagate(True)
        self.lutpath_label.grid(row=6, column=0, sticky="w")
        self.lutpath_entry.grid(row=6, column=1, columnspan=4, sticky="ew")
        
        self.noise_label = tk.Label(self, text="Noise Value:")
        self.noise_entry = tk.Entry(self, state="normal")
        self.noise_entry.insert(0, self.default_noise)
        self.noise_entry.pack_propagate(True)
        self.noise_label.grid(row=7, column=0, sticky="w")
        self.noise_entry.grid(row=7, column=1, sticky="w")
        
        self.sharpen_label = tk.Label(self, text="Sharpen Value:")
        self.sharpen_entry = tk.Entry(self, state="normal")
        self.sharpen_entry.insert(0, self.default_sharpen)
        self.sharpen_entry.pack_propagate(True)
        self.sharpen_label.grid(row=8, column=0, sticky="w")
        self.sharpen_entry.grid(row=8, column=1, sticky="w")
        
        # Add the Animation checkbox
        self.animation_var = tk.BooleanVar()
        self.animation_checkbox = tk.Checkbutton(self, text="Animation", variable=self.animation_var)
        self.animation_checkbox.grid(row=10, column=0, sticky="w")
        
        # Add the DPX checkbox
        self.dpx_var = tk.BooleanVar()
        self.dpx_checkbox = tk.Checkbutton(self, text="DPX", variable=self.dpx_var)
        self.dpx_checkbox.grid(row=9, column=0, sticky="w")
        
        # Add the denoise checkbox
        self.denoise_var = tk.BooleanVar()
        
        self.denoise_checkbox = tk.Checkbutton(self, text="Denoise", variable=self.denoise_var)
        self.denoise_checkbox.select()
        self.denoise_checkbox.grid(row=11, column=0, sticky="w")
        
        self.run_button = tk.Button(self, text="Run", command=self.run)
        self.run_button.config(background="red")
        self.run_button.grid(row=12, column=0, columnspan=2, sticky="ew")
        
        # Set the default size of the window
        self.master.geometry("800x315")

    def run(self):
        directory = self.directory_entry.get()
        xaxis = self.xaxis_entry.get()
        yaxis = self.yaxis_entry.get()
        proc_xaxis = self.proc_xaxis_entry.get()
        proc_yaxis = self.proc_yaxis_entry.get()
        lutpath = self.lutpath_entry.get()
        animation = self.animation_var.get()
        a_ratio = self.a_ratio_entry.get()
        dpx = self.dpx_var.get()
        noise = self.noise_entry.get()
        sharpen = self.sharpen_entry.get()
        denoise = self.denoise_var.get()
        

        if lutpath is None:
            lutpath = self.default_lutpath
        if directory is None:
            directory = self.default_watch_directory

        watcher = Watcher(directory, xaxis, yaxis, lutpath, animation, a_ratio, dpx, noise, proc_xaxis, proc_yaxis, sharpen, denoise)
        watcher.run(xaxis, yaxis, lutpath, animation, a_ratio, dpx, noise, proc_xaxis, proc_yaxis, sharpen, denoise)
        
def main():
    root = tk.Tk()
    gui = WatcherGUI(root)
    gui.pack()
    root.mainloop()

if __name__ == "__main__":
    main()