How to Use Watermarkd: A Batch Watermarking Library with GUI

What is watermarking?

Watermarking is a technology of printing less opaque text or symbols on images and items. Believe it or not it’s considered an ancient technology as old as paper since it’s been used on special letters, stamps and printed currency.

As digital imaging has never been such a humongous part of humans’ lives, the usage, creation and demand related to digital images rises as well. Despite the rise of open source and sharing mentality in the digital community, there seems to still be significant room for watermarking applications and so the demand for innovation continues.

Star shaped watermarks among with other patterns on 100 Euro banknote

Why Watermarkd?

Watermarkd is a Python library published by under the open source license Apache-2.0.

In this post we will try to demonstrate what can be accomplished with Watermarkd library.

Watermarkd library allows users to handle watermarking operations in Python directly from the terminal or through use of an optional GUI component.

Since it’s specialized only for watermarking tasks, Watermarkd has a very light graphical user interface. You can probably get an image watermarked (or watermarkd!) during the time it takes to start up a major Image Manipulation software.

It has the most commonly used features when it comes to watermarking and expected to cater to 90% or more of the people looking to watermark an image.

How does Watermarkd work?

Currently, Watermarkd library consists of one sole class named Spread which houses two functions that spread out watermark text on image(s):

  • single function: Spread.single() can be used to apply watermark on a single image (photo).
  • batch function: Spread.batch() can be used to apply watermark to multiple images in a folder.
Here are some images created with Watermarkd on the go.
Medical Field Watermarking
Business confidentiality communicated through watermark

Below you can see various usage types of Watermarkd Library under four titles:

– Single Function Usage

– Batch Function Usage

– Single Function Usage with GUI

– Batch Function Usage with GUI

Single Function Usage

Single function can be used in two main ways:

  • through command line without any interface using its parameters
  • through GUI by enabling gui parameter via True boolean.

as simple as passing True argument to gui as below:


Let’s first investigate usage scenarios without gui:

import Watermarkd as wmd


This code, once executed, will create a watermarkd image on the user’s Desktop. You don’t have to assign gui parameter to False since it’s the default option. So, basically it’s the same thing as this code:

wmd.Spread.single(gui=False, img_path=r"c:/myimage.png")

Also, please note that, when not using gui, the only parameter you have to pass an argument to is img_path, since all the other parameters have default values and hence, they are optional.

Let’s continue to explore other parameters that can be passed to the single function:


  • gui, (bool), default False : enables GUI
  • img_path, (string), mandatory parameter :signifies image path
  • wm_text,  (string), default= “Watermarkd”) : Watermark Text
  • wm_trans, (int [1-255]), default= 85 : Signifies Watermark Transparency 
  • font_size, (int), default= 55) : Watermark Font Size
  • font_name, (string), default= “arial.ttf” : Font Type
  • filename, (string), default=”Watermarkd” : File Name for Saving
  • save_to_path, (string), default=”Desktop/watermarkd_” : Saving Folder Path
  • save_to_suffix, (string), default=”.png” : File Type for Saving 
  • output_filename, (default= r”c:/Users/”+user_path+”/Desktop/watermarkd.png”) : File Name For Saving

Here is another example, in which watermark text is assigned to “Inception”:

from Watermarkd import Spread

f = r"c:/Users/ABC/Desktop/film.png"
Spread.single(img_path=f, wm_text="Inception")
Film Making Industry Watermarking

Batch Function Usage

Batch function is very similar to single function with a few subtle differences. First and foremost, it handles a folder of images rather than a single image. 

So, img_path parameter is exchanged with folder_path parameter.

Otherwise the rest of the differences are mainly internal which you can check out in the source code if you like.

from Watermarkd import Spread


This code will read all the images from the given folder New_Photos, and create an output folder named Watermarkd_ in user’s Desktop and save all the watermarked files there with the default values for optional parameters. Check out a list of parameters below for adjusting different values such as: watermark text, font size, font type, file name, path name, transparency etc.

If you check out the source code, most of the variable names are also conveniently borrowed from the single function.

Regarding the inner workings of the batch function, the main thing is: it doesn’t apply the watermarking algorithm to a single image, instead watermarking algorithm and positioning etc are placed inside a for loop which iterates through the images in the given folder.

So, in simpler words, folder is iterated with a for loop, image file name is taken which becomes the img_path similar to single function, then watermarking is applied and then files is saved and next iteration starts with the next file.

Here is another example:

from Watermarkd import Spread

Spread.batch(folder_path=f, wm_text="Photographer A. C. Jonah, #927-654-92**")
  • gui, (bool), default False : enables GUI
  • folder_path, (string), mandatory parameter  :signifies folder path
  • wm_text,  (string), default= “Watermarkd”) : Watermark Text
  • wm_trans, (int [1-255]), default= 85 : Signifies Watermark Transparency 
  • font_size, (int), default= 55) : Watermark Font Size
  • font_name, (string), default= “arial.ttf” : Font Type
  • filename, (string), default=”Watermarkd” : File Name for Saving
  • save_to_path, (string), default=”Desktop/watermarkd_” : Saving Folder Path
  • save_to_suffix, (string), default=”.png” : File Type for Saving 
  • output_filename, (default= r”c:/Users/”+user_path+”/Desktop/watermarkd.png”) : File Name For Saving
