Introduction
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
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.
from django.shortcuts import render
from subprocess import Popen, PIPE, STDOUT
def homepage(request):
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.
<!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.
def watermark(request):
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"
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.
<!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.
You can also create a larger mask and merge the two images.
Create a mask that is the same size as your original image. Place it at 0, 0.
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