Esempio n. 1
0
class NewImageWindow(Toplevel):
    def __init__(self, master = None, pathToImage = None, name=None, image=None): 
        super().__init__(master = master)

        if pathToImage is not None and image is None:
            self.pathToImage = pathToImage
            self.image = ImageSaved(pathToImage)
        elif image is not None:
            self.image = image

        self.manager = StateManager(self.image.cv2Image)
        self.set_images()
        self.set_geometry()
        self.set_basic(master, name)
        self.place_menu()
        self.manage_line_profile()
        self.bind_functions()

    # BASICS
    def create_another(self, master, pathToImage, name, image):
        NewImageWindow(master, pathToImage, name, image)

    def set_geometry(self):
        self.minsize(self.imageFromArray.width, self.imageFromArray.height)
        self.geometry('{}x{}'.format(self.imageFromArray.width, self.imageFromArray.height)) 
    
    def set_images(self):        
        self.imageFromArray = Image.fromarray(self.image.cv2Image)
        self.imageCopy = self.imageFromArray.copy()

    def set_basic(self, master, name):
        self.focus_force()
        self.master = master
        self.name = name 
        self.title(name) 
        self.imagePanel = Canvas(self)
        self.imagePanel.place(relwidth=1, relheight = 1, x=0, y=0)

        self.profileWindow = None
        self.histogramWindow = None
        self.lutWindow = None
        self.thresholdScaleWindow = None
        self.posterizeWindow = None
        self.neighborWindow = None
        NewTwoArgsWindow.images[self] = self.name
        for widget in self.master.winfo_children():
            if(type(widget) == NewTwoArgsWindow):
                widget.update_list()
                break

    
    def manage_line_profile(self):
        self.lineCoords = {"x":0,"y":0,"x2":0,"y2":0}
        self.line = None

    def bind_functions(self):
        #Zmienić resize'owanie, aktualnie nie pasuje do linii profilu
        self.bind('<Configure>', self.resize_img)
        self.bind('<Control-d>', lambda event: self.duplicate_window())
        self.bind('<Control-z>', lambda event: self.undo())
        self.bind('<Control-y>', lambda event: self.redo())
        self.bind('<Control-s>', lambda event: self.save())
        self.bind("<ButtonPress-3>", self.click)
        self.bind("<B3-Motion>", self.drag)
        self.protocol("WM_DELETE_WINDOW", lambda:self.report_close_to_windows())

    def undo(self):
        self.image.cv2Image = self.manager.undo()
        self.image.copy = self.image.cv2Image
        self.image.fill_histogram()
        self.update_visible_image()
        self.update_child_windows()

    def save(self):
        try:
            cv2.imwrite(self.pathToImage, self.image.cv2Image)
        except AttributeError:
            self.save_as()

    def save_as(self):
        try:
            ext = self.image.file_extension
        except AttributeError:
            ext = '.bmp'
        finally:
            dir = ''
            dir = filedialog.asksaveasfilename(
            initialfile='*'+ext,
            defaultextension=ext, 
            filetypes=[
                ('JPEG', '*.jpg'), 
                ('PNG', '*.png'), 
                ('BMP', '*.bmp'), 
                ('GIF', '*.gif'),
                ]
            )
            if dir != '':
                try:
                    
                    cv2.imwrite(dir, self.image.cv2Image)
                    self.pathToImage = dir
                    self.name = os.path.split(dir)[1]
                    self.title(self.name)
                except cv2.error:
                    messagebox.showerror("Błąd", "Nieoczekiwany błąd, sprawdź czy dobrze wprowadziłeś nazwę pliku")


    def redo(self):
        self.image.cv2Image = self.manager.redo()
        self.image.copy = self.image.cv2Image
        self.image.fill_histogram()
        self.update_visible_image()
        self.update_child_windows()

    def report_close_to_windows(self):
        NewTwoArgsWindow.images.pop(self)
        for widget in self.master.winfo_children():
            if(type(widget) == NewTwoArgsWindow):
                widget.update_list()
                break
        self.destroy()
    # -------------------

    # WINDOW
    def duplicate_window(self):
        NewImageWindow(self.master, None, self.name + '(Kopia)', ImageSaved(None,self.image.cv2Image)).focus_set()
    
    def place_menu(self):
        topMenu = Menu(self)
        self.dsc = Menu(topMenu, tearoff=False)
        histMan = Menu(topMenu, tearoff=False)
        
        imageMan = Menu(topMenu, tearoff=False)
        imageAnalize = Menu(topMenu, tearoff=False)
        pointOper = Menu(imageMan, tearoff=False)

        neighborOper = Menu(imageMan, tearoff=False)
        smooth = Menu(neighborOper, tearoff=False)
        detectEdges = Menu(neighborOper, tearoff=False)
        prewitt = Menu(neighborOper, tearoff=False)
        sharpen = Menu(neighborOper, tearoff=False)
        medianM = Menu(neighborOper, tearoff=False)


        imageMan.add_cascade(label="Jednoargumentowe", menu=pointOper)
        imageMan.add_command(label="Dwuargumentowe", compound=LEFT, command=self.create_two_args_window)
        imageMan.add_cascade(label="Sąsiedztwa", menu=neighborOper)
        imageMan.add_command(label="Morfologiczne", compound=LEFT, command=self.create_morph_window)
        imageMan.add_command(label="Morfologiczna ekstrakcja linii", compound=LEFT, command=self.create_morph_line_window)
        imageMan.add_command(label="Watershed", compound=LEFT, command=self.handle_watershed)
        
        # Opcje OPISU
        self.dsc.add_command(label='Cofnij', compound=LEFT, accelerator='Ctrl+Z', command=self.undo)
        self.dsc.add_command(label='Powtórz', compound=LEFT, accelerator='Ctrl+Y', command=self.redo)
        self.dsc.add_command(label='Zapisz', accelerator='Ctrl+S', compound=LEFT, command=self.save)
        self.dsc.add_command(label='Zapisz jako...', accelerator='Ctrl+Shift+S', compound=LEFT, command=self.save_as)
        self.dsc.add_command(label='Histogram', compound=LEFT, command=self.create_histogram_window)
        self.dsc.add_command(label='LUT', compound=LEFT, command=self.create_lut_window)        
        self.dsc.add_command(label='Linia profilu', compound=LEFT, state=DISABLED, command=self.create_profile_window)

        # Opcje MANIPULACJI HISTOGRAMEM
        histMan.add_command(label="Rozciąganie", compound=LEFT, command=self.stretch_image)
        histMan.add_command(label="Rozciąganie przedziałami", compound=LEFT, command=self.create_stretching_window)
        histMan.add_command(label="Wyrównanie", compound=LEFT, command=self.equalize_image)

        # Opcje OPERACJI JEDNOARGUMENTOWYCH
        pointOper.add_command(label="Negacja", compound=LEFT, command=self.negate_image)
        pointOper.add_command(label="Progowanie", compound=LEFT, command=lambda:self.threshold_image("BASIC"))
        pointOper.add_command(label="Progowanie adaptacyjne", compound=LEFT, command=lambda:self.threshold_image("ADAPT"))
        pointOper.add_command(label="Progowanie Otsu", compound=LEFT, command=lambda:self.threshold_image("OTSU"))
        pointOper.add_command(label="Posteryzacja", compound=LEFT, command=self.posterize_image)

        # Opcje OPERACJI SĄSIEDZTWA
        neighborOper.add_cascade(label="Wygładzanie", menu=smooth)
        neighborOper.add_cascade(label="Detekcja krawędzi", menu=detectEdges)
        neighborOper.add_cascade(label="Wyostrzanie", menu=sharpen)
        neighborOper.add_cascade(label="Medianowe", menu=medianM)
        neighborOper.add_command(label="Własne", compound=LEFT, command=lambda:self.create_custom_mask_window(1))
        neighborOper.add_command(label="Własne splot", compound=LEFT, command=lambda:self.create_custom_mask_window(0))

        neighborOper.add_separator()
        neighborOper.add_command(label="OPCJE SKRAJNYCH PIKSELI", state='disabled', compound=LEFT)
        self.border = IntVar(self)
        self.border.set(2)
        neighborOper.add_radiobutton(label="Bez zmian (isolated)", variable=self.border, value=0)
        neighborOper.add_radiobutton(label="Odbicie lustrzane (reflect)", variable=self.border, value=1)
        neighborOper.add_radiobutton(label="Powielenie skrajnego piksela (replicate)", variable=self.border, value=2)
        
        smooth.add_command(label="Blur", compound=LEFT, command=lambda:self.handle_neighbor_operations("BLUR", self.border.get())) #POZMIENIAĆ COMMANDY
        smooth.add_command(label="Gaussian Blur", compound=LEFT, command=lambda:self.handle_neighbor_operations("GAUSSIAN", self.border.get()))

        detectEdges.add_command(label="Sobel", compound=LEFT, command=lambda:self.handle_neighbor_operations("SOBEL", self.border.get()))
        detectEdges.add_command(label="Laplasjan", compound=LEFT, command=lambda:self.handle_neighbor_operations("LAPLASJAN", self.border.get()))
        detectEdges.add_command(label="Canny", compound=LEFT, command=lambda:self.handle_neighbor_operations("CANNY", self.border.get()))
        detectEdges.add_cascade(label="Prewitt", menu=prewitt)
        
        prewitt.add_command(label="Prewitt N", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW N", self.border.get()))
        prewitt.add_command(label="Prewitt NE", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW NE", self.border.get()))
        prewitt.add_command(label="Prewitt E", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW E", self.border.get()))
        prewitt.add_command(label="Prewitt SE", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW SE", self.border.get()))
        prewitt.add_command(label="Prewitt S", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW S", self.border.get()))
        prewitt.add_command(label="Prewitt SW", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW SW", self.border.get()))
        prewitt.add_command(label="Prewitt W", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW W", self.border.get()))
        prewitt.add_command(label="Prewitt NW", compound=LEFT, command=lambda:self.handle_neighbor_operations("PRW NW", self.border.get()))

        sharpen.add_command(label="Laplasjan 0/-1/5", compound=LEFT, command=lambda:self.handle_neighbor_operations("LAPLASJAN 1", self.border.get()))
        sharpen.add_command(label="Laplasjan -1/-1/9", compound=LEFT, command=lambda:self.handle_neighbor_operations("LAPLASJAN 2", self.border.get()))
        sharpen.add_command(label="Laplasjan 1/-2/5", compound=LEFT, command=lambda:self.handle_neighbor_operations("LAPLASJAN 3", self.border.get()))

        medianM.add_command(label="Maska 3x3", compound=LEFT, command=lambda:self.handle_neighbor_operations("MEDIAN 3", self.border.get()))
        medianM.add_command(label="Maska 5x5", compound=LEFT, command=lambda:self.handle_neighbor_operations("MEDIAN 5", self.border.get()))
        medianM.add_command(label="Maska 7x7", compound=LEFT, command=lambda:self.handle_neighbor_operations("MEDIAN 7", self.border.get()))        

        # OPECJE ANALIZY
        imageAnalize.add_command(label="Wyznaczenie wektora cech obiektów", compound=LEFT, command=self.handle_objects_vector)
        imageAnalize.add_command(label="Klasyfikacja obiektów fasoli, ryżu i grochu", compound=LEFT, command=self.handle_classify)

        # DODANIE GŁÓWNYCH ZAKŁADEK
        topMenu.add_cascade(label="Obraz", menu=self.dsc)
        topMenu.add_cascade(label="Manipulacja histogramem", menu=histMan)
        topMenu.add_cascade(label="Operacje", menu=imageMan)
        topMenu.add_cascade(label="Analiza", menu=imageAnalize)

        self.config(menu = topMenu)
    
    def update_visible_image(self):
        self.set_images()
        self.photoImage = ImageTk.PhotoImage(self.imageFromArray)
        self.imagePanel.delete("all")
        self.imagePanel.create_image(0, 0, anchor=NW, image=self.photoImage)
    # -------------------

    # SET CHILD WINDOWS
    def create_moments_window(self, colours, moments, areas, lengths, aspectRatios, extents, solidities, equivalentDiameters):
        for widget in self.winfo_children():
            if(type(widget) == NewMomentsWindow):
                widget.lift()
                widget.focus_set()
                return
        wg = NewMomentsWindow(self, colours, moments, areas, lengths, aspectRatios, extents, solidities, equivalentDiameters)
        wg.focus_set()

    def create_morph_window(self):
        if not self.image.check_if_binary():
            messagebox.showerror("Błąd", "Obraz musi być binarny. Wykonaj posteryzację dla wartości 2 i spróbuj ponownie.")
            return
        for widget in self.winfo_children():
            if(type(widget) == NewMorphWindow):
                widget.lift()
                widget.focus_set()
                return
        wg = NewMorphWindow(self)
        wg.focus_set()

    def create_morph_line_window(self):
        for widget in self.winfo_children():
            if(type(widget) == NewMorphLineWindow):
                widget.lift()
                widget.focus_set()
                return
        wg = NewMorphLineWindow(self)
        wg.focus_set()

    def create_custom_mask_window(self, option):
        if option == 1:
            for widget in self.winfo_children():
                if(type(widget) == NewCustomMaskWindow):
                    widget.lift()
                    widget.focus_set()
                    return
            wg = NewCustomMaskWindow(self)
            wg.focus_set()
        elif option == 0:
            for widget in self.winfo_children():
                if(type(widget) == NewCustomMaskWindowConv):
                    widget.lift()
                    widget.focus_set()
                    return
            wg = NewCustomMaskWindowConv(self)
            wg.focus_set()

    def create_two_args_window(self):
        for widget in self.master.winfo_children():
            if(type(widget) == NewTwoArgsWindow):
                widget.lift()
                widget.focus_set()
                return
        wg = NewTwoArgsWindow(self.master)
        wg.focus_set()

    def create_profile_window(self):
        if(self.profileWindow is not None):
            self.profileWindow.lift()
            self.profileWindow.focus_set()
            return
        self.profileWindow = NewLineProfileWindow(self.name, self.lineCoords, self)
        
    def create_histogram_window(self):
        if(self.histogramWindow is not None):
            self.histogramWindow.lift()
            self.histogramWindow.focus_set()
            return
        self.histogramWindow = NewHistogramWindow(self.name, self)

    def create_stretching_window(self):
        for widget in self.winfo_children():
            if(type(widget) == NewCustomStretchWindow):
                widget.lift()
                widget.focus_set()
                return
        wg = NewCustomStretchWindow(self)
        wg.focus_set()

    def create_lut_window(self):
        if(self.lutWindow is not None):
            self.lutWindow.lift()
            self.lutWindow.focus_set()
            return
        self.lutWindow = NewLutWindow(self.name, self)

    def update_child_windows(self):
        if self.histogramWindow is not None:
            self.histogramWindow.update_histogram()

        if self.lutWindow is not None:
            self.lutWindow.display_lut_values()

        if self.profileWindow is not None:
            self.profileWindow.update_line(self.lineCoords)

    def resize_child_windows(self):
        offsetX = self.winfo_rootx()-self.winfo_x()
        offsetY = self.winfo_rooty()-self.winfo_y()
        if self.posterizeWindow is not None:
            self.posterizeWindow.geometry('%dx%d+%d+%d' % (self.winfo_width(), self.posterizeWindow.height, self.winfo_x()+offsetX, self.winfo_y()+self.winfo_height()+offsetY+2))
        if self.thresholdScaleWindow is not None:
            self.thresholdScaleWindow.geometry('%dx%d+%d+%d' % (self.thresholdScaleWindow.width, self.winfo_height(),self.winfo_x()+self.winfo_width()+offsetX+2, self.winfo_y()+offsetY))
    # -------------------

    # ONE CLICK OPERATIONS
    def handle_classify(self):
        self.image.classify()
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()
        messagebox.showinfo("LEGENDA", "Zielony = ryz \n Niebieski = soczewica \n Czerwony = fasola")

    def handle_objects_vector(self):
        colours, moments, areas, lengths, aspectRatios, extents, solidities, equivalentDiameters = self.image.get_objects_vector()
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()
        self.create_moments_window(colours, moments, areas, lengths, aspectRatios, extents, solidities, equivalentDiameters)

    def handle_watershed(self):
        self.image.my_watershed()
        self.image.copy = self.image.cv2Image
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()

    def equalize_image(self):
        self.image.equalize()
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()

    def stretch_image(self, oldMin=None, oldMax=None, newMini=None, newMaxi=None):
        self.image.stretch(oldMin, oldMax, newMini, newMaxi)
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()

    def negate_image(self):
        self.image.negate()
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()

    def threshold_image(self, method):
        if method == "BASIC":
            if self.thresholdScaleWindow is None:
                self.thresholdScaleWindow = NewSliderWindow(self)
            else:
                self.thresholdScaleWindow.cancel()
                self.thresholdScaleWindow = NewSliderWindow(self)
        elif method == "ADAPT":
            if self.thresholdScaleWindow is None:
                self.thresholdScaleWindow = NewAdaptiveSliderWindow(self)
            else:
                self.thresholdScaleWindow.cancel()
                self.thresholdScaleWindow = NewAdaptiveSliderWindow(self)
        elif method == "OTSU":
            self.image.threshold_otsu()
            self.manager.new_state(self.image.cv2Image)
            self.update_visible_image()
            self.update_child_windows()

    def posterize_image(self):
        if self.posterizeWindow is None:
            self.posterizeWindow = NewPosterizeWindow(self.name, self)
            self.manager.new_state(self.image.cv2Image)
            self.update_visible_image()
            self.update_child_windows()

    def handle_neighbor_operations(self, operation, borderOption):
        self.image.neighbor_operations(operation, borderOption)
        self.manager.new_state(self.image.cv2Image)
        self.update_visible_image()
        self.update_child_windows()
    # -------------------


    # ON EVENT
    def click(self, e):
        self.lineCoords["x"] = e.x
        self.lineCoords["y"] = e.y
        self.dsc.entryconfigure('Linia profilu', state=NORMAL)

    def drag(self, e):
        self.lineCoords["x2"] = e.x
        self.lineCoords["y2"] = e.y
        if self.line is not None:
            self.imagePanel.delete(self.line) 
        self.line = self.imagePanel.create_line(self.lineCoords["x"], self.lineCoords["y"], self.lineCoords['x2'], self.lineCoords['y2'], fill='red', dash=(2, 2))
        
        if self.profileWindow is not None:
            self.profileWindow.update_line(self.lineCoords)

    def resize_img(self, event):
        self.resize_child_windows()
        #self.newWidth = event.width
        #self.newHeight = event.height
        #self.imageFromArray = self.imageCopy.resize((self.newWidth, self.newHeight))
        self.photoImage = ImageTk.PhotoImage(self.imageFromArray)
        self.imagePanel.create_image(0, 0, anchor=NW, image=self.photoImage)
    # -------------------