More Photo Examples:
Model Promoting Watermarked with Watermarkd
Young Model Posing For Photography Agency with Watermarkd
Univeristy of Utah Campus Watermarked with Watermarkd
New Release Poster for Software

Watermarkd Usage: Single Function with GUI

Usage with GUI is pretty straightforward.

  1. Pick an image file
  2. Type your watermark text
  3. Choose a watermark text size. Options are:
    • Normal (by default)
    • Small
    • Large
    • Value: Let’s a custom font value to be entered. Possibly something like ~150 for high rez images where something like 30 might suffice for a low resolution image.
  4. Transparency: This value defines the transparency (or opacity) of your watermark text. 85 is the default value but you can go all the way down to 0 for a completely transparent text (Watermark would be invisible then). 255, the maximum value will create a solid white watermark text (which is more just a white text than watermark since it’s not transparent at all. It can sometimes be useful nevertheless.)
  5. Save as: Different options to save watermarked image as different image types such as:
    1. png (default)
    2. jpg
    3. gif
    4. bmp
  6. Save to: Path or folder that you’d like to save your watermarked image to. You can either type it or choose it with the help of the Path button.
  7. Filename: Filename you’d like to save your watermarked image under.

Once submit is pressed all the inputs from the user get registered and watermarking process starts.

It can last anywhere between miliseconds to a couple of seconds depending on the resolution of the image. (For the very high resolutions you might need to allow 2-3 seconds which also depends on the availability of computation resources.)

Just as GUI is a new dimension for the Python coder, Packaging also is a new dimension that opens up a whole new world of opportunities and skillset.

To activate GUI component all you have to do is:


You don’t have to pass any other arguments to setting parameters since they’ll be overridden after the GUI is executed.

from Watermarkd import Spread

Single Function's GUI Component in Watermarkd

Watermarkd Usage: Batch Function with GUI

Usage with GUI in batch function is also simple and straightforward. It’s mostly overlapping with single function’s steps. Here are the main differences:

  • Pick a folder instead of a single image through the Browse button.
  • At the bottom file name will be used as a seed to generate multiple images with watermark. For instance, if you choose Work as file name, files will be saved as Work1.jpg, Work2.jpg, Work3.jpg etc.

Once submit is pressed all the inputs from the user get registered and watermarking process will start.

It can last anywhere between miliseconds to a couple of seconds per image depending on the resolution of the image. (For the very high resolutions you might need to allow 2-3 seconds per image which also depends on the availability of computation resources.)  After that time watermarked images will be created in the specified folder or default folder if none is specified.

Activate graphical user interface for bath watermarking a folder of images similar to the code in single() function’s case:

from Watermarkd import Spread

Watermarkd batch function's GUI window

Further Steps with Watermarkd

As much as I’d love to see Watermarkd getting used by people with different backgrounds such as:

  • Photographers 
  • Bloggers
  • Entrepreneurs and Business Owners
  • Students
  • Teachers
  • Media Agencies
  • Artists, Illustrators

I’d also love to see it being checked out as a learning tool for Python packaging and Python coding in general. It has all the ingredients necessary to comprehend packaging in Python.

Besides packaging topics such as: Pypi repository publishing, Github hosting, licensing etc. It also has fundamental coding topics, but nothing too complicated to discourage an intermediate or even beginner coder, such as:

Additionally, it’s pretty simple to understand and demonstrates all the ingredients needed to publish a library on PyPI (Python Package Index), such as:

    • requirements.txt
    • License
Every great coder was a beginner at some point. So, don’t be too shy. One step at a time you too can become great, and create great programs,  there is no doubt about it. Some helpful topics to discover can be:

Such topics open up whole new worlds for a programmer to progress towards, you can see them as paths you can take in your journey(Oftentimes you can combine these paths for a great product as well).

Final Thoughts

If you’re a beginner or intermediate programmer go ahead and create a Github account. Maybe create a trivial repository where you take some notes and save some files if you like, just to start getting familiar with the environment.

If you like Watermarkd or Holypython’s work in general, you’re welcome to visit our Github repos as well. You can read the source code along with other necessary files there (, requirements.txt, etc.). 

As simple as it is, I hope Watermarkd library serves as a practical solution for people who might need watermarking and I’d love to see it serve educational purposes for coders and developers who never had a chance to explore packaging and publishing topics related to Python.

Thank you so much for visiting.

ps: I’d like to thank creators and contributors of Pillow the Friendly PIL Fork and PySimpleGUI libraries for creating such fantastic libraries and influencing further developments. Also, a huge thank you to Stack Overflow community for sharing so much expert level knowledge and being so kind.

How to Watermark Images w/Python (PIL)

What's Watermarking?

Watermark is a semi-transparent text or logo that sort of blends with an image in the background to make the image more difficult to copy or share without appropriate references.

Watermark is an old idea (usually applied to money, stamp or institutional papers) that’s been adapted nicely to the digital image practices.

Holy Python is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Why does someone need Watermarking?

