basic framework created.

This commit is contained in:
Falko Victor Habel 2024-03-10 16:31:27 +01:00
parent d4e0938f13
commit 56f42189aa
4 changed files with 529 additions and 3 deletions

View File

@ -1,4 +1,76 @@
# MPENN
# MPENN - Massive Picture Editing Tool for Neural Networks
MPENN is a versatile software that combines a video-to-picture converter with a powerful Massive Picture Editing Tool for Neural Networks.
With MPENN, you can quickly and efficiently edit and scale images in large quantities.
MPENN is a cutting-edge software solution designed to revolutionize the way we handle image processing and editing, particularly for applications involving neural networks. This versatile tool combines the functionality of a video-to-picture converter with a powerful editor, allowing for the quick and efficient manipulation and scaling of images in large volumes. Whether you're working on machine learning datasets, enhancing digital media assets, or simply need a robust tool for bulk image editing, MPENN is your go-to solution.
## Features
MPENN provides a suite of powerful features designed to streamline the process of converting, editing, and managing large sets of images. Here's what you can expect:
- **Video to Picture Sequence Conversion**: Effortlessly convert videos into sequences of high-quality images, ready for editing or analysis.
- **Fast Resize Capabilities**: Quickly adjust the sizes of your images to meet specific requirements without compromising on quality.
- **Bounding Boxes with Customization**: Annotate your images with bounding boxes. MPENN allows you to customize these boxes with a variety of colors and thicknesses to suit your needs.
- **Data Tracking and Management**: For each output folder, MPENN generates a detailed `.json` file. This file contains important information about each image, including bounding box details, making it easier to manage and analyze your data.
With these features, MPENN streamlines the workflow for users who need to process and analyze images at scale, particularly those working with neural networks and AI applications.
# Getting Started with MPENN
Welcome to MPENN, your comprehensive tool for massive picture editing for neural networks. Follow these steps to get up and running with MPENN, and begin transforming your videos and images into machine learning-ready datasets.
## 1. Download MPENN
First, you need to obtain the MPENN software. You can download it from its Gitea repository. Use the link below to navigate to the repository, and then download the latest release suited for your operating system.
https://gitea.example.com/mpenn/releases
*Note: Replace the URL above with the actual Gitea repository link.*
## 2. Setting Up Your Environment
After downloading MPENN, it's recommended to create a virtual environment (venv) for running the software. This ensures that your Python environment remains clean and organized. Open your terminal or command prompt and navigate to the MPENN directory. Then, run the following commands:
```bash
# Create a virtual environment named 'mpenn-env'
python -m venv mpenn-env
# Activate the virtual environment
# On Windows
mpenn-env\Scripts\activate
# On Unix or MacOS
source mpenn-env/bin/activate
# Install MPENN dependencies
pip install -r requirements.txt
```
## Running MPENN
With your environment set up, you're ready to launch MPENN. Run the `main.py` File to get started
### 1. Converting Video to Picture Sequence
If you're starting with a video, MPENN makes it easy to convert it into a sequence of images. Use the video-to-picture feature to specify your video file and the output directory for the images.
### 2. Processing Image Sequences
Whether you've generated your images from a video or already have a sequence of images, you can start editing them with MPENN. You can:
- Resize Images: Quickly adjust the resolution of your images to fit your requirements.
- Create Bounding Boxes: Annotate your images with bounding boxes for object detection tasks.
## Documentation
Will follow soon.
## License
MPENN is made available under the [Attribution-NonCommercial-ShareAlike 4.0](.../.././LICENSE). For more details, see the LICENSE file.
## Connect with Us
Stay updated with the latest news and updates about MPENN by following us on our social media channel(s):
- [LinkedIn](https://www.linkedin.com/in/FalkoHabel/)

262
main.py Normal file
View File

@ -0,0 +1,262 @@
import customtkinter as Ctk
from PIL import Image
import scripts.get_sys_info as system_code
from scripts.ClosePopup import ClosePopup
from scripts.settings import Settings
from scripts.converter import Converter
from scripts.labeling import Labeling
from scripts.folder_mangement.OpenFolder import OpenFolder
from scripts.folder_mangement.CreateFolder import CreateFolder
from scripts.folder_mangement.SwitchFolder import SwitchFolder
from scripts.SaveData import SaveData
SELECTION_TEXT = "Select your Directory and your Export Directory"
class App(Ctk.CTk):
def __init__(self):
super().__init__()
system_code.load_json_file()
system_code.set_theme()
self.my_font = Ctk.CTkFont(family="Berlin Sans FB", size=22)
self.font_entry = Ctk.CTkFont(family="Berlin Sans FB", size=18)
self.minsize(1000, 750)
self.geometry(f"{system_code.window_width}x{system_code.window_height}")
icon_path = "./icons/Program_Icon.ico"
self.iconbitmap(icon_path)
self.after(1, self.wm_state, system_code.window_state)
self.back_btn_image = Image.open(r"./icons/back.png")
self.title("MPENN")
self.protocol('WM_DELETE_WINDOW', self.close_attempt)
self.settings_frame = Settings(master=self)
self.converter_frame = Converter(master=self)
self.last_frame = None
self.popup = None
self.active_frame = None
self.Switch_folder = None
self.create_folder = None
self.open_folder = None
self.labeling = None
self.current_labeling_frame = None
self.last_opened_labeling_frame = None
self.window_informaton_save = ()
# Top Menu
# Widgets
self.settings_btn = Ctk.CTkButton(self, text="Settings", width=100, command=self.open_settings, font=self.my_font)
image = Ctk.CTkImage(self.back_btn_image, size=(50, 50))
self.back_btn = Ctk.CTkButton(self, text="", image=image,width=50, command=self.go_back, font=self.my_font)
self.window_information = Ctk.CTkLabel(self, text="Choose if you want to convert or label DATA", width=100, font=self.my_font)
# Alignment
self.place_top_menu()
self.show_main_menu()
# Open Main UI initially
def show_main_menu(self):
self.window_information.configure(text="Choose if you want to convert or label DATA")
self.buttons_frame = Ctk.CTkFrame(self)
self.active_frame = self.buttons_frame
# Main Menu
self.convert_btn = Ctk.CTkButton(self.buttons_frame, text="Convert Videos\ninto IMG-Sequences", command=self.open_converter, width=64, font=self.my_font)
self.label_btn = Ctk.CTkButton(self.buttons_frame, text="Label and\n manage Images", command=self.open_current_labeling_ui, width=64,font=self.my_font)
# Main Menu
self.active_frame.place(
relx=0.5, rely=0.52,relwidth=0.95, relheight=0.85, anchor="center"
)
self.convert_btn.place(
relx=0.15,
rely=0.5,
anchor="w",
relwidth=0.3,
relheight=0.1
) # Position the converter button to the left
self.label_btn.place(
relx=0.85,
rely=0.5,
anchor="e",
relwidth=0.3,
relheight=0.1
) # Position the label button to the right
def open_current_labeling_ui(self):
self.active_frame.place_forget()
if self.current_labeling_frame == None:
self.window_information.configure(text=SELECTION_TEXT)
self.Switch_folder = SwitchFolder(master=self, callback=self.open_labeling)
self.current_labeling_frame = self.Switch_folder
self.current_labeling_frame.place(relx=0.5, rely=0.52,relwidth=0.95, relheight=0.85, anchor="center")
else:
self.current_labeling_frame.place(relx=0.5, rely=0.52,relwidth=0.95, relheight=0.85, anchor="center")
self.active_frame = self.current_labeling_frame
self.last_frame = self.buttons_frame
def open_labeling(self, img_paths, output):
self.last_opened_labeling_frame = self.current_labeling_frame
self.last_frame = self.current_labeling_frame
self.current_labeling_frame.place_forget()
self.labeling = Labeling(master=self, img_paths=img_paths, output_path=output, callback=self.open_folder_uis, window_information=self.change_window_information)
self.current_labeling_frame = self.labeling
self.active_frame = self.current_labeling_frame
self.place_ui()
def reopen_labeling(self, output):
self.last_opened_labeling_frame = self.current_labeling_frame
self.last_frame = self.current_labeling_frame
self.current_labeling_frame.place_forget()
self.labeling.update_active_output_path(output)
self.current_labeling_frame = self.labeling
self.active_frame = self.current_labeling_frame
self.place_ui()
def open_folder_uis(self, output, variant): # variant: 0: CreateFolder 1: OpenFolder 3: SwitchFolder
self.last_opened_labeling_frame = self.current_labeling_frame
self.last_frame = self.current_labeling_frame
self.current_labeling_frame.place_forget()
if variant == 0:
self.window_information.configure(text="Create new folder in active directory")
self.create_folder = CreateFolder(master=self,output_path=output, callback=self.reopen_labeling)
self.current_labeling_frame = self.create_folder
self.active_frame = self.current_labeling_frame
elif variant == 1:
self.window_information.configure(text="Select new folder in active directory")
self.open_folder = OpenFolder(master=self, output_path=output, callback=self.reopen_labeling)
self.current_labeling_frame = self.open_folder
self.active_frame = self.current_labeling_frame
else:
self.window_information.configure(text=SELECTION_TEXT)
self.current_labeling_frame = self.Switch_folder
self.active_frame = self.current_labeling_frame
self.place_ui()
def open_settings(self):
if self.active_frame == self.settings_frame:
return
elif self.active_frame == self.buttons_frame:
self.last_frame = self.buttons_frame
elif self.active_frame == self.converter_frame:
self.last_frame = self.converter_frame
elif self.active_frame == self.Switch_folder:
self.last_frame = self.Switch_folder
elif self.active_frame == self.labeling:
self.last_frame = self.labeling
elif self.active_frame == self.create_folder:
self.last_frame = self.create_folder
elif self.active_frame == self.open_folder:
self.last_frame = self.open_folder
self.active_frame.place_forget()
self.window_information.configure(text="Settings")
self.last_opened_labeling_frame = self.current_labeling_frame
self.active_frame = self.settings_frame
self.place_ui()
def open_converter(self):
self.window_information.configure(text=SELECTION_TEXT)
self.last_frame = self.buttons_frame
self.buttons_frame.place_forget()
self.active_frame = self.converter_frame
self.place_ui()
def go_back(self):
if self.active_frame == self.buttons_frame:
return
elif self.active_frame == self.settings_frame:
system_code.save_setting_change()
if self.last_frame != self.buttons_frame:
self.active_frame.place_forget()
self.active_frame = self.last_frame
self.last_frame = None
self.place_ui()
else:
self.active_frame.place_forget()
self.active_frame = self.buttons_frame
self.place_ui()
elif self.should_switch_to_labeling():
self.switch_to_labeling()
elif self.active_frame == self.Switch_folder:
self.active_frame.place_forget()
self.active_frame = self.buttons_frame
self.last_frame = None
self.last_opened_labeling_frame = self.Switch_folder
self.place_ui()
elif self.active_frame == self.converter_frame:
self.active_frame.place_forget()
self.active_frame = self.buttons_frame
self.last_frame = None
self.place_ui()
elif self.active_frame == self.labeling:
self.active_frame.place_forget()
self.active_frame = self.buttons_frame
self.last_frame = None
self.last_opened_labeling_frame = self.labeling
self.place_ui()
def should_switch_to_labeling(self):
return (
(self.open_folder is not None and self.open_folder.winfo_ismapped()) or
(self.create_folder is not None and self.create_folder.winfo_ismapped()) or
(self.Switch_folder is not None and self.Switch_folder.winfo_ismapped() and self.last_frame != self.buttons_frame)
)
def switch_to_labeling(self):
self.hide_and_prepare_last_labeling()
self.change_window_information(None)
self.active_frame.place_forget()
self.active_frame = self.labeling
self.place_ui()
def hide_and_prepare_last_labeling(self):
if self.current_labeling_frame:
self.current_labeling_frame.place_forget()
self.last_opened_labeling_frame = self.current_labeling_frame
self.current_labeling_frame = self.labeling
def change_window_information(self, value):
if value is not None:
self.window_informaton_save = value
self.window_information.configure(text=f"{self.window_informaton_save[0]} from {self.window_informaton_save[1]} -> {self.window_informaton_save[2]}")
def place_ui(self):
self.active_frame.place(relx=0.5, rely=0.52,relwidth=0.95, relheight=0.85, anchor="center")
def close_attempt(self):
if self.settings_frame.winfo_ismapped() or self.converter_frame.winfo_ismapped():
self.popup = ClosePopup(master=self, callback=self.close_program)
else:
system_code.save_windows_information(self.geometry(),self.state())
self.close_program()
def close_program(self):
self.destroy()
def place_top_menu(self):
self.settings_btn.place(
relx=0.015,
y=35,
anchor="w",
relwidth=0.1,
relheight=0.035
)
self.back_btn.place(
relx=0.13,
y=35,
anchor="w",
relwidth=0.035,
relheight=0.035
)
self.window_information.place(
relx=0.5,
y=35,
anchor="center",
relwidth=0.6,
relheight=0.095
)
app = App()
app.mainloop()

40
scripts/ClosePopup.py Normal file
View File

@ -0,0 +1,40 @@
import customtkinter as Ctk
from tkinter import messagebox
class ClosePopup(Ctk.CTkToplevel):
def __init__(self,master, callback, **kwargs, ):
super().__init__(master, **kwargs)
self.my_font = Ctk.CTkFont(family="Berlin Sans FB", size=22)
self.geometry("400x300")
self.resizable(False, False)
self.callback = callback
self.label = Ctk.CTkLabel(self, text="Do you want to leave?\n You might lose some Data", font=self.my_font)
# Add exit button
self.exit_button = Ctk.CTkButton(self, text="Exit",fg_color="#bd202d",hover_color="#f24150", command=self.confirm_exit, font=self.my_font)
# Add leave button
self.leave_button = Ctk.CTkButton(self, text="Stay", command=self.destroy, font=self.my_font)
self.grab_set()
#aligning
self.align()
# Confirm exit method
def confirm_exit(self):
self.destroy() # Closes the ToplevelWindow
self.callback()
def align(self):
self.label.place(
relx=0.5,
rely=0.4,
anchor="center",
)
self.exit_button.place(
relx=0.25,
rely=0.6,
anchor="center",
)
self.leave_button.place(
relx=0.75,
rely=0.6,
anchor="center",
)

152
scripts/get_sys_info.py Normal file
View File

@ -0,0 +1,152 @@
import os
import re
import customtkinter
import json
json_file_path = "./data/program.json"
window_width = None
window_height = None
window_state = None
btn_img_size = None
skipable_frames = None
img_format = None
img_format_options = (".gif", ".jpg", ".png", ".tif")
data_modes = ("Resize", "Labeling")
thread_switcher = ("automatic", "manual")
used_threads = None
thread_detection_mode = None
thread_options = []
data = {}
data_mode = None
color = None
thickness = None
def load_json_file():
global data, window_width, window_height, window_state, skipable_frames ,img_format, used_threads, thread_detection_mode, thread_options, btn_img_size, data_mode, color, thickness
# Check if the file exists
if not os.path.exists(json_file_path):
# Create the file with default values
data = {
"window_width": 800,
"window_height": 1000,
"window_state": "zoomed", # there are zoomed and normal
"btn_img_size": (75, 75),
"skipable_frames": 30,
"img_format": ".jpg",
"used_threads": calculate_automatic_threads(),
"thread_detection_method": "automatic",
"data_mode": "Resize",
"color": "#5f00c7",
"thickness": 4
}
with open(json_file_path, 'w') as f:
json.dump(data, f, indent=2)
else:
# Load the file
with open(json_file_path, 'r') as f:
data = json.load(f)
window_width = data["window_width"]
window_height = data["window_height"]
window_state = data["window_state"]
btn_img_size = data["btn_img_size"]
skipable_frames = data["skipable_frames"]
img_format = data["img_format"]
used_threads = data["used_threads"]
thread_detection_mode = data["thread_detection_method"]
data_mode = data["data_mode"]
color = data["color"]
thickness = data["thickness"]
thread_options = set_thread_options()
def calculate_automatic_threads():
global used_threads
pc_threads = os.cpu_count()
used_threads = int(pc_threads) // 4
return used_threads
def set_thread_options():
global used_threads, thread_options
count = os.cpu_count() - 2
thread_options = [i for i in range(2, count, 2)]
return thread_options
def save_setting_change():
global data, used_threads, thread_detection_mode, img_format, data_mode, color, thickness
# Get current window information
if thread_detection_mode == "manual":
thread_data = {
"used_threads": used_threads,
}
else:
thread_data = {
"used_threads": calculate_automatic_threads(),
}
print("hello", data_mode)
save_data = {
"img_format": img_format,
"skipable_frames": skipable_frames,
"thread_detection_method": thread_detection_mode,
"data_mode": data_mode,
"color": color,
"thickness": thickness,
}
save_data.update(thread_data)
# Update only the changed values
for key in save_data:
if data[key] != save_data[key]:
data[key] = save_data[key]
# Save the updated JSON save_data
with open(json_file_path, 'w') as f:
json.dump(data, f, indent=2)
def save_windows_information(geometry, state):
global data
print(data)
"""Save window information to JSON file."""
width, height, _, _ = map(int, re.findall(r'\d+', geometry))
# Get current window information
if state == "zoomed":
window_info = {
"window_width": 800,
"window_height": 1000,
"window_state": state,
}
else:
window_info = {
'window_width': width,
'window_height': height,
"window_state": state,
}
# Update only the changed values
for key in window_info:
if data[key] != window_info[key]:
data[key] = window_info[key]
# Save the updated JSON data
with open(json_file_path, 'w') as f:
json.dump(data, f, indent=2)
def save_img_btn_size(btn_size):
global data
window_info = {
"btn_img_size": btn_size,
}
# Update only the changed values
for key in window_info:
if data[key] != window_info[key]:
data[key] = window_info[key]
# Save the updated JSON data
with open(json_file_path, 'w') as f:
json.dump(data, f, indent=2)
def set_theme():
# Get the current working directory
current_dir = os.getcwd()
# Create the path to the Json file
theme_path = os.path.join(current_dir, 'data', 'MPENN_theme.json')
customtkinter.set_appearance_mode("System") # Modes: system (default), light, dark
customtkinter.set_default_color_theme(theme_path)