Image Cutter¶
Processing images via programming is at times more convenient than using image manipulation programs. Sometimes just because you don't want to install a massive program and learn how to use it when all you wanted to do was to get some pieces cut from an image using some kind of pattern - and ideally for multiple images using the same parameters. While using just code for this sounds great and all, finding these parameters is usually much easier when you have a visual tool, so some form of a user interface is probably still needed...
To be more specific this project is about making a program where the user can place N rectangles on top of a loaded image, and save the cuts underneath those rectangles as individual image files. Placing the rectangles takes place in a visual editing tool using keyboard and/or mouse, and the cut parameters can be saved into a file so that they can be easily used to make a similar cut from another image.
kseen.
kseen.
Regarding Libraries¶
This project uses an image processing library called Pillow. You can install it from the command line with
lin dokumentaatiosta]].
pip install pillow
. Looking into its documentation is considered part of the project, although most of its functionality is not used here. Everything you need to know can be found from the Image module documentation.lin dokumentaatiosta]].
For the graphical user interface you can use the sweeperlib library. More about that later.
Specific Requirements¶
The two main functionalities of the program are the user interface for defining cut parametesr, and the mode that uses said parameters to take cuts from the loaded image.
When making the program it's important to keep in mind what its purpose is. For an example case, let's assume we've taken a screenshot of the YouTube front page every day. After a certain period of time, we want to get every thumbnail from these screenshots saved as their own images. Since the layout of the front page is always the same (at least for the first two rows, disregarding shorts), it's possible to define a set of rectangles that will be exactly on top of the thumbnails. These parameters can then be saved and used for clipping thumbnails from every image.
Edit View¶
This view is an interactive user interface where the loaded image is at the background. Cut indicators are drawn on top of the image. The example below uses partially transparent white rectangles, but you can choose your own method. This view is done with the sweeperlib which in turn is implemented on top of Pyglet. This means that the origin of this view is in the bottom left corner.
The cut is a grid made with equal distance between each rectangle. The user can set the following parameters:
- Amount of rectangles horizontally and vertically (separately).
- Bottom left corner coordinates of the bottom left rectangle. If these are changed, the other rectangles will move accordingly.
- Distance between rectangles horizontally and vertically. When changed, these should move all rectangles except the ones in the bottom row (for vertical change), or in the left column (for horizontal change).
- The height and width of a rectangle. All rectangles will change to this size.
In order to get full points, the editor must allow both pixel-precise control, and faster control for larger changes by holding a key. One way to implement this is to make it so that first press of a key changes the parameter by a pixel, and then if it is held down beyond a certain threshold, it will increment/decrement the parameter constantly until released.
Cutting Feature¶
This feature takes the parameters defined in the edit view and performs the cut operation itself by saving the regions as separate images using Pillow. When implementing this feature it's critical to keep in mind that unlike Pyglet, Pillow uses the top left corner of the image as its origin. It also defines rectangles in a different way. While Pyglet defines a rectangle as its bottom left coordinates, width, and length; Pillow defines one as the x coordinates of its left and right edges, and y coordinates of top and bottom edges. The parameters of each rectangle must therefore be converted into Pillow's coordinate system to get the desired regions clipped from the image.
In order to guarantee a nice workflow for users, it must be possible to use the cutting feature without opening edit view in between. I.e. it must be possible to just load another image and use the saved parameters to take the same cuts from that image. However, if the loaded image is not the same size as the one the parameters were made for, the program should give an error message.
Other Features¶
In addition to its two main features the program also needs to have the following smalle features.
- Loading an image. It's hard to cut pieces from an image without defining one. The loading feature can be accessible from the program's menu, or the loaded image can be defined as a command line parameter - your choice. If you choose to implement with command line parameters, the cut parameters must also be loadable using a command line parameter.
- Saving cut parameters. It needs to be possible to save the parameters defined in the edit view into a suitable data file, in order to enable using them for all similar images in the future. The saving feature allows the user to choose the name of the saved file.
- Loading cut parameters. Loading parameters follows naturally from saving them. The user must have a way to decice which parameters to load, if any.
Test Image¶
You can use the image below for testing. The squares in the image are 2 pixels away from the image borders, their size is 42 pixels, and their spacing is 8 pixels.
Graphics¶
Since the placement of rectangles needs to be done interactively, it's best to use an existing game library for it. This is largely because a simple game library already has good support for reading inputs in real time and drawing graphics in a window. Because learning to use a full game library goes a bit beyond the scope of this course we have provided a wrapper library called sweeperlib. It was initially made for the minestomper project topic but its use has since expanded into other projects - like this one. Sweeperlib abstracts away some more complex aspects of Pyglet and allows you to handle inputs and draw graphics using functions that are not too dissimilar to what've used throughout the course. Note that it's not mandatory to use sweeperlib, but your program has to have a graphical user interface nevertheless.
Use¶
Download the library from the link above if you haven't already. Unlike the minestomper project, this one does require any images, as the things that are drawn are the image chosen by the user, and dynamically created basic shapes layered on top of it.
Game Libraries 101¶
Whereas programs so far have had their own main loop (usually with
while True
), game libraries typically hide the main loop under the hood, and it is usually much more complex. It's been hidden so that you don't need to worry about it. When using a library, the game is usually implemented through handler functions that the main loop calls when certain triggers happen. For instance, mouse clicks can have a handler attached - this handler function will then be called whenever the user clicks a mouse button. Another very common handler is a drawing handler to draw the game's graphics, which is called whenever the game screen needs to be updated.You will not be creating a main loop in your own program. Instead, you will be implementing handler functions. These handlers must be attached to events. This can be done with functions in our library (named like: set_some_handler). The docstring for each of these describes what kind of a handler function is required. The general workflow of using the library is:
- load game graphics
- create game window
- define and register handler functions
- start the game
After this the game will run in the handler functions. There's a very simple example of drawing graphics at the end of the library module. The main program is a small piece of code that draws all sprites in some order on one row inside the window.
The most important fact that needs to be accounted for is that handlers are called by the main loop running out of your reach. This means you cannot cotrol what arguments your functions are given. As a result, the game's state (like the field) must be smuggled to the handlers in some other way. The preferred way to do this is to create a main program level dictionary for the state. A dictionary defined in the main program can be accessed and modified in any and all functions of the program due to its mutable nature.
Return Box¶
Deadline: 2025-08-31 23:59