The need for watermarks arises in situations like:

  • When you’d like to make your mark on your digital product.
  • When you want to discourage copying of your material
  • To make copyright claims known without blocking image’s visibility
  • To communicate a message such as on digital images, pitch deck or business presentations: Confidentiality, Warning, Incomplete, TBD (to be decided) etc.
  • For marketing reasons
  • To gain more recognition etc.

So, if you own or work with a business, website, blog, professional career, startup, book, e-book, reports or if you’re involved with digital photography at a professional level chances are very high that you will at some point need watermarking tools. 

Why Watermark with Python or another programming language?

Python works phenomenally with Watermarking as it does in so many other occasions. We live in a competitive world and competing well also means optimizing your time, optimizing your routine tasks, decreasing labor and increasing intellectual work that creates value.

Watermarking can be time-consuming, tedious and distracting. Definitely not a very intellectual occupation. With coding you can make it a 1-click automated process saving you tons of time and focused work in future. This is the kind of optimization that makes individuals and businesses thrive.

You can fine tune many parameters: text color, transparency, randomness, location, transparency via alpha channel, image type, watermark message etc.

  • You can formulate your watermarking process
    • For instance, there is a big difference between saying place watermark in (300,200) coordinates and saying place watermark in (image_height/2, image_width/2) You get the point right? This leads to automation and opens doors to a new world such as batch editing, scripting etc.
  • You can create your own software
  • You can automate Watermarking process and make it a 1-click solution in future.
  • It’s much less resource-hungry compared to GUI alternatives like Photoshop or GIMP
  • If written well it won’t cause freezing or glitches as heavy GUI alternatives sometimes do
  • It’s a great way to practice coding , conceptualizing, planning, automation and intellectual thinking

Watermarking with Python Tutorial (Applying Text)

We’re going to use PIL imaging library of Python in this tutorial.

Let’s first create a text on the image without transparency. We got an appetizing image that can be worked on.

We’ll apply a text on the image in about 7 steps:

  1. Import the libraries (Image, ImageDraw and ImageFont from PIL library)
  2. Open the image to work on
  3. Create draw object (this is a handle that takes the opened image as argument)
  4. Create text and font object (we’re selecting arial as font and 82 as size) Font size here can be formulated proportional to image size. Especially in a batch processing scenario font can appear too small or too big as image resolution fluctuates if it’s given as a constant integer.
  5. We’re simply calculating x and y coordinates for text. Horizontally it’s in the middle and text size is also encountered. Vertically it’s 300 pixels up from the end of the image.
  6.  Simply saving the new image. You can use the same path and name which will overwrite the original image. Or, if you want to keep the originals as is, you can give it a new name as in the example. At this step, you can also convert the image type if it’s more convenient for you. For instance, we’re starting with a .jpg image and converting it to .png at the saving step.
from PIL import Image, ImageDraw, ImageFont

#Opening Image
img ='Desktop/cake.jpg') 

#Creating draw object
draw = ImageDraw.Draw(img) 

#Creating text and font object
text = ""
font = ImageFont.truetype('arial.ttf', 82)

#Positioning Text
textwidth, textheight = draw.textsize(text, font)
width, height = img.size 

#Applying text on image via draw object
draw.text((x, y), text, font=font) 

#Saving the new image'Desktop/cake_watermarked.png')

Great, we’ve made our mark on the image. Now, let’s get more sophisticated.

Although nothing too complex, adding transparency to image slightly complicates the process. But, don’t worry, once you understand it or implement it, it’s actually pretty reasonable.

We will achieve transparency through alpha channel in an RGBA (stands for red-green-blue-alpha) type image. Each of these R-G-B-A channels take an integer value between 0 and 255. So if Alpha equals 25 it will approximately equal to 10% transparency.

Check out this example (0, 0, 0, 25). This image has all RGB channels set to 0, which means black color and then Alpha Channel is equal to 25 which means 10% transparent.

On the other hand you’ll most likely prefer a light color like white for your watermark, so we will use an RGBA combination similar to: (255, 255, 255, 25)

You can check out this digital image basics tutorial in Python if you’d like to polish your knowledge about digital images from coding perspective.

Creating Transparent Text (Watermark)

Now, let’s create a transparent watermark as it’s usually meant to be.

It may appear complicated, it’s not.

The only difference here are:

We will convert the image we’re opening to RGBA so it includes an alpha channel which we will work with.

We will create a new transparent image. Semi-transparent text will be applied to this image. You can think of it as a text layer to speak with Photoshop or Gimp lingo. This will be achieved using .new() method of Image module.

While saving, we will combine both images (original image and transparent text layer). This will be achieved using .alpha_composite(img1, img2) method of Image module.

#Importing Libraries
import random
from PIL import Image, ImageDraw, ImageFont

#Opening Image & Creating New Text Layer
img ='Desktop/cake.jpg').convert("RGBA")
txt ='RGBA', img.size, (255,255,255,0))

#Creating Text
text = ""
font = ImageFont.truetype("arial.ttf", 82)

#Creating Draw Object
d = ImageDraw.Draw(txt)

#Positioning Text
width, height = img.size 
textwidth, textheight = d.textsize(text, font)

#Applying Text
d.text((x,y), text, fill=(255,255,255, 125), font=font)

