opencv-gui / src /tkinter_app.py
samuellimabraz's picture
Add initial project structure with OpenCV and Mediapipe integration
ae1809b unverified
raw
history blame
22.7 kB
import cv2
import numpy as np
import time
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk
from opencv_utils import OpenCVUtils
class MainWindow:
def __init__(self, root: Tk) -> None:
self.root = root
self.font = ("Arial", 12, "bold")
self.font_small = ("Arial", 10, "bold")
self.colors = {
"yellow": "#FDCE01",
"black": "#1E1E1E",
"white": "#FEFEFE",
}
self.congig_interface()
self.root.bind("<q>", self.close_application)
self.functions = []
self.aplication = OpenCVUtils()
self.fps_avg_frame_count = 30
self.COUNTER, self.FPS = 0, 0
self.START_TIME = time.time()
# For optical flow
self.prev_gray = None
def close_application(self, event) -> None:
"""
Close the application
:param event: The event that triggered the function
"""
# Libera a webcam e destrói todas as janelas do OpenCV
self.cap.release()
cv2.destroyAllWindows()
self.root.destroy()
def congig_interface(self) -> None:
self.root.geometry("1500x1000")
self.root.title("OpenCV + Tkinter")
self.root.config(bg=self.colors["black"])
self.paned_window = PanedWindow(self.root, orient=HORIZONTAL)
self.paned_window.pack(fill=BOTH, expand=1)
# Cria a barra lateral com os sliders
self.sidebar = Frame(
self.paned_window,
width=700,
bg=self.colors["black"],
background=self.colors["black"],
padx=10,
pady=10,
)
self.paned_window.add(self.sidebar)
# Create a scrollbar for the sidebar
canvas = Canvas(self.sidebar, bg=self.colors["black"], highlightthickness=0)
scrollbar = Scrollbar(self.sidebar, orient="vertical", command=canvas.yview)
scrollable_frame = Frame(
canvas,
bg=self.colors["black"],
)
scrollable_frame.bind(
"<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
# Cria as trackbars
self.color_filter_var = IntVar()
self.color_filter_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.apply_color_filter, self.color_filter_var
),
)
Checkbutton(
scrollable_frame,
text="Color Filter",
variable=self.color_filter_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
self.lower_hue = Scale(
scrollable_frame,
from_=0,
to=180,
orient=HORIZONTAL,
label="Lower Hue",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.lower_hue.pack(anchor="center")
self.upper_hue = Scale(
scrollable_frame,
from_=0,
to=180,
orient=HORIZONTAL,
label="Upper Hue",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.upper_hue.pack(anchor="center")
self.lower_saturation = Scale(
scrollable_frame,
from_=0,
to=255,
orient=HORIZONTAL,
label="Lower Sat",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.lower_saturation.pack(anchor="center")
self.upper_saturation = Scale(
scrollable_frame,
from_=0,
to=255,
orient=HORIZONTAL,
label="Upper Sat",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.upper_saturation.pack(anchor="center")
self.lower_value = Scale(
scrollable_frame,
from_=0,
to=255,
orient=HORIZONTAL,
label="Lower Value",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.lower_value.pack(anchor="center")
self.upper_value = Scale(
scrollable_frame,
from_=0,
to=255,
orient=HORIZONTAL,
label="Upper Value",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.upper_value.pack(anchor="center")
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.canny_var = IntVar()
self.canny_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.apply_edge_detection, self.canny_var
),
)
Checkbutton(
scrollable_frame,
text="Canny",
variable=self.canny_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
self.lower_canny = Scale(
scrollable_frame,
from_=0,
to=255,
orient=HORIZONTAL,
label="Lower Canny",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.lower_canny.pack(anchor="center")
self.upper_canny = Scale(
scrollable_frame,
from_=0,
to=255,
orient=HORIZONTAL,
label="Upper Canny",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.upper_canny.pack(anchor="center")
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.blur_var = IntVar()
self.blur_var.trace_add(
"write",
lambda *args: self.add_function(self.aplication.blur_image, self.blur_var),
)
Checkbutton(
scrollable_frame,
text="Blur",
variable=self.blur_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack(anchor="center")
self.blur = Scale(
scrollable_frame,
from_=1,
to=15,
orient=HORIZONTAL,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.blur.pack(anchor="center")
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.rotation_var = IntVar()
self.rotation_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.rotate_image, self.rotation_var
),
)
Checkbutton(
scrollable_frame,
text="Rotation",
variable=self.rotation_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack(anchor="center")
self.rotation_angle = Scale(
scrollable_frame,
from_=0,
to=360,
orient=HORIZONTAL,
label="Rotation Angle",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.rotation_angle.pack(anchor="center")
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.resize_var = IntVar()
self.resize_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.resize_image, self.resize_var
),
)
Checkbutton(
scrollable_frame,
text="Resize",
variable=self.resize_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
Label(
scrollable_frame,
text="Height",
bg=self.colors["black"],
fg=self.colors["white"],
).pack()
self.height = Scale(
scrollable_frame,
from_=100,
to=1080,
orient=HORIZONTAL,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.height.pack(anchor="center")
self.width = Scale(
scrollable_frame,
from_=100,
to=1920,
orient=HORIZONTAL,
label="Width",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.width.pack(anchor="center")
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.contour_var = IntVar()
self.contour_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.apply_contour_detection, self.contour_var
),
)
Checkbutton(
scrollable_frame,
text="Contour",
variable=self.contour_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
# Add new OpenCV functions
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.hist_equal_var = IntVar()
self.hist_equal_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.equalize_histogram, self.hist_equal_var
),
)
Checkbutton(
scrollable_frame,
text="Histogram Equalization",
variable=self.hist_equal_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.adaptive_threshold_var = IntVar()
self.adaptive_threshold_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.adaptive_threshold, self.adaptive_threshold_var
),
)
Checkbutton(
scrollable_frame,
text="Adaptive Threshold",
variable=self.adaptive_threshold_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.morphology_var = IntVar()
self.morphology_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.morphology, self.morphology_var
),
)
Checkbutton(
scrollable_frame,
text="Morphology",
variable=self.morphology_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
# Morphology operation options
self.morph_op_var = StringVar(value="erode")
Label(
scrollable_frame,
text="Operation",
bg=self.colors["black"],
fg=self.colors["white"],
).pack()
for op in ["erode", "dilate", "open", "close"]:
Radiobutton(
scrollable_frame,
text=op.capitalize(),
variable=self.morph_op_var,
value=op,
bg=self.colors["black"],
fg=self.colors["white"],
selectcolor=self.colors["black"],
highlightbackground=self.colors["black"],
).pack(anchor="w")
self.morph_kernel_size = Scale(
scrollable_frame,
from_=1,
to=31,
orient=HORIZONTAL,
label="Kernel Size",
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
)
self.morph_kernel_size.set(5)
self.morph_kernel_size.pack(anchor="center")
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.sharpen_var = IntVar()
self.sharpen_var.trace_add(
"write",
lambda *args: self.add_function(self.aplication.sharpen, self.sharpen_var),
)
Checkbutton(
scrollable_frame,
text="Sharpen",
variable=self.sharpen_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.hough_lines_var = IntVar()
self.hough_lines_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.hough_lines, self.hough_lines_var
),
)
Checkbutton(
scrollable_frame,
text="Hough Lines",
variable=self.hough_lines_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.optical_flow_var = IntVar()
self.optical_flow_var.trace_add(
"write",
lambda *args: self.add_function(
self.process_optical_flow, self.optical_flow_var
),
)
Checkbutton(
scrollable_frame,
text="Optical Flow",
variable=self.optical_flow_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.pencil_sketch_var = IntVar()
self.pencil_sketch_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.pencil_sketch, self.pencil_sketch_var
),
)
Checkbutton(
scrollable_frame,
text="Pencil Sketch",
variable=self.pencil_sketch_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.color_quantization_var = IntVar()
self.color_quantization_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.color_quantization, self.color_quantization_var
),
)
Checkbutton(
scrollable_frame,
text="Color Quantization",
variable=self.color_quantization_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.hand_tracker_var = IntVar()
self.hand_tracker_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.detect_hands, self.hand_tracker_var
),
)
Checkbutton(
scrollable_frame,
text="Hand Tracker",
variable=self.hand_tracker_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
ttk.Separator(scrollable_frame, orient=HORIZONTAL).pack(fill=X, padx=3, pady=3)
self.face_tracker_var = IntVar()
self.face_tracker_var.trace_add(
"write",
lambda *args: self.add_function(
self.aplication.detect_faces, self.face_tracker_var
),
)
Checkbutton(
scrollable_frame,
text="Face Tracker",
variable=self.face_tracker_var,
font=self.font,
bg=self.colors["black"],
fg=self.colors["white"],
highlightbackground=self.colors["black"],
selectcolor=self.colors["black"],
).pack()
# Cria o label para exibir a imagem
self.image_label = Label(self.paned_window, bg=self.colors["black"])
self.paned_window.add(self.image_label)
def add_function(self, function: callable, var: IntVar) -> None:
"""
Add or remove a function from the list of functions to be applied to the image
:param function: The function to be added or removed
:param var: The variable that controls the function
"""
if var.get() == 1:
self.functions.append(function)
else:
self.functions.remove(function)
def process_optical_flow(self, frame: np.ndarray) -> np.ndarray:
"""
Special handler for optical flow which needs to track previous frames
:param frame: The current frame
:return: The processed frame with optical flow
"""
curr_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
if self.prev_gray is not None:
frame = self.aplication.optical_flow(self.prev_gray, curr_gray, frame)
self.prev_gray = curr_gray
return frame
def process_image(self, frame: np.ndarray) -> np.ndarray:
"""
Process the image with the functions selected by the user
:param frame: The image to be processed
:return: The processed image
"""
function_dict = {
self.aplication.apply_color_filter: [
(
self.lower_hue.get(),
self.lower_saturation.get(),
self.lower_value.get(),
),
(
self.upper_hue.get(),
self.upper_saturation.get(),
self.upper_value.get(),
),
],
self.aplication.apply_edge_detection: [
self.lower_canny.get(),
self.upper_canny.get(),
],
self.aplication.blur_image: [self.blur.get()],
self.aplication.rotate_image: [self.rotation_angle.get()],
self.aplication.resize_image: [self.width.get(), self.height.get()],
self.aplication.morphology: [
self.morph_op_var.get(),
self.morph_kernel_size.get(),
],
}
for function in self.functions:
args = function_dict.get(function, [])
frame = function(frame, *args)
return frame
def run(self) -> None:
"""
Run the main loop of the tkinter application
"""
# Abre a webcam
self.cap = cv2.VideoCapture(0)
self.START_TIME = time.time()
while True:
# Lê um frame da webcam
ret, frame = self.cap.read()
if not ret:
break
# Aplica as funções do OpenCV
frame = self.process_image(frame)
output = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
if self.COUNTER % self.fps_avg_frame_count == 0:
self.FPS = self.fps_avg_frame_count / (time.time() - self.START_TIME)
self.START_TIME = time.time()
self.COUNTER += 1
# Show the FPS
fps_text = "FPS = {:.1f}".format(self.FPS)
cv2.putText(
output,
fps_text,
(24, 30),
cv2.FONT_HERSHEY_DUPLEX,
1,
(0, 0, 0),
1,
cv2.LINE_AA,
)
# Converte a imagem NumPy para uma imagem PIL
pil_image = Image.fromarray(output)
# Converte a imagem PIL para uma imagem Tkinter
tk_image = ImageTk.PhotoImage(pil_image)
# Exibe a imagem no label
self.image_label.config(image=tk_image)
self.image_label.image = tk_image
# Atualiza a janela tkinter
self.root.update()
cv2.waitKey(1)
def main():
# Cria a janela principal
root = Tk()
main_window = MainWindow(root)
main_window.run()
if __name__ == "__main__":
main()