diff --git a/.idea/.gitignore b/.gitignore similarity index 61% rename from .idea/.gitignore rename to .gitignore index 26d3352..44ce839 100644 --- a/.idea/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ # Default ignored files /shelf/ /workspace.xml +.ipynb_checkpoints +.DS_Store diff --git a/.idea/GUI.iml b/.idea/GUI.iml index d0876a7..8388dbc 100644 --- a/.idea/GUI.iml +++ b/.idea/GUI.iml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/ImageAnnotationProject.iml b/.idea/ImageAnnotationProject.iml deleted file mode 100644 index e5d1e48..0000000 --- a/.idea/ImageAnnotationProject.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 8607595..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index edafe49..e3e6805 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index cfd3f44..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8842217 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# SEE-Segment TKinter gui framework + +This framework is the start of a Graphical User Interface to help segment images to establish ground truth. To use this tool run the following from python (requires TKinter): + +'''python combined.py''' + + diff --git a/.DS_Store b/ScrollAndZoom/.DS_Store similarity index 73% rename from .DS_Store rename to ScrollAndZoom/.DS_Store index c47e27a..ed544a3 100644 Binary files a/.DS_Store and b/ScrollAndZoom/.DS_Store differ diff --git a/combined.py b/combined.py new file mode 100644 index 0000000..b6aa71f --- /dev/null +++ b/combined.py @@ -0,0 +1,122 @@ +import tkinter as tk +from tkinter import filedialog +from PIL import Image, ImageTk + + +class FileOpening_and_Paint(): + def __init__(self, root = None): + self.root = root + #title of GUI window + self.root.title("BRUSH AND ERASE TOOL") + #size of GUI window + self.root.geometry("800x600") + #initial size of brush + self.brush_size = 10 + #initial color of brush + self.brush_color = "black" + #initial size of eraser tool + self.eraser_size = 10 + self.image = 0 + + self.create_widgets() + self.setup_bindings() + + def create_widgets(self): + #creates a canvas widget with white background + self.canvas = tk.Canvas(self.root, bg="white") + #packs canvas widget into main window + self.canvas.pack(fill=tk.BOTH, expand=True) + + self.button = tk.Button(self.root, text="Choose file to upload", command=self.browse_file) + self.button.pack(side="top") + #creates button widget + self.brush_button = tk.Button(self.root, text="Brush", command=self.set_brush_tool) + #packs button widget into main window + self.brush_button.pack(side="top") + #creates eraser button + self.eraser_button = tk.Button(self.root, text="Eraser", command=self.set_eraser_tool) + #packs eraser button into main window + self.eraser_button.pack(side="top") + #creates a Label widget with appropriate text and packs it into main window + brush_size_label = tk.Label(self.root, text="Brush Size:") + #label appears on the left side + brush_size_label.pack(side="top") + #creates horizontal scale widget with values ranging from 1 to 50 + self.brush_size_scale = tk.Scale(self.root, from_=1, to=50, orient=tk.HORIZONTAL, command=self.set_brush_size) + #sets brush size to whatever the value in the scale is + self.brush_size_scale.set(self.brush_size) + self.brush_size_scale.pack(side="top") + + + def browse_file(self): + filetypes = (("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("All files", "*.*")) + filepath = filedialog.askopenfilename(title="Select an image file", filetypes=filetypes) + if filepath: + global image + image = Image.open(filepath) + image = image.convert("RGBA") + self.image = ImageTk.PhotoImage(image) + self.canvas.create_image(0, 0, anchor="nw", image=self.image) + + #sets up the event bindings for the canvas widget to enable drawing on the canvas with the brush tool + def setup_bindings(self): + #binds the left mouse button motion event to the draw_brush method when the mouse is moved while the left mouse button is held down + self.canvas.bind("", self.draw_brush) + #binds the left mouse button release event to the reset method when the left mouse button is released + self.canvas.bind("", self.reset) + + #sets the current tool to the brush tool + def set_brush_tool(self): + #sets the instance variable current_tool to the string "brush", indicating that the brush tool is currently selected + self.current_tool = "brush" + #changes the cursor for the canvas widget to a pencil icon + self.canvas.config(cursor="pencil") + + #sets the current tool to the eraser tool + def set_eraser_tool(self): + #sets the instance variable current_tool to the string "eraser", indicating that the eraser tool is currently selected + self.current_tool = "eraser" + #changes the cursor for the canvas widget to a tcross icon + self.canvas.config(cursor="tcross") + + #updates brush size with whatever the value in the scale is + def set_brush_size(self, size): + self.brush_size = int(size) + + #implements the drawing functionality for the brush and eraser tools, depending on the current tool selected by the user + def draw_brush(self, event): + #checks if the current tool selected is the brush tool + if self.current_tool == "brush": + #gets the x and y coordinates of the mouse pointer from the event object + x, y = event.x, event.y + #calculates the top-left corner of the oval to be drawn based on the current mouse position and the size of the brush + x1, y1 = (x - self.brush_size), (y - self.brush_size) + #calculates the bottom-right corner of the oval to be drawn based on the current mouse position and the size of the brush + x2, y2 = (x + self.brush_size), (y + self.brush_size) + #creates an oval shape on the canvas with the calculated coordinates, fill color and outline color specified + self.canvas.create_oval(x1, y1, x2, y2, fill=self.brush_color, outline=self.brush_color) + #checks if the current tool selected is the eraser tool + elif self.current_tool == "eraser": + #gets the x and y coordinates of the mouse pointer from the event object + x, y = event.x, event.y + #calculates the top-left corner of the rectangle to be drawn based on the current mouse position and the size of the eraser + x1, y1 = (x - self.eraser_size), (y - self.eraser_size) + #calculates the bottom-right corner of the rectangle to be drawn based on the current mouse position and the size of the eraser + x2, y2 = (x + self.eraser_size), (y + self.eraser_size) + #creates a white rectangle shape on the canvas with the calculated coordinates, hence erasing anything drawn + self.canvas.create_rectangle(x1, y1, x2, y2, fill="white", outline="white") + self.canvas.create_image(0, 0, anchor="nw", image=self.image) + + #called when the user releases the left mouse button after drawing on the canvas + def reset(self, event): + #ensures that there is a new brush stroke from the current mouse position + self.previous_point = None + + +#represents the main window frame of a Tkinter GUI application +root = tk.Tk() +#defines the behavior of the GUI application +paint = FileOpening_and_Paint(root) +# waits for user input and responds to it by calling the appropriate event handler functions +root.mainloop() + diff --git a/File opening/file_opening.py b/file_opening/file_opening.py similarity index 52% rename from File opening/file_opening.py rename to file_opening/file_opening.py index 5e5bf15..3e69185 100644 --- a/File opening/file_opening.py +++ b/file_opening/file_opening.py @@ -6,29 +6,35 @@ class FileOpening(tk.Frame): def __init__(self, master=None): super().__init__(master) - self.button = tk.Button(self, text="Choose file to upload", command=self.browse_file) + self.select_button = tk.Button(self, text="Choose file to upload", command=self.browse_file) + self.save_button = tk.Button(self, text="Save Image", command=self.save_image) self.label_image = tk.Label(self) self.master = master self.pack() self.create_widget() def create_widget(self): - self.button.pack(side="top") + self.select_button.pack(side="top") + self.save_button.pack(side="top") self.label_image.pack(side="top") def browse_file(self): filetypes = (("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("All files", "*.*")) filepath = filedialog.askopenfilename(title="Select an image file", filetypes=filetypes) if filepath: - image = Image.open(filepath) - photo = ImageTk.PhotoImage(image) - self.label_image.configure(image=photo) - self.label_image.image = photo + self.image = Image.open(filepath) + self.photo = ImageTk.PhotoImage(self.image) + self.label_image.configure(image=self.photo) + self.label_image.image = self.photo + + def save_image(self): + if hasattr(self, 'label_image'): + save_path = filedialog.asksaveasfilename(defaultextension='.jpg') + if save_path: + self.image.save(save_path) root = tk.Tk() root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight())) win = FileOpening(master=root) win.mainloop() - - diff --git a/updated_brush_erase/.DS_Store b/updated_brush_erase/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/updated_brush_erase/.DS_Store differ diff --git a/updated_brush_erase/test.py b/updated_brush_erase/test.py new file mode 100644 index 0000000..72b5b02 --- /dev/null +++ b/updated_brush_erase/test.py @@ -0,0 +1,166 @@ +import tkinter as tk +from tkinter import filedialog +from PIL import Image, ImageTk + + +class FileOpening_and_Paint(): + def __init__(self, root = None): + self.root = root + #title of GUI window + self.root.title("BRUSH AND ERASE TOOL") + #size of GUI window + self.root.geometry("800x600") + #initial size of brush + self.brush_size = 10 + #initial color of brush + self.brush_color = "black" + #initial size of eraser tool + self.eraser_size = 10 + #initialize image variable + self.image = 0 + #initialize a list which stores all events + self.event_list = [] + self.last_x = None + self.last_y = None + #settung up canvas and bindings + self.create_widgets() + self.setup_bindings() + + def create_widgets(self): + #creates a canvas widget with white background + self.canvas = tk.Canvas(self.root, bg="white") + #packs canvas widget into main window + self.canvas.pack(fill=tk.BOTH, expand=True) + #creates a button for the user to be able to choose a file to upload + self.button = tk.Button(self.root, text="Choose file to upload", command=self.browse_file) + self.button.pack(side="top") + #creates brush button widget + self.brush_button = tk.Button(self.root, text="Brush", command=self.set_brush_tool) + #packs button widget into main window + self.brush_button.pack(side="top") + #creates undo button + self.eraser_button = tk.Button(self.root, text="Undo", command=self.set_eraser_tool) + #packs undo button into main window + self.eraser_button.pack(side="top") + #creates an erase all button and packs it into the canvas + self.erase_all_button = tk.Button(self.root, text="Erase All", command=self.set_erase_all) + self.erase_all_button.pack(side="top") + #bar to set brush size + brush_size_label = tk.Label(self.root, text="Brush Size:") + #label appears on the left side + brush_size_label.pack(side="top") + #creates horizontal scale widget with values ranging from 1 to 50 + self.brush_size_scale = tk.Scale(self.root, from_=1, to=50, orient=tk.HORIZONTAL, command=self.set_brush_size) + #sets brush size to whatever the value in the scale is + self.brush_size_scale.set(self.brush_size) + self.brush_size_scale.pack(side="top") + + + def browse_file(self): + #allowed file paths + filetypes = (("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("All files", "*.*")) + filepath = filedialog.askopenfilename(title="Select an image file", filetypes=filetypes) + if filepath: + image = Image.open(filepath) + image = image.convert("RGBA") + self.image = ImageTk.PhotoImage(image) + #opens image into the canvas + self.canvas.create_image(0, 0, anchor="nw", image=self.image) + self.event_list = [] + + #sets up the event bindings for the canvas widget to enable drawing on the canvas with the brush tool + def setup_bindings(self): + #binds the left mouse button motion event to the draw_brush method when the mouse is moved while the left mouse button is held down + self.canvas.bind("", self.draw_brush) + #binds the left mouse button release event to the reset method when the left mouse button is released + self.canvas.bind("", self.reset) + + #sets the current tool to the brush tool + def set_brush_tool(self): + #sets the instance variable current_tool to the string "brush", indicating that the brush tool is currently selected + self.current_tool = "brush" + #changes the cursor for the canvas widget to a pencil icon + self.canvas.config(cursor="pencil") + + #sets the current tool to the eraser tool + def set_eraser_tool(self): + #sets the instance variable current_tool to the string "eraser", indicating that the eraser tool is currently selected + self.current_tool = "brush" + if self.event_list: + #undo the last brush stroke + self.remove_latest_event() + + def set_erase_all(self): + #erases everything on the campus and if there was an image, puts that back into the canvas + if self.image != 0: + self.canvas.create_image(0, 0, anchor="nw", image=self.image) + else: + self.canvas.delete("all") + self.event_list = [] + self.current_tool = "brush" + + #updates brush size with whatever the value in the scale is + def set_brush_size(self, size): + self.brush_size = int(size) + + def remove_latest_event(self): + #undoes last brush stroke + if (len(self.event_list) > 5): + self.event_list.pop() + self.event_list.pop() + self.event_list.pop() + self.event_list.pop() + self.event_list.pop() + else: + self.event_list.pop() + self.canvas.delete("all") + if self.image != 0: + self.canvas.create_image(0, 0, anchor="nw", image=self.image) + for i in self.event_list: + self.canvas.create_oval(i[0], i[1], i[2], i[3], fill=i[4], outline=i[4]) + + #implements the drawing functionality for the brush and eraser tools, depending on the current tool selected by the user + def draw_brush(self, event): + #checks if the current tool selected is the brush tool + if self.current_tool == "brush": + #gets the x and y coordinates of the mouse pointer from the event object + x, y = event.x, event.y + x1, y1 = (x - self.brush_size), (y - self.brush_size) + #calculates the bottom-right corner of the oval to be drawn based on the current mouse position and the size of the brush + x2, y2 = (x + self.brush_size), (y + self.brush_size) + #calculates the distance between the previous position and the current position + if (self.last_x != None) and (self.last_y != None): + distance = ((x - self.last_x) ** 2 + (y - self.last_y) ** 2) ** 0.5 + #calculates the step size and direction between the previous position and the current position + step_x = (x - self.last_x) / distance if distance > 0 else 0 + step_y = (y - self.last_y) / distance if distance > 0 else 0 + #draw a sequence of small ovals between the previous position and the current position + for i in range(int(distance)): + oval_x = int(self.last_x + i * step_x) + oval_y = int(self.last_y + i * step_y) + oval_x1, oval_y1 = (oval_x - self.brush_size), (oval_y - self.brush_size) + oval_x2, oval_y2 = (oval_x + self.brush_size), (oval_y + self.brush_size) + self.canvas.create_oval(oval_x1, oval_y1, oval_x2, oval_y2, fill=self.brush_color, outline=self.brush_color) + self.event_list.append((oval_x1, oval_y1, oval_x2, oval_y2, self.brush_color)) + else: + self.canvas.create_oval(x1, y1, x2, y2, fill=self.brush_color, outline=self.brush_color) + self.event_list.append((x1, y1, x2, y2, self.brush_color)) + #update the previous position with the current position + self.last_x, self.last_y = x, y + #checks if the current tool selected is the eraser tool + elif self.current_tool == "eraser": + self.set_eraser_tool() + + #called when the user releases the left mouse button after drawing on the canvas + def reset(self, event): + #ensures that there is a new brush stroke from the current mouse position + self.last_x = None + self.last_y = None + + +#represents the main window frame of a Tkinter GUI application +root = tk.Tk() +#defines the behavior of the GUI application +paint = FileOpening_and_Paint(root) +# waits for user input and responds to it by calling the appropriate event handler functions +root.mainloop() \ No newline at end of file