#Combining Original Image with Text and Saving
watermarked = Image.alpha_composite(img, txt)'cake_watermarked50.png')
PIL watermark demonstration with 50% (alpha channel =125)
Here is a step-by-step breakdown:
  1. Importing Libraries: This step is self-explanatory we’re importing Image, ImageDraw and ImageFont from PIL library.
  2. Opening Image & Creating New Text Layer:
    • Here we’re opening the original image but also immediately converting it to RGBA using .convert method.
    • Also, we’re creating a new text layer, which is also RGBA, with full transparency (alpha channel is 0).
  3. Creating Text: Text is defined as before and font object is also created.
  4. Creating Draw Object: Another standard procedure, draw object is created.
  5. Positioning Text: Image position is calculated similar to previous operation, horizontally centered and 300 pixels from below.
  6. Applying Text: At this step, we are using fill argument with RGBA parameters in a tuple. (255,255,255,125) will fill the text with approximately 50% transparency and white color, font argument is assigned to font object we created.
  7. Combining Original Image with Text and Saving: Finally both images are combined using .alpha_composite() method. Then the new image object is saved with file name cake_watermarked50

Creating Multiple Watermarks (using Python For Loop)

What about multiple small watermarks? Those can be handy too. You might prefer scattered watermarks on your images compared to one big watermark in the middle.

In this case powers of coding start to shine. You can simply place relevant watermarking steps inside a loop and loop it as many times as you’d like.

Check out this example: I lowered font size to 27, lowered transparency to approximately 30% and created a loop using range function. x and y positions are randomly chosen with the help of random library. Inside the same loop same text is applied to draw object which creates in multiple watermarks in multiple random positions throughout the image.

Everything else is similar to the previous example. Here is the Python code:

import random
from PIL import Image, ImageDraw, ImageFont

#Opening Image & Creating New Text Layer
img ='Desktop/cake.jpg').convert("RGBA")
txt ='RGBA', img.size, (255,255,255,0))

#Creating Text
text = ""
font = ImageFont.truetype("arial.ttf", 27)

#Creating Draw Object
draw = ImageDraw.Draw(txt)

#Positioning of Text
width, height = img.size 
# textwidth, textheight = d.textsize(text, font)
# x=width/2-textwidth/2
# y=height-textheight-300

# Loop for Multiple Watermarks
for i in range(7):
    x=random.randint(0, width-300)
    y+=random.randrange(0,int(height/8), 19)+random.randint(0,100)
    draw.text((x,y), text, fill=(255,255,255, 75), font=font)

#Combining both layers and saving new image
watermarked = Image.alpha_composite(img, txt)'Desktop/cake_M_30.png')

Regarding positions in the loop, I’ve just assigned a random integer to x coordinate and excluded 300 pixels from the end so that text doesn’t start too late horizontally and get cut off.

Regarding y coordinate, I tried a slightly more sophisticated approach in an attempt to avoid overlapping watermarks. y starts at 200th pixel and incrementally increases In a range between 0 and “height of the image divided by amount of watermarks”. Then a random integer between 0 and 100 is added to create more randomness.

You can play with it to see a more optimal solution. I’m sure someone can come up with a smarter distribution model regarding x and y coordinates in multiple watermark situations.

You can check out Watermarkd Python library here and its source code here. Watermarkd has an improved watermark positioning algorithm with batch function (for watermarking multiple files at once) and GUI components.

Conclusion & New Ideas (Batch Watermarking, GUI Watermark Application)

This is the end. Hopefully this tutorial can be useful for many people. Besides being a totally practical topic, I find it also helpful to practice Python and gain new coding skills.

Additionally you can combine this knowledge with some other Python tutorials we have such as:

After this tutorial, you definitely deserved some cake! or another treat that your prefer!

Thanks for checking out this Python tutorial.

How to batch resize multiple images in Python (via PIL library)

This tutorial will show you how to resize any number of images in 6 lines of Python code (excluding library import lines).

Holy Python is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Used Where?

  • To resize multiple images without too much labor in a variety of applications:
    • For websites
    • Books
    • Archiving
    • Databases
    • Data science
    • Machine learning
    • Photography etc.

We’re going to need Python’s PIL library and os library.

Let’s import them first:

import PIL
import os
from PIL import Image

Estimated Time

5 mins

Skill Level





PIL, os


You can resize multiple images in Python with the awesome PIL library and a small help of the os (operating system) library.

Secondly, let’s assign the path of the images to a variable:

f = r'C://Users/xx/Desktop/testimages'
By using os.listdir() function you can read all the file names in a directory.

After that, all you have to do is to create a for loop to open, resize and save each image in the directory.

f_img variable is assigned the path plus file name with a slash sign in the middle in each iteration.

In last 3 lines, image is opened, resized and saved consequently.

f = r'C://Users/xx/Desktop/testimages'
for file in os.listdir(f):
    f_img = f+"/"+file
    img =
    img = img.resize((2296,1724))

This is a very crude code without any bells and whistles so, it can be improved.

For example, adding a try / except statement to the loop can be a good idea. It would save the program from breaking down when resize operation can not be carried out (maybe due to having other file formats in the directory or a broken image file.)

