Image Conversion Web App with Python and Flask
Tags: webdev, python, html, javascript
Published: 2023-02-10 02:03:49

In this article, we will create a simple image conversion web app using Python Flask and Python Image Library (PIL). This app allows users to upload an image, choose a format, and convert the image to the selected format. The article is intended for developers who are looking to build a simple web app with Flask.

## **Installing Required Packages**
To start, we will install two packages that we will be using in this project: Flask and Pillow (Python Image Library). To install them, run the following command:
`pip install Flask Pillow
`

## **Creating Required Files**
We will be creating three files for this project: **app.py**, **convert.html**, and **index.html**.

## **Flask Server**
Let's start by creating a Flask server in the **app.py** file. In this file, we will write the code for our Flask application. First, we will import the necessary modules and then the code for running our app

```
from flask import Flask, render_template, request, redirect
from PIL import Image
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return render_template("index.html")

if __name__ == '__main__':
    app.run(debug=True)
```
In the code above, we are importing the required packages and creating a Flask instance. We also have a home page route that returns an **index.html** file.

## **HTML and JavaScript Template**
Next, we will create two HTML templates: **index.html** and **convert.html** inside a templates folder. The **index.html** file will be the home page of our web application, where users will be able to upload images to be converted. The **convert.html** file will be used to display the converted image.
We can use a ready-made HTML template from a website such as https://bootstrapious.com for the index.html file. This template will provide a user-friendly interface for uploading images and selecting the format to convert the image to.

In the Index.html template, we will include in the form the following parameters:

```
<form action="/convert" enctype="multipart/form-data" method="post" id="form">

```

This form will be used to submit the uploaded image for conversion in a **"/convert"** route which we will create shortly in our **app.py**, also we are sending a multipart/form-data because we are submitting a text and an image, so we are splitting the data.

In this template, you will need to add the following code for the image input field:

```
<input id="upload" type="file" onchange="readURL(this);" name="image" accept=".jpg, .png, .bmp, .eps, .gif, .im, .msp, .pcx, .ppm, .tiff" class="form-control border-0">

```

The **"accept"** attribute specifies the types of files that can be uploaded.


We will also add a select field to specify the format to convert the image to:

```
<div class="input-group mb-3">
                <select class="custom-select" id="inputGroupSelect02" name="format">
                    <option value="BMP" selected>BMP</option>
                    <option value="EPS">EPS</option>
                    <option value="GIF">GIF</option>
                    <option value="IM">IM</option>
                    <option value="JPEG">JPEG</option>
                    <option value="MSP">MSP</option>
                    <option value="PCX">PCX</option>
                    <option value="PNG">PNG</option>
                    <option value="PPM">PPM</option>
                    <option value="TIFF">TIFF</option>
                </select>
                <div class="input-group-append">
                     <label class="input-group-text" for="inputGroupSelect02">To</label>
                </div>
            </div>
            <button type="submit" class="btn btn-outline-secondary">convert</button>
            <br>.
```

This code displays the options for the image formats that we offer to convert to.

Our **"Index.html"** file will end up looking like this:

```
<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Image converter</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="static/css/index.css">
        <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
       <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
    </head>
    <body>
        
        <h1> This is an image converter </h1>
        <div class="container py-5">

    <!-- For demo purpose -->
    <header class="text-white text-center">
        <h1 class="display-4">Bootstrap image upload</h1>
        <p class="lead mb-0">Build a simple image upload button using Bootstrap 4.</p>
        <p class="mb-5 font-weight-light">Snippet by
            <a href="https://bootstrapious.com" class="text-white">
                <u>Bootstrapious</u>
            </a>
        </p>
        <img src="https://bootstrapious.com/i/snippets/sn-img-upload/image.svg" alt="" width="150" class="mb-4">
    </header>


    <div class="row py-4">
        <div class="col-lg-6 mx-auto">

            <!-- Upload image input-->
            <form action="/convert" enctype="multipart/form-data" method="post" id="form">
            <div class="input-group mb-3 px-2 py-2 rounded-pill bg-white shadow-sm">
                <input id="upload" type="file" onchange="readURL(this);" name="image" accept=".jpg, .png, .bmp, .eps, .gif, .im, .msp, .pcx, .ppm, .tiff" class="form-control border-0">
                <label id="upload-label" for="upload" class="font-weight-light text-muted">Choose file</label>
                <div class="input-group-append">
                    <label for="upload" class="btn btn-light m-0 rounded-pill px-4"> <i class="fa fa-cloud-upload mr-2 text-muted"></i><small class="text-uppercase font-weight-bold text-muted">Choose file</small></label>
                </div>
            </div>
            

            <div class="input-group mb-3">
                <select class="custom-select" id="inputGroupSelect02" name="format">
                    <option value="BMP" selected>BMP</option>
                    <option value="EPS">EPS</option>
                    <option value="GIF">GIF</option>
                    <option value="IM">IM</option>
                    <option value="JPEG">JPEG</option>
                    <option value="MSP">MSP</option>
                    <option value="PCX">PCX</option>
                    <option value="PNG">PNG</option>
                    <option value="PPM">PPM</option>
                    <option value="TIFF">TIFF</option>
                </select>
                <div class="input-group-append">
                     <label class="input-group-text" for="inputGroupSelect02">To</label>
                </div>
            </div>
            <button type="submit" class="btn btn-outline-secondary">convert</button>
            <br>
            </form>
            <!-- Uploaded image area-->
            <p class="font-italic text-white text-center">The image uploaded will be rendered inside the box below.</p>
            <div class="image-area mt-4"><img id="imageResult" src="#" alt="" class="img-fluid rounded shadow-sm mx-auto d-block"></div>

        </div>
    </div>
</div>


        <script src="static/javascript/index.js"></script>
        
        <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.slim.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>


        <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
    </body>
</html>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vu7ceomwbqz3wu8bd3w3.png)


**In the next part of our code, we handle the file conversion process in the backend.**

Now, Let's create our **"/convert"** route in our app.py:

```
@app.route('/convert', methods=["POST", "GET"])
def convert():
    return redirect("/")
