Click here to Skip to main content
15,886,091 members
Articles / Programming Languages / Python

Use Linux Apps With Django to Process Images

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
15 Jul 2019CPOL4 min read 5.4K   2  
Run command line Linux apps inside of Django to rapidly process 360 degree images.

Introduction

Image 1

Image credit: ikoma360

Using Linux image processing apps with Django offers a quick way to prototype and share ideas with a development or business team. This article shows how to extract EXIF metadata from a 360° image and apply a watermark. Although the example images were taken with a RICOH THETA camera, the techniques should work with any camera capable of taking a 360° image.

The examples in this article focus on the common Linux apps exiftool and ImageMagick running on a server. You can also use these tools on the command line on a Linux workstation. This allows photographers and all non-developers to assist with testing out different techniques.

You can see a live 360 image demo of the watermark created in this article here. Use your mouse to click and drag the image up, down, left, right.

Background

This is a continuation of the article 360 Image Web Gallery with Django and A-Frame. The display and processing of 360 images are slightly different from normal JPEG images. The previous article focused on 360 image display. This article shows simple processing techniques with Django and command line tools.

A 360 image is in equirectangular format and relies on metadata or EXIF data to show the orientation. Here is example data from a DNG/RAW image taken with a RICOH THETA Z1 by Toyo Fujita and stitched into equirectangular format with the vendor's software before exporting to JPG.

Full Pano Height Pixels : 3584 
Full Pano Width Pixels : 7168 
Initial Horizontal FOV Degrees : 70.0 
Initial View Heading Degrees : 0.0 
Initial View Pitch Degrees : 0.0 
Initial View Roll Degrees : 0 
Pose Heading Degrees : -54.1 
Pose Pitch Degrees : 0.0 
Pose Roll Degrees : 0.0 
Projection Type : equirectangular 
Stitching Software : RICOH THETA Stitcher v1.00.4 
Use Panorama Viewer : true 

In addition to orientation information, the EXIF data will also contain location information if there was a GPS unit available.

Date/Time Created : 2019:07:06 08:58:29-08:58 
GPS Altitude : 43 m Above Sea Level 
GPS Date/Time : 2019:06:17 19:59:06Z 
GPS Latitude : 34 deg 40\' 5.20" N 
GPS Latitude Ref : North 
GPS Longitude : 135 deg 26\' 15.80" E 
GPS Longitude Ref : East 
GPS Position : 34 deg 40\' 5.20" N, 135 deg 26\' 15.80" E 
Image Size : 7168x3584 
Megapixels : 25.7 
Shutter Speed : 1/13 

Extracting EXIF Data - Simple Example

Image 2

You can easily grab and modify EXIF data with a free tool called exiftool.

$ exiftool filename.jpg

In the Django views.py file, you can access Linux commands directly with subprocess.

In the example below, I've written the full path of the image file into the code. In production, you will need to grab this from your file upload code.

By pushing stdout through a PIPE, you can get the output back to Django.

Python
from django.shortcuts import render
from subprocess import Popen, PIPE, STDOUT

def homepage(request):
    # pass in file name after upload for production
    image_file_name = "/home/craig/Development/django/shell/shell/media/osaka-night.jpg"
    process = Popen(['exiftool', image_file_name], stdout=PIPE, stderr=STDOUT)
    output_byte = process.stdout.read()
    output_list = str(output_byte)[2:-1].strip().split('\\n')
    return render(request, 'home.html', 
    {"output": output_list, "filename": image_file_name.split('/')[-1]})

The output is passed to an HTML template as the third parameter of render. The data is sent as a dictionary. In this example, the value of the dictionary is a list. In the HTML, simply iterate through the list with a for loop. The {% for line in output %} section is how you get the data into your HTML.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdnjs.cloudflare.com/ajax/libs/aframe/0.7.1/aframe.min.js" 
integrity="sha256-SNDsmWBFQwJAjxLymQu5Fqh6rW4RKLJXUQboy3O0BUA=" 
crossorigin="anonymous"></script>
<style>
a-scene {
height: 400px;
width: 100%;
}

</style>
<title>EXIF Data extraction</title>
</head>
<body>
<h1>RICOH THETA exif output</h1>
  <a-scene embedded>
     <a-sky src="/media/{{filename}}" rotation="0 -130 0"></a-sky>
  </a-scene>

{% for line in output %}
    {{ line }}
    <br >
{% endfor %}

</body>
</html>

The example above uses A-Frame to display and navigate the 360 image.

Adding Watermark - More Complex Example

This example adds a watermark using ImageMagick. It takes two images and merges them together into a third image. The location of the third image is passed to an HTML template file for rendering with A-Frame.

The composite tool is part of ImageMagick. The command line sequence is:

$ composite -geometry +3000+1600 theta_logo.png toyo-hardrock.jpg new-image.jpg

To get this to work with subprocess Popen, you put the command and arguments in a list and pass the list to Popen.

Python
def watermark(request):
    # pass in file name after upload for production
    image_file_name = "/home/craig/Pictures/theta/2019/watermark/toyo-hardrock.jpg"
    logo_file_name = "/home/craig/Pictures/theta/2019/watermark/theta_logo.png"
    output_file = "/home/craig/Development/django/shell/shell/media/new-image.jpg"
    # composite is part of imagemagick package
    Popen(['composite', '-geometry', '+3000+1600', logo_file_name,
        image_file_name, output_file], stdout=PIPE, stderr=STDOUT)

    return render(request, 'watermark.html', {"output": output_file.split('/')[-1]})

The watermark is theta_logo.png. It is a PNG file with 40% transparency. ImageMagick is capable of merging this with a JPG file.

The HTML is simpler. It just displays the image using the file location that was passed to it.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>watermark</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/aframe/0.7.1/aframe.min.js" 
    integrity="sha256-SNDsmWBFQwJAjxLymQu5Fqh6rW4RKLJXUQboy3O0BUA=" 
    crossorigin="anonymous"></script>
</head>
<body>
    <h1>Watermark</h1>

    <a-scene>
            <a-sky src="/media/{{output}}" rotation="0 -130 0"></a-sky>
          </a-scene>
    
</body>
</html>

I'm setting the watermark into the center of the equirectangular image.

Image 3

You can also create a larger mask and merge the two images.

Image 4

Create a mask that is the same size as your original image. Place it at 0, 0.

Image 5

Live example

You can use the same mask template for all your images. As the mask is embedded into the image itself, there is little risk of theft.

Points of Interest

Although it's a matter of personal choice, working with Python on the back-end is more enjoyable than working with JavaScript. Django is considered old compared to something like a JavaScript MEAN stack, but it's super fast and easy to get something up in an hour. If you want something even faster, you can use Linux commands within Python. You can also put multiple Linux commands in a bash script and run the script from inside of Django. Yes, it's a hack, but sometimes you just want to show something quickly.

Moving to Production

After prototyping, you should use a Python or C library to process images with Django. A common Python graphics library is Pillow.

More Tests, Info, and Experiments

The code used in this test is available on GitHub.

A bunch of us are running tests on using Django with 360 images. Check out the latest discussion here.

History

  • 16th July, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --