basic framework created.
This commit is contained in:
parent
d4e0938f13
commit
56f42189aa
78
README.md
78
README.md
|
@ -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/)
|
||||
|
|
|
@ -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()
|
|
@ -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",
|
||||
)
|
|
@ -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)
|
Loading…
Reference in New Issue