```
So what we did here was just create the route that accepts both POST and GET requests. If a GET request is made, the user will be redirected to the main page.


Now, Let's get our image from the frontend:

```
@app.route('/convert', methods=["POST", "GET"])
def convert():
    if request.method == "POST":
        file = request.files["image"]
        format = request.form.get("format")
   return redirect("/")
```
Here, if a POST request is made, we retrieve two inputs from the user. The first input is the **image** file, and the second input is the desired **format** for the conversion. The desired format is obtained from the **select** input in the **"index.html"** file.
Note: the **name** parameter must be **format** else it won't work.


After retrieving the inputs, we split the filename into two parts - the output image and its file type. We then convert the desired format to lowercase.

Next, we open the image using the Python Image Library (PIL) and save it with the desired format. We then move the image to a newly created folder called **"images"** within the **"static"** folder to keep our files organized.

Finally, we generate the URL of the image and pass it to the **"convert.html"** template using the **"render_template"** function. The final output of the **"app.py"** file is as follows:

```
@app.route("/convert", methods=["POST", "GET"])
def convert():
    if request.method == "POST":
        file = request.files["image"]
        format = request.form.get("format")
        outputimage, x = file.filename.split('.')
        format = format.lower()
        outputimage = outputimage + "." + format
        with Image.open(file) as image:
            image.convert('RGB').save( outputimage)
            path = 'static/images/' + outputimage 
            os.rename(outputimage, path )
            filepath = 'images/' + outputimage
            image_url = url_for('static', filename=filepath)
        return render_template("convert.html", image_url=image_url)
        
    return redirect("/")
```
Now that we have our image conversion code working, it's time to display the image on a webpage and make it look presentable.

We start by opening our convert.html file and displaying the image using the following HTML code:

```
<div class="container">
        <img src="{{ image_url }}" class="img-fluid" alt="Responsive image">
        <br><br>
        <a download href="{{ image_url }}" class="download-btn">Download</a>
        <a href="javascript:history.back()" class="back-btn">Back</a>
      </div>
```

Here, the **"<img>"** tag is used to display the image, and the **"src"** attribute is set to **"image_url"** so that the URL of the converted image is dynamically displayed on the page.


We also added a download button using the **<a>** tag, which allows the user to **download** the image with a single click. The download attribute is used to specify that the linked resource should be downloaded, rather than navigating to it.

Now, let's make the page look more attractive by adding some basic styling using CSS.

```
<style>
        .container {
        text-align: center; 
        background-color: #f2f2f2; 
        padding: 20px; 
        border-radius: 10px;
      }
      
      .img-fluid {
        width: 50%;
        height: auto;
      }
      
      .download-btn, .back-btn {
        background-color: #757f9a;; 
        color: white; 
        padding: 10px 20px; 
        border-radius: 20px; 
        text-decoration: none;
        margin-top: 20px;
      }
    </style>
```

The **.container** class sets the background color, padding, and border radius for the overall container. The **.img-fluid** class sets the width of the image to 50% and the height to **auto** to make the image responsive.

The **.download-btn** and **.back-btn** classes set the background color, text color, padding, border radius, and margin for the download and back buttons, respectively.

To run the app, navigate to the directory containing the files and run the following command in the terminal:

```
python app.py
```
Then, open a web browser and navigate to `http://localhost:5000`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlgajuh1dozcqlusnp16.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q66tfrjbhz2svznnob2v.png)

And that’s it, our image conversion app is now complete! You can now run the application using the command ‘python app.py’ and you should be able to convert your images to the desired format by uploading an image from your computer, selecting a format from the dropdown, and clicking the convert button. You can then download the converted image from the app and see the changes for yourself.

We have now successfully created a simple and effective image conversion app using Flask, PIL and HTML/CSS. This app can be further improved by adding more image formats, error handling, and more styling to make it visually appealing, but for now, we have a working model that can be used as a starting point. The complete code for this project is available on my GitHub repository at https://github.com/feranmiodugbemi10/imageconverter.git. Feel free to clone the repository and use the code as a starting point for your own projects. 

I hope this article was helpful in understanding how to create an image conversion app using Flask, PIL and HTML/CSS. If you have any questions or feedback, please let me know in the comments section below.