I wrote a simple GUI applications to help me select 'positive' regions of a bunch of photos for the purpose of training an object detectir using OpenCV Haar Cascades. For training purposes, you need a series of 'positive' images which show the kind of item you want, closely cropped, and then you need a bunch of negative/background images that don't have the item at all. This script helps you generate both at once by paging through photos and quickly generating the rectangle you want to use to crop the image to select the 'positive' region. I'd like feedback on everything, of course, but particularly on whether the division of labor is sensible and also on formatting. I have the feeling I could make this code a lot more readable. imageeditor.pyimport Tkinterimport Image, ImageTkfrom Tkinter import Tk, BOTHfrom ttk import Frame, Button, Styleimport imagefeedimport imagefilefeedIMAGE_DIRECTORY = /home/whales/imgs/POSITIVE_DIRECTORY = /home/whales/tkinter/positive/NEGATIVE_DIRECTORY = /home/whales/tkinter/negative/IMAGE_RESIZE_FACTOR = .2class ImageEditor(Frame):    class ImageEditor provides functionality to page through photos so that users can  select a portion    of each photo to be saved separately as a 'positive' image and the rest to be saved separately as   four 'negative' images    def __init__(self, parent):        Initializes the window with access to an imagefeed class that supplies from and saves images to the appropriate locations        Frame.__init__(self, parent)                   self.parent = parent        self.corners = []        self.image_feed = imagefeed.ImageFeed(imagefilefeed.FileFeed(IMAGE_DIRECTORY, POSITIVE_DIRECTORY, NEGATIVE_DIRECTORY), IMAGE_RESIZE_FACTOR)        self.image = self.image_feed.returnTKImage()        self.canvas = None        self.initUI()        self.resetCanvas()    def initUI(self):        Adds a Tkinter canvas element that tracks mouse clicks to select image region for saving        self.style = Style()        self.style.theme_use(default)        self.pack(fill=BOTH, expand=1)        self.canvas = Tkinter.Canvas(self, width = self.image.width(), height = self.image.height())               self.canvas.bind(<Button-1>, self.OnMouseDown)        self.canvas.pack()        nextButton = Button(self, text=Next, command=self.next)        nextButton.place(x=0, y=0)        resetButton = Button(self, text=Reset, command=self.reset)        resetButton.place(x=0, y=22)    def next(self):        Saves current edits and advances to the next image        if len(self.corners) == 2:            self.image_feed.writeImages(self.corners)        self.image_feed.nextImage()        self.reset()        def resetCanvas(self):        Resets all canvas elements without advancing forward        self.image = self.image_feed.returnTKImage()        self.canvas.create_image(0, 0, image=self.image, anchor=nw)        self.canvas.configure(height = self.image.height(), width = self.image.width())        self.canvas.place(x = 0, y = 0, height = self.image.height(), width = self.image.width())    def reset(self):        Removes all drawings on the canvas so user can start over on same image        self.corners = []        self.canvas.delete(all)        self.resetCanvas()    def OnMouseDown(self, event):        Records location of user clicks to establish cropping region        self.corners.append([event.x, event.y])        if len(self.corners) == 2:            self.canvas.create_rectangle(self.corners[0][0], self.corners[0][1], self.corners[1][0], self.corners[1][1], outline ='cyan', width = 2)def main():    root = Tk()    root.geometry(250x150+300+300)    app = ImageEditor(root)    root.mainloop()  if __name__ == '__main__':    main()  imagefeed.pyimport cv2import Image, ImageTkclass ImageFeed:     The ImageFeed class manages all operations related to loading, saving, and formatting         images for presentation. The ImageFeed has a member file manager that determines what        files it loads. The ImageFeed supplies a TkinterImage to requester objects.    def __init__(self, file_feed, rescale_factor):        self.file_feed = file_feed        self.rescale_factor = rescale_factor        self.image = None        self.cv_img = None        self.nextImage()    def returnTKImage(self):        return self.image    def nextImage(self):         Calls the file feed's method to advance in the file list and then loads and formats            the next image file.        img = cv2.imread(self.file_feed.next_file())                    self.cv_img = img        img_small = cv2.resize(img, (0,0), fx = self.rescale_factor, fy = self.rescale_factor)        b, g, r = cv2.split(img_small)        img_small = cv2.merge((r,g,b))        im = Image.fromarray(img_small)        self.image = ImageTk.PhotoImage(image=im)           def writeImages(self, corners):         Writes the single 'positive' image to the positive directory and the four 'negative' images to the negative directory.            The 'negative' images are the four rectangles around the positive image that do not contain the positive image. The             parameter corners supplies two diagonal points of the rectangle enclosing the 'positive' region of the image.        new_img = self.cv_img[corners[0][1]/self.rescale_factor:corners[1][1]/self.rescale_factor, corners[0][0]/self.rescale_factor:corners[1][0]/self.rescale_factor]        cv2.imwrite(.join(self.file_feed.get_positive_file()), new_img)        low_x = min(corners[0][0], corners[1][0])/self.rescale_factor        high_x = max(corners[0][0], corners[1][0])/self.rescale_factor        low_y = min(corners[0][1], corners[1][1])/self.rescale_factor        high_y = max(corners[0][1], corners[1][1])/self.rescale_factor        neg_file_name = self.file_feed.get_negative_file();        new_img = self.cv_img[ :low_y, :]        cv2.imwrite({}{}{}.format(neg_file_name[0], LY, neg_file_name[1]), new_img)        new_img = self.cv_img[ high_y: , :]        cv2.imwrite({}{}{}.format(neg_file_name[0], HY, neg_file_name[1]), new_img)        new_img = self.cv_img[ :, :low_x ]        cv2.imwrite({}{}{}.format(neg_file_name[0], LX, neg_file_name[1]), new_img)        new_img = self.cv_img[:,  high_x: ]        cv2.imwrite({}{}{}.format(neg_file_name[0], HX, neg_file_name[1]), new_img)imagefilefeed.pyimport osclass FileFeed():    The FileFeed class determines appropriate file paths to use for retrieving images       and for saving 'positive' and 'negative' images while avoiding duplicating work       in the event that user completes classification in multiple sessions. To avoid duplication       the same positive_directory and negative_directory should always be used for one batch    def __init__(self, existing_directory, positive_directory, negative_directory):        Saves file locations to instance variables and determines the appropriate files            for editing, based on removing any that have already been edited        self.existing_directory = existing_directory        self.positive_directory = positive_directory        self.negative_directory = negative_directory        self.index = 0        # Retrieves complete list of files to be edited        list_of_files = []        file_names = []        walker = iter(os.walk(self.existing_directory))        next(walker)        for dir, _, _ in walker:            files = [dir + / +  file for file in os.listdir(dir)]                        list_of_files.extend(files)            file_names.extend(os.listdir(dir))        # Determines which files have already been edited        list_of_processed_files = []        processed_file_names = []        walker = iter(os.walk(self.positive_directory))        next(walker)        for dir, _, _ in walker:            files = [dir + / +  file for file in os.listdir(dir)]                        list_of_processed_files.extend(files)            processed_file_names.extend(os.listdir(dir))        # List of files to edit does not include those that have already been edited        good_names = set(file_names) - set(processed_file_names)        self.list_of_files = [f for i, f in enumerate(list_of_files) if file_names[i] in good_names]     def next_file(self):        self.index += 1        return self.list_of_files[self.index - 1]    def get_negative_file(self):        Returns a tuple containing the absolute directory (0) and filename (1)        where a negative file should be saved for the corresponding image/index. This         preserves folder and file identity from the original directory, but now in the        'negative' directory.        files = self.list_of_files[self.index].split(/)        try:            os.stat(self.negative_directory+files[-2])        except:            os.mkdir(self.negative_directory+files[-2])        return ({}{}/.format(self.negative_directory, files[-2]), files[-1])    def get_positive_file(self):        Returns same items as get_negative_file except for the positive directory        files = self.list_of_files[self.index].split(/)        try:            os.stat(self.positive_directory+files[-2])        except:            os.mkdir(self.positive_directory+files[-2])        return ({}{}/.format(self.positive_directory, files[-2]), files[-1])
Python GUI for cropping and saving images quickly
python;object oriented;tkinter;opencv
For improving readability and general coding style, PEP0008 is invaluable. It's the Python style guide and it details a lot of useful conventions to help with this. But there's two in particular I'll advise you about.DocstringsIt's good to include docstrings, but you have it formatted a bit off and rendundantly. To take your ImageEditor class for example, you don't need to begin with class ImageEditor, anyway reading it already knows which class it's from. If you're going to have multiple lines, you should just have a single line summarising the class, followed by a blank line and then the rest of the detail. The Python style guide recommends you stick to a maximum of 79 characters per line, so you should keep to that too. Here's how I'd rewrite your docstring:class ImageEditor(Frame):    Pages through photos so users can edit them.   Allows a user to select a portion of each photo to be saved separately   as a 'positive' image and the rest to be saved separately as   four 'negative' images.There's a lot more detail in the PEP about docstring conventions. And you can probably rewrite that better than me as the author of the code.Character limitThe character limit I mentioned above also comes in to play in your actual code. You might be averse to splitting long lines up because of how Python cares about whitespace, but you can freely split up lines in certain cases, like when you're using brackets ().     self.image_feed = imagefeed.ImageFeed(imagefilefeed.FileFeed(IMAGE_DIRECTORY, POSITIVE_DIRECTORY, NEGATIVE_DIRECTORY), IMAGE_RESIZE_FACTOR)You could split that line up inside the brackets to make it look like this:    self.image_feed = imagefeed.ImageFeed(                        imagefilefeed.FileFeed(                            IMAGE_DIRECTORY, POSITIVE_DIRECTORY, NEGATIVE_DIRECTORY),                        IMAGE_RESIZE_FACTOR)Your personal style may vary but I tried to indent it to show that the imagefilefeed is part of the same set of parameters as IMAGE_RESIZE_FACTOR. 