In this post, I want to tell a brief story of how I have created a tool named img2sh. It is also my first python package which is published over PyPI. In this very post, I will try to answer questions like:
How is it developed?
What are the challenges?
How should a package created and deployed over PyPI?
Img2sh is a tool to show images directly on the terminal. For colored images, 256 xterm color support is required. This script basically resizes the image with anti-aliasing and quantized its colors to xterm color pallette. Github repository of the project can be reached from here
Testing the package is super easy. Install using pip and run.
pip install img2sh --user
img2sh demo.jpeg -w 80 -i
For detailed usage arguments:
$ python img2sh/cli.py --help
As it can be understood from its name, img2sh is a python app to show images directly in the terminal. I think it could be helpful when connected over ssh to a server with no desktop environment. The images can be examined quickly on the terminal screen.
While the tool was developed, the followings are the challenges I have encountered.
In the digital world, images are made of pixels. Pixel is the smallest part of the image which can contain only one color. Color changes with the pixel intensity values. To keep it simple, I added a grayscale image to illustrate the pixel concept. It is a quite low resolution, grayscale image. Pixel values are shown in the following image. Pixel intensity values are changing with how far the pixel value close to black or white.
Colored images basically work with the same concept. The difference is colored images are usually indicated with 3 different pixel values which are red, blue and green. By this method, the proportion of these color intensity is changed to show different colors on screen.
How the coloring mechanism works in the terminal should be figured out to implement this app. I understood clearly how colors are handling in an image using the following link. After that, I have found colored python module which provides support for colored terminal output from python apps. I implement the colors using this library.
How should the command line arguments be parsed? Actually it is quite easy with python. Let’s examine the next code block:
After the initialization, arguments can be used with commands like
args.width. The arguments can be configured as mandatory or optional and the type of argument also be specified. This package is a pretty useful and standard package that is widely used most of the python projects.
For further improvement in the project, it should be solved how different image formats can handle. Fortunately, Pillow package can handle various kinds of image formats such as jpeg, png, tiff. This packet can provide pixel values for different image types using the same get_pixel method interface. However, the problem is the dimension of the color values are representing. At standard jpeg a pixel value is represented with 24 bits which are 3 bytes. Each byte value represents a different color channels Red, Blue, and Green. Png differs from the alpha channel. At PNG images, colors are created with 4 bytes. Red, Blue, Green, and Alpha. Alpha is the transparency channel of the image. So the dimension of the pixel is different. But this problem is easily solved in findNearestColor function. In these functions, the dimension of the pixel is handled.
In this section, I will try to answer the question that how to create a setup.py file which supports also entry point and can be executed without python shell. Setup.py file is used to define the python package metadata and its installation instructions.
from setuptools import setup, find_packages
The entry points of the module can be specified with specifying entry_points property of the object. It is defined as console script and first, the name of the executable should be written. It should be mapped to the executable python function. In my case, it is img2sh:cli:main.
There are useful tricks in the code block also. For example, you do not have to write a whole long description of this file. It can be read from different files like
README.md. It is the same for your requirements. It still can be read from requirements.txt but with one condition. These files should be specified in the MANIFEST.in file.
The following link great resource to create a setup.py file. I learned from here. You should check it out.
As you may already know, python packages are usually distributed using PyPI servers. When we use the pip package manager in default, packages are downloaded from this server. In this project, I want that my package can be installed over PyPI. So I created a PyPI account and upload my package there.
to Uploading the PyPI package actually quite simple. The following commands are required to create a package from a module and to upload it to PyPI servers.
python setup.py sdist bdist_wheel
twine upload --repository-url https://upload.pypi.org/legacy/ dist/*
How can be the performance of the tool optimized?
In the first prototype, the code is written with simple logic. This causes too much processing time because of the for loops inside it. The following code part is written to find the nearest color inside a palette. The function loops in pallette and tries to find the smallest distance. In version 2, I implement it using numpy package. The operation executes in vectorial form and its execution time decreased exponentially.
def findNearestColor(color, pallette):
def findNearestColor(color, pallette):
# for v1 real 0m2,618s user 0m2,519s sys 0m0,080s # for v2 real 0m1,690s user 0m1,892s sys 0m0,417s
Thank you for your interest. See you later…
This package is developed using: