{"id":7,"date":"2022-01-11T04:49:15","date_gmt":"2022-01-11T04:49:15","guid":{"rendered":"https:\/\/noerguerra.com\/?p=7"},"modified":"2022-05-11T20:37:18","modified_gmt":"2022-05-11T20:37:18","slug":"a-guide-to-efficient-image-compression-with-python","status":"publish","type":"post","link":"https:\/\/noerguerra.com\/blog\/a-guide-to-efficient-image-compression-with-python\/","title":{"rendered":"A guide to efficient image compression with Python"},"content":{"rendered":"<p>Reducing the file size of images can be very useful to save disk space or reduce bandwidth use. With Python, you can compress dozens or hundreds of images in a quick and efficient way.<\/p>\n<h2>What you will learn<\/h2>\n<p>After reading this post, you will know:<\/p>\n<ul>\n<li>How to install the Python Image Library (Pillow)<\/li>\n<li>How to change the resolution of an image using Pillow<\/li>\n<li>How to reduce the size of JPEG and PNG images through different ways using Python and Pillow<\/li>\n<li>How to bulk compress all the images in a directory using Python<\/li>\n<\/ul>\n<h2>How to reduce the size of an image<\/h2>\n<p>Digital images are composed of multiple individual pixels. The process of reducing the size of an image is known as <strong>image compression<\/strong>. <\/p>\n<p>There are two ways to compress an image:<\/p>\n<ul>\n<li>Removing information from the image (Lossy compression)<\/li>\n<li>Grouping common patterns together (lossless compression)<\/li>\n<\/ul>\n<p>JPEG and PNG are popular lossy and lossless compression formats, respectively.<\/p>\n<blockquote>\n<p>If you are interested in learning more about image compression, you can read <em><a href=\"https:\/\/www.keycdn.com\/support\/what-is-image-compression\">What is image compression?<\/a><\/em> from keycdn.com<\/p>\n<\/blockquote>\n<p>You can apply lossy and lossless compression to image files with Python using the Pillow Library.<\/p>\n<h2>Installing Pillow<\/h2>\n<p>Pillow is a fork of the original Python Image Library (PIL) that was developed after PIL stopped being updated in 2009. Pillow adds support for Python 3 and receives constant updates.<\/p>\n<p>Pillow brings image manipulation to Python, and you can install it running the following command in your console:<\/p>\n<p><code>pip install Pillow<\/code><\/p>\n<p>You can make sure that Pillow has been successfully installed by opening a Python interpreter and typing<\/p>\n<p><code>import PIL<\/code><\/p>\n<p>If no message appears, the module has been successfully added to your system.<\/p>\n<h2>Change the resolution of an image using Pillow<\/h2>\n<p>Lowering the resolution of an image in many cases will result in an image with a smaller file size.<\/p>\n<p>Resizing an image to a lower resolution can be done very easily like this:<\/p>\n<pre><code>from PIL import Image\n\nimg = Image.open(\"image.jpg\")\nnew_size = (800, 600)\nimg = img.resize(new_size)\nimg.save(\"new_image.jpg\", format=\"JPEG\")<\/code><\/pre>\n<p>What this code does is:<\/p>\n<ol>\n<li>Import the Image module from Pillow<\/li>\n<li>Open <code>image.jpg<\/code> and store it in the variable <code>img<\/code><\/li>\n<li>Define the new size of the image. This value has to be an iterable object, like a tuple or a list.<\/li>\n<li>Resize the image and update <code>img<\/code> with the output of the method.<\/li>\n<li>Save the new image as a JPEG file in a new location. If you wish to overwrite the original image, you can save the new image in the same location as the original.<\/li>\n<\/ol>\n<p>Alternately, you may use the <code>Image.thumbnail<\/code> method if you wish to keep the aspect ratio by default:<\/p>\n<pre><code>from PIL import Image\n\nimg = Image.open(\"image.jpg\")\nnew_size = (800, 600)\nimg.thumbnail(new_size)\nimg.save(\"new_image.jpg\", format=\"JPEG\")<\/code><\/pre>\n<p><code>Image.thumbnail<\/code> is very similar to <code>Image.resize<\/code> but with some important differences:<\/p>\n<ul>\n<li><code>Image.thumbnail<\/code> resizes the image to the largest size that preserves aspect ratio.<\/li>\n<li>The output image size will not exceed the original image.<\/li>\n<li>The output image size will not exceed the size specified in the arguments of the function.<\/li>\n<li>The <code>Image<\/code> object is modified in place. <code>Image.thumbnail<\/code> changes the original image and returns <code>None<\/code> as a result.<\/li>\n<\/ul>\n<h2>Optimize images<\/h2>\n<p>Saving an image with the <code>optimize<\/code> flag is the easiest way to reduce the size of an image with Python and Pillow without losing quality.<\/p>\n<pre><code>from PIL import Image\n\nimg = Image.open(\"image.jpg\")-\nimg.save(\"new_image.jpg\", format=\"JPEG\", optimize=True)<\/code><\/pre>\n<p>When the <code>optimize<\/code> flag is present and <code>True<\/code>, Pillow will perform an additional pass on the image to select optimal encoder settings. This can have an important impact in the size of the image, for example, consider the following image:<\/p>\n<p><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img-1024x683.jpg\" alt=\"\" \/><\/a><\/p>\n<p>After saving the image with <code>optimize=True<\/code> the image size was reduced in 91 KB, or 45 % of the original size. In the following table you can see a direct comparison between the original image, that image saved using the default <code>Image.save<\/code> parameters, and that image saved with the <code>optimize<\/code> flag set to <code>True<\/code>.<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">Original image<\/th>\n<th style=\"text-align: center;\">Default parameters<\/th>\n<th style=\"text-align: center;\"><code>optimize=True<\/code><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img-150x150.jpg\" alt=\"\" \/><\/a><\/td>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img1.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img1-150x150.jpg\" alt=\"\" \/><\/a><\/td>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img2.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img2-150x150.jpg\" alt=\"\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">166 KB<\/td>\n<td style=\"text-align: center;\">115 KB<\/td>\n<td style=\"text-align: center;\">75 KB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Consider using the <code>optimize<\/code> flag when compressing a large amount of files. The size reduction will not always be this dramatic, but the amount of storage can add up if you are dealing with hundreds of images.<\/p>\n<p>You can use the <code>optimize<\/code> option when saving JPEG, PNG and GIF images.<\/p>\n<h2>Reducing the file size of a JPEG image<\/h2>\n<h3>Reducing the quality of an image<\/h3>\n<p>JPEG files can be saved with different amounts of compression. This is defined by the <em>quality<\/em> level of the image. An image saved at 100% quality will have virtually no compression, while an image saved at 35% quality will be noticeably affected by the algorithm.<\/p>\n<p>Below you can see how to define the quality of a JPEG image when saving it with Pillow:<\/p>\n<pre><code>from PIL import Image\n\nimg = Image.open(\"image.jpg\")\nimg.save(\"new_image.jpg\", format=\"JPEG\", quality=85)<\/code><\/pre>\n<p>The <code>quality<\/code> parameter only works with JPEG images, and its value must be an integer. When the quality is not specified, the default value of <code>75<\/code> is used by Python instead.<\/p>\n<p>Lowering the quality of an image is one of the most effective ways to reduce its size, as you can see in the following table.<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">95% Quality<\/th>\n<th style=\"text-align: center;\">75% Quality<\/th>\n<th style=\"text-align: center;\">35% Quality<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img_95.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img_95-150x150.jpg\" alt=\"\" \/><\/a><\/td>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img_75.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img_75-150x150.jpg\" alt=\"\" \/><\/a><\/td>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img_35.jpg\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/img_35-150x150.jpg\" alt=\"\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">299 KB<\/td>\n<td style=\"text-align: center;\">113 KB<\/td>\n<td style=\"text-align: center;\">56.7 KB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You can see how different values affect the quality of an image in this <a href=\"http:\/\/fotoforensics.com\/tutorial-estq.php\">tutorial from FotoForensics.<\/a><\/p>\n<h2>Reducing the file size of a PNG image<\/h2>\n<h3>Reducing the color palette<\/h3>\n<p>In Pillow, the <code>mode<\/code> of an image defines the type and depth of each pixel in the image. For example, a image with a mode of <code>1<\/code> will have pixels with a depth of 1-bit, allowing only two colors (black and white).<\/p>\n<p>Other common color modes are:<\/p>\n<ul>\n<li><code>L<\/code> (8-bit pixels, black and white)<\/li>\n<li><code>P<\/code> (8-bit pixels, mapped to any other mode using a color palette)<\/li>\n<li><code>RGB<\/code> (3&#215;8-bit pixels, true color)<\/li>\n<li><code>RGBA<\/code> (4&#215;8-bit pixels, true color with transparency mask)<\/li>\n<\/ul>\n<p>Choosing the right mode for your image has an important role in reducing its size. <\/p>\n<blockquote>\n<p>Read more about color modes in the <a href=\"https:\/\/pillow.readthedocs.io\/en\/stable\/handbook\/concepts.html#modes\">Pillow documentation<\/a>.<\/p>\n<\/blockquote>\n<p>You can reduce the amount of colors in a PNG image using Pillow through an operation called <code>quantize<\/code>. This operation will change the mode of the image to <code>P<\/code>.<\/p>\n<pre><code>from PIL import Image\n\nimg = Image.open(\"image.png\")\nimg = img.quantize(colors=128)\nimg.save(\"new_image.png\")<\/code><\/pre>\n<p>The <code>quantize<\/code> method will return a new image with the specified amount of colors as upper bound. For example, the following image only has 16 colors despite calling <code>img.quantize(colors=128)<\/code>.<\/p>\n<p><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python.png\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python-150x150.png\" alt=\"\" \/><\/a><\/p>\n<pre><code>In [1]: from PIL import Image\n\nIn [2]: img = Image.open(\"image.png\")\n\nIn [3]: img = img.quantize(colors=128)\n\nIn [4]: len(img.palette.colors)\nOut[4]: 16<\/code><\/pre>\n<p>After calling <code>Image.quantize<\/code> the size of the image is reduced considerably, specially when the <code>optimize<\/code> flag is present. Look at the comparison in the following table:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">Original Image<\/th>\n<th style=\"text-align: center;\">After <code>Image.quantize<\/code><\/th>\n<th style=\"text-align: center;\">After <code>optimize=True<\/code><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python.png\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python-150x150.png\" alt=\"\" \/><\/a><\/td>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python_1.png\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python_1-150x150.png\" alt=\"\" \/><\/a><\/td>\n<td style=\"text-align: center;\"><a href=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python_2.png\"><img decoding=\"async\" src=\"https:\/\/noerguerra.com\/wp-content\/uploads\/2022\/01\/python_2-150x150.png\" alt=\"\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">29 KB<\/td>\n<td style=\"text-align: center;\">9 KB<\/td>\n<td style=\"text-align: center;\">7 KB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>How to compress all the images in a directory<\/h2>\n<p>When you have a directory full of images, Pillow makes it simple to reduce their size, as you can see in the following script:<\/p>\n<pre><code>from PIL import Image\nfrom pathlib import Path\n\ndirectory = Path(\"Images\/\")\nfile_list = directory.iterdir()\nmax_size = (1280, 720)\n\nfor file in file_list:\n    img = Image.open(file)\n    file_name = file.stem + \"_compressed\" + file.suffix\n\n    img.thumbnail(max_size)\n    if img.format == \"JPEG\":\n        img.save(file.with_name(file_name), quality=85, optimize=True)\n    elif img.format == \"PNG\":\n        img = img.quantize(colors=256)\n        img.save(file.with_name(file_name), optimize=True)<\/code><\/pre>\n<p>Step by step, this script:<\/p>\n<ol>\n<li>Imports the Pillow and pathlib modules.<\/li>\n<li>Stores the directory where the images are located in <code>directory<\/code>.<\/li>\n<li>Defines a max size for each image.<\/li>\n<li>For each image:\n<ol>\n<li>Open as an <code>Image<\/code> object.<\/li>\n<li>Define a new file name with <code>_compressed<\/code> appended at the end.<\/li>\n<li>Resize to <code>max_size<\/code> if the image is larger than <code>max_size<\/code>.<\/li>\n<li>Save with the new file name and 85% quality (if JPG) or 256 colors at most (if PNG).<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<hr \/>\n<p>Pillow provides a simple way to perform complex operations on your images using Python. Learn more about what you can do with this module by reading <a href=\"https:\/\/pillow.readthedocs.io\/en\/stable\/index.html\">the official documentation<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reducing the file size of images can be very useful to save disk space or reduce bandwidth use. With Python, you can compress dozens or hundreds of images in a quick and efficient way. What you will learn After reading this post, you will know: How to install the Python Image Library (Pillow) How to&hellip; <a class=\"more-link\" href=\"https:\/\/noerguerra.com\/blog\/a-guide-to-efficient-image-compression-with-python\/\">Continue reading <span class=\"screen-reader-text\">A guide to efficient image compression with Python<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":53,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-7","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","entry"],"_links":{"self":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts\/7","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/comments?post=7"}],"version-history":[{"count":11,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts\/7\/revisions"}],"predecessor-version":[{"id":89,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts\/7\/revisions\/89"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/media\/53"}],"wp:attachment":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/media?parent=7"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/categories?post=7"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/tags?post=7"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}