Dusky day in Manhattan

Above image was taken with Panasonic Lumix G85 and is ~5mb in original size and 4592×3448 resolution.

For instance, resizing 1000 similar images in imagetest folder to 50% of the current resolution took approximately 30 secs on my computer. On the other hand, resizing 1000 images of already reduced size (approx. 500kb) takes nearly 10 secs to resize.

Here is the full code:

import PIL
import os
import os.path
from PIL import Image

f = r'c://Users/xx/Desktop/imagetest'
for file in os.listdir(f):
    f_img = f+"/"+file
    img =
    img = img.resize((2296,1724))


In short, computation offers  great power indeed.

The code took about a couple of minutes to write and it took Python kernel approximately half a minute to fully process. 

(It may take slightly longer to write a similar code in the very beginning but you get faster and faster eventually, just like spoken languages.)

To sum up, doing the same process manually would probably take days plus the physical and mental fatigue. 

You can rely on other scripts and software for resizing multiple images but doing this in Python was a breeze without any glitches. Moreover it’s good coding practice and writing scripts in Python is almost a zen-like experience sometimes. You should definitely give it a try. In addition to that, images are used in much more complex tasks such as machine learning in Python.

Thank you for checking this tutorial out.


To time your codes you can also use Python’s timeit library.

If you’d like to see an extensive tutorial about Python’s PIL library, you can check out this post here.

Digital Image Basics with PIL Library in Python

A digital image is basically a representation of colors through numbers. In this tutorial we’ll see the logic behind those numbers and most common image types in Python.

If digital images were biological beings we could probably say they have absolutely thrived in evolutionary terms in the first decades of the new millennia. Digital image production surged in humongous amounts since the first digital cameras hit the mainstream consumer markets sometime in the late 90s.

As Computer Vision is one of the cutting edge technologies in a range of domains from Robotics to Autonomous Driving, understanding digital image can be a very satisfying and fruitful process.

Used Where?

  • Image manipulation
  • Image research
  • Computer vision
  • Data science
  • Web
  • Apps

First thing first, let’s import PIL library and its Image module:

import PIL
from PIL import Image

Estimated Time

5 mins

Skill Level



RGB, B&W, Monochrome


PIL, cv2, numpy

Tutorial Provided by


After that we can open a colorful image and see how it looks in numbers. To open the image with PIL:

f1 = r'C://Users/xx/Desktop/151013.jpg'
data1 =

To get the pixel values of an image we can use:

.getdata() function of the Image module.

raw1 = data1.getdata()

If we print raw1 we’ll get a list of tuples consisted of 3 elements each. The reason why each tuple has 3 values is because the image we opened is an “RGB” image which stands for Red, Green and Blue channels.

Turns out any color in the visible spectrum can be produced by a combination of blue, green and red wavelenghts also known as additive color mixture.

Let’s take a partial look:



[(186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (186, 190, 189), (184, 188, 187), (185, 189, 188), (186, 190, 189), (187, 191, 190), (187, 191, 190) ……… (187, 191, 190), (187, 191, 190), (186, 190, 189), (186, 190, 189), (188, 192, 191), (188, 192, 191), (187, 191, 190), (184, 188, 187), (182, 186, 185), (182, 186, 185), (182, 186, 185), (184, 188, 187), (185, 189, 188)]

There are going to be as many tuples as there are pixels in the image. So if the image is 500×500 pixels that means there will be 250000 pixels in the image hence 250000 tuples in the list above representing each pixel in 3 values: red, green and blue.

Also each value usually takes a number between 0 and 255 which adds up to 256 total values. This is because the common 24 bit depth suggests an 8bit*8bit*8bit structure. Each color represented with 8 bit means 2⁸ which is 2x2x2x2x2x2x2x2 = 256.

Bunch of croc babies tanning in the sun (RGB Image)

Black and White

Now let’s convert the same image to Black & White and investigate its pixel values.

For this conversion we need to use .convert("L")

Here is an extensive Python tutorial if you’d like to cover a more detailed Python tutorial about converting images to Black & White or Monochrome.

data2 = data1.convert("L")
raw2 = data2.getdata()


[189, 189, 188, 188, 190, 190, 189, 186, 184, 184, 184, 186, 187, 188, 187, 185, 184, 185, 186, 185, 187, 190, 190, 188, 186, 185, 186, 186, 186, 186, 185, 185, 184, 184, 184, 183, 183, 183, 183,    ……….   183, 183, 183, 179, 180, 181, 174, 175, 176, 176, 174, 173, 174, 175, 172, 173, 174, 175, 175, 175, 175, 174, 175, 175, 175, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 171, 171, 171, 170, 170, 170, 169, 169, 168, 168, 168, 167, 167, 167, 166, 166, 165, 165, 165, 165]

Now you can see instead of tuples of 3 values, we have only 1 value for each pixel again from 0 to 255. If the pixel value is 0 that means pitch black, 255 means white and any value in between means different tones of white, black and gray.

Same image in Black & White


The difference between B&W and Monochrome is that monochrome pixels will take only either 0 or 255 as value while B&W pixels can take any value from 0 to 255 inclusive. 

This means each monochrome pixel is either full black or full white and there are no gray tones in between for any pixel.

For this conversion we need to use .convert("1")

data3 = data1.convert("1")
raw3 = data3.getdata()


[0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 255, 255, 0, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 255, 0, 255, 255, 0, 255, 0, 255, 255, 0,     ……     , 255, 0, 255, 255, 255, 0, 255, 0, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 255]

Monochrome Image

RGBA (RGB with Alpha Transparency Channel)

Another useful image type is RGBA. RGBA images have one additional channel compared to RGB’s red-green-blue, which is alpha.

Alpha channel is particularly useful when you need transparency adjustments. For instance, an RGBA color shade with (0, 0, 0, 255) is full pitch black since RGB are all zero and transparency is maximum value of alpha.

Similarly, (255, 255, 255, 25) means white color with ~10% transparency since alpha channel is approximately 10 percent of its maximum value and RGB are all at maximum creating white color.

You can also apply this knowledge to pixel-level image manipulation.

Transparency is particularly useful when:

  • Drawing on images with transparency
  • Applying color shades, color masks or tint on images
  • Applying transparent text on images (Watermarking)
  • Combining multiple images with transparent blending

You can easily convert an image to RGBA upon opening with following code:

from PIL import Image
img ='testimage.jpg').convert("RGBA")

Pixel Position

So after using .getdata() function, if we get 10s of thousands of numbers, how do we know which number represent which pixel? It’s easier than it sounds.

If you were lucky and unlucky enough to witness the dial-up modem miracle, you might remember that often times images were interrupted during loading on websites due to low internet speed (maybe 1kb per second or even bytes per sec sometimes).

The reason the bottom part of the image is missing when this happens is because the pixels start loading from left to right horizontally and once the row is filled they start loading the next row until all the rows are finished to the bottom.

Pixel positions

Now that you know the inner workings of digital images, a lot of machine learning is actually done with analyzing or manipulating those numbers pixels take as values.

Python code to show the difference between 2 images

This is a tutorial about how to write a simple code in Python to show the differences between 2 images.

Although It’s a crude code this can be very useful for practicing fundamental programming skills. You can also find a more elegant approach here. (soon to be published.)

In something like 25 lines of code you can see the demonstration of:

for loops, PIL library usage, manual counter (c), conditional expression (if – else), continue / break statement and data array subtraction (numpy).


Used Where?

  • Quick styling of matplotlib graphs and charts.

First thing first, let’s get out the Python libraries and make them ready to use:

import numpy as np
import cv2
import PIL
from PIL import Image
from PIL import ImageDraw

Estimated Time

15 mins

Skill Level

Upper Intermediate




PIL, cv2, numpy

Course Provided by


If we look at the Package Contents, you can see that there are tons of features. We will look closer at Image and discover its capabilities.

At this point you are ready to open an image file. All you need is to assign an image path and then open it with Image using: where f is the path.

f1 = r'C://Users/xx/Desktop/macfd.jpg'
f2 = r'C://Users/xx/Desktop/macfd2.jpg'
data1 =
data2 =

Hudson River (on the right) is the river between Manhattan and New Jersey and the river between Manhattan and Brooklyn is called East River (on the left).

Manhattan Island from above. Hudson River on the right and East River on the left

Here is the same image with a tiny difference. 

Here is the same image with A320 in it. OK it's not a Picasso painting but can you see it?

On January 15, 2009, some New Yorkers couldn’t believe their eyes as an Airbus A320 descended and smoothly touched down on Hudson River in a matter of a couple of minutes. Plane “rivered” somewhere across 48th Street saving lives of all 155 people on board.

Captain Sullenberger’s heroic achievement was going to be remembered as Miracle on the Hudson describing the unlikely odds of his massive aircraft successfully ditched on Hudson River after suffering a strike with a flock of Canada geese and lost both engines shortly after taking off from New York’s own LaGuardia Airport. 

Let’s see if Python can tell the difference between a normal Hudson River and Hudson River with an Airbus A320 on it.

Let’s start with converting the image to Black & White (“L”) in order to eliminate color differences and achieve computation simplicity with one channel color mode.

data3 = data1.convert("L")
data4 = data2.convert("L")

Now, let’s get the pixels of each image.

raw1 = data3.getdata()
raw2 = data4.getdata()

We imported numpy to subtract 2 pixel arrays from each other. Now it’s time to shine for numpy:

diff_pix = np.subtract(raw1,raw2)

Now let’s create an empty image same size as others and create an image from the differences between 2 previous images:

img_final ="L",(602,756))


Code below might appear complex, it’s not. First half is figuring out where the big pixel difference happens. Second half is drawing an appropriate rectangle.

Last 3 lines is to show the image and save it as well. (I had to save the image to show it on this page, you may wanna skip that step.)

  • To see a tutorial about drawing on images and calculating box position you can see this post here.
  • To see a tutorial about digital image pixel positions you can see this post here.

So the code is simple.
It looks for the first pixel where pixel difference between images is bigger than 25. As soon as it’s bigger than 25 the counter stops showing the different pixel’s position.

Then a division by 602 (image’s horizontal resolution) gives us the coordinates to draw the box.
Box is drawn with an approximate negative margin so that the box covers the object as a whole.

for i in diff_pix:
    if i > 25:

x10 = c%602
y10 = c//602

x1,y1,x2,y2 = x10-30, y10-20, x10+30, y10+20
Drawer = ImageDraw.Draw(data2)
Drawer.rectangle((x1, y1, x2, y2), outline="red", width=3)

f3 = r'C://Users/xx/Desktop/macfd3.bmp'

Voila! We have an image with a drawing that points out the difference. It’s relatively manual and crude which can be good if you’re looking to practice a lot of fundamental coding concepts.

Python basically showing off its mind blowing versatility.


#Opening images and converting to B&W
f1 = r'C://Users/xx/Desktop/macfd.jpg'
f2 = r'C://Users/xx/Desktop/macfd2.jpg'
data1 =
data2 =
data3 = data1.convert("L")
data4 = data2.convert("L")
raw1 = data3.getdata()
raw2 = data4.getdata()

#Subtracting pixels
diff_pix = np.subtract(raw1,raw2)

#Creating a new image with only the different pixels
img_final ="L",(602,756))

#Calculating box coordinates
for i in diff_pix:
    if i > 25:

x10 = c%602
y10 = c//602

#Drawing the box
x1,y1,x2,y2 = x10-30, y10-20, x10+30, y10+20
Drawer = ImageDraw.Draw(data2)
Drawer.rectangle((x1, y1, x2, y2), outline="red", width=3)

#Saving the image with box
f3 = r'C://Users/xx/Desktop/macfd3.bmp'

Image Manipulation with Python (PIL)

In this tutorial we will take a closer look at PIL module and discover some of its powerful features. You will be able to understand some image manipulation methods with Python including basic editing options such as crop, save, resize etc. and some amazing filter options.

Holy Python is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Used Where?

  • Image manipulation
  • Editing images
  • Drawing objects on images
  • Placing text on images
  • Filtering images
  • Opening / copying / saving images
  • Converting images
  • Pixelizing images…

Let’s start with importing the PIL library:

import PIL
    PIL - Pillow (Fork of the Python Imaging Library)

    Pillow is the friendly PIL fork by Alex Clark and Contributors.

Estimated Time

25 mins

Skill Level

Upper Intermediate


Image, ImageFilter, ImageEnhance, ImageDraw





Image Manipulation with Python using PIL module

If we look at the Package Contents, you can see that there are tons of features. We will look closer at Image and discover its capabilities.
BmpImagePlugin BufrStubImagePlugin
ContainerIO CurImagePlugin
EpsImagePlugin ExifTags FitsStubImagePlugin
FliImagePlugin FontFile FpxImagePlugin
GbrImagePlugin GdImageFile GifImagePlugin
GimpPaletteFile GribStubImagePlugin
Hdf5StubImagePlugin IcnsImagePlugin
IcoImagePlugin ImImagePlugin Image ImageChops ImageCms ImageColor ImageDraw ImageDraw2 ImageEnhance ImageFile ImageFilter ImageFont ImageGrab ImageMath ImageMode ImageMorph ImageOps ImagePalette ImagePath ImageQt ImageSequence ImageShow ImageStat ImageTk ImageTransform ImageWin ImtImagePlugin IptcImagePlugin
JpegImagePlugin JpegPresets McIdasImagePlugin
MpoImagePlugin MspImagePlugin OleFileIO PSDraw PaletteFile PalmImagePlugin
PcdImagePlugin PcfFontFile PcxImagePlugin PdfImagePlugin PdfParser PixarImagePlugin
PsdImagePlugin PyAccess SgiImagePlugin
SunImagePlugin TarIO TgaImagePlugin
TiffImagePlugin TiffTags WalImageFile
WmfImagePlugin XVThumbImagePlugin
from PIL import Image
At this point you are ready to open an image file. All you need is to assign an image path and then open it with Image using: where f is the path.
f = r'c:/Users/t/Desktop/IMG_5510.jpg'
img =

Now, img is your image. If you print it you will get a memory address similar to what happens when your print some builtin functions in Python such as: filter, map, range and zip.

--PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1280x960 at 0x12E16610F60--

Now, img is your image. If you print it you will get a memory address similar to what happens when your print some builtin functions in Python such as: filter, map, range and zip.

To open the image all you have to do is use the .show() method.
Showing image with PIL
So now that you have an image in your program, what’s next? A good idea is to discover all the options using help() function. Also keep in mind that you can use powerful Python basics such as for loops or while loops to edit or create batches of image files.

Let’s see what methods will show if we run help on PIL’s Image object.

Here is a summarized list of all the functions you can use with Image object.
crop(self, box=None)
draft(self, mode, size)
effect_spread(self, distance)
filter(self, filter)
fromstring(self, *args, **kw)
getchannel(self, channel)
getcolors(self, maxcolors=256)
getdata(self, band=None)
getpixel(self, xy)
seek(self, frame)
transpose(self, method)
By using help() function, you can explore all the possibilities you can do with your image (now named img) and with PIL module. Here are some ideas:


from PIL import ImageFilter
Now that we know some of the fundamentals of PIL, let’s try to do some tricks. Using ImageFilter you can apply some awesome filters to your images -with and within Python! But first of all, let’s explain something that can be quite confusing for a beginner. When applying Image.filter() we will use another object inside the parenthesis. You can think about it this way. Image.filter() is the method to apply filters to images in PIL. And inside the parenthesis we will use ImageFilter library. This may seem a bit awkward but separating the filters and the method to apply them: .filter() can be helpful to understand it better.


img2 = img.filter(ImageFilter.EMBOSS)


img2 = img.filter(ImageFilter.CONTOUR)


img2 = img.filter(ImageFilter.SHARPEN)


img2 = img.filter(ImageFilter.MaxFilter(size=3))"img2.jpg")
  • MaxFilter will make darker spots brighter in an image. What it does is, it takes a pixel and searches its neighboring pixels, it replaces the pixel with maximum pixels from the neighbor pixels.

  • What that means is it will take a pixels and replace it with the brighter pixels surrounding it. This will create an effect that lessens the dark spots. Brilliant isn’t it? 


img3 = img.filter(ImageFilter.MinFilter(size=3))"img3.jpg")
MinFilter is the opposite of MaxFilter. What it will do is take a pixel and search its neighbor pixels and replace it with the minimum value (darker pixel). This will create an effect that multiplies the dark spots.
Light Pixel Multiplication with MaxFilter in Python PIL
Dark Pixel Multiplication with MinFilter in Python PIL
So, it’s rather simple to apply these ImageFilter filters but where do they come from? How to know which builtin filters we can apply? Also simple, if you run the help function on ImageFilter you can see all the details about this object. Here is a summary:

Built-in Filters


Multiband Filters


Rank Filters




from PIL import ImageEnhance
ImageEnhance module has great image enhancing features as well. You have to first create an enhancer object and then apply .enhance() method to it.

Built-in Objects





f = r'c:/Users/ABC/Desktop/IMG_5510.jpg'
img =


An .enhance() value of 1 will apply original brightness.
applier = ImageEnhance.Brightness(img)


An .enhance() value of 1 will apply original contrast. A contrast value of 2 usually enhances an image in a nice way.
applier = ImageEnhance.Contrast(img)


An .enhance() value of 1 will apply original sharpness.
applier = ImageEnhance.Sharpness(img)


An .enhance() value of 1 will apply original colors, 0 will give black&white, 100 will blow out the colors and 0.5 will be half way into a black&white image.
applier = ImageEnhance.Color(img)
If you’d like to save an image just apply .save() method instead of .show(). Inside the parenthesis you can write the full path but if you just type a name, it will still save to a default folder, typically your c://Users/Name folder.


ImageDraw in PIL (pillow) works similar to ImageFilter and ImageEnhance. First you create a drawing object with the image you’d like to work on and then apply it. Look below:

Drawing a rectangle

import PIL
from PIL import ImageDraw

f = r"c://Users/ABC/0559.jpg"
img = Drawer = ImageDraw.Draw(img) Drawer.rectangle((x1, y1, x2, y2))

Basically, those last two lines are all you need to start drawing on your image.

Let’s explain how the coordinates work in Drawer.rectangle which can be a bit confusing sometimes. And let’s also look at some of the parameters that can make your drawing object more defined such as color and thickness.

  • x1 is how far you’d like to start from the left side
  • y1 is how far you’d like to start from the top
  • x2 is how far you’d like to go to the right
  • y2 is how far you’d like to go to the bottom
  • coordinates are written in that order: (x1,y1,x2,y2)

This is similar to drawing a rectangle in Paint or many other image editors. If you remember you start drawing from the top left (x1, y1) and you finish at the bottom right (x2, y2).

Here are the codes used to create this rectangle on this image:

f = r"c://Users/ABC/0559.jpg"
img =

Drawer = ImageDraw.Draw(img)
Drawer.rectangle((1200,400,1700,1000), fill=None, outline="red", width=3)

Drawing an ellipse

Here are the codes used to create this rectangle on this image:

f = r"c://Users/ABC/0559.jpg"
img =

Drawer = ImageDraw.Draw(img)
Drawer.ellipse((1200,400,1700,1000), fill=None, outline="blue", width=4)


You need very few lines to open and show an image the rest is an endless sea of image manipulation options.

import PIL
from PIL import Image
f = r"c://Users/ABC/Desktop"

img =

You can save the image with below code:

f = r"c://Users/ABC/Desktop"

Image module has tons of useful features but also:

  • ImageEnhance to enhance some image features.
  • ImageFilter to apply filters.
  • ImageDraw to draw objects on the image.

It’s useful to first create an object while using all of these above:

Filter_1= ImageFilter.EMBOSS
Enhancer = ImageEnhance.Color(img)
Drawer = ImageDraw.Draw(img)
  • We suggest you to use a proper IDE such as Spyder or PyCharm for this tutorial so you can take advantage of smart completion features of the parameters and arguments. Also showing the image will be more convenient since you won’t need to take any additional steps. In Jupyter you will need to import image displaying libraries from IPython to do this.
  • Closing, we hope you enjoyed this tutorial. Image manipulation can be a lot of fun and offers great power. Using Python and PIL library it’s also more straightforward than ever.