Browse Source

Generate tiles in python instead of shell + imagemagick

Baptiste Jonglez 10 years ago
parent
commit
3ccf76ec2a
4 changed files with 50 additions and 127 deletions
  1. 45 0
      panorama/gen_tiles.py
  2. 0 119
      panorama/gen_tiles.sh
  3. 4 6
      panorama/models.py
  4. 1 2
      panorama/static/panorama/js/pano.js

+ 45 - 0
panorama/gen_tiles.py

@@ -0,0 +1,45 @@
+"""
+Given an image, generate a set of tiles at various zoom levels.
+The format of the filename of times is:
+
+  00<zoom>-00<x>-00<y>.jpg
+
+where zoom is 0 for the original size, 1 when the image is downscaled 2
+times, 2 when the image is downscaled 4 times, etc.
+"""
+
+import sys
+import os
+import tempfile
+
+import PIL.Image
+
+
+def gen_tiles(image, output_path, min_scale=0, max_scale=8, crop_x=256, crop_y=256):
+    orig_im = PIL.Image.open(image)
+    for scale in range(min_scale, max_scale + 1):
+        if scale == 0:
+            im = orig_im
+        else:
+            scaled_size = (orig_im.size[0] >> scale, orig_im.size[1] >> scale)
+            im = orig_im.resize(scaled_size)
+        for x in range(0, im.size[0], crop_x):
+            for y in range(0, im.size[1], crop_y):
+                geom = (x, y, min(im.size[0], x + crop_x),
+                        min(im.size[1], y + crop_y))
+                dest = os.path.join(output_path,
+                                    "{:03}-{:03}-{:03}.jpg".format(scale,
+                                                                   x // crop_x,
+                                                                   y // crop_y))
+                im.crop(geom).save(dest)
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Usage: {} <image>".format(sys.argv[0]))
+        print("Generate tiles of the given image.")
+        exit(1)
+    origin = sys.argv[1]
+    out = tempfile.mkdtemp(prefix="demo-pano-")
+    print("Generating tiles in {} ...".format(out))
+    gen_tiles(origin, out)

+ 0 - 119
panorama/gen_tiles.sh

@@ -1,119 +0,0 @@
-#!/bin/bash
-
-trap clean_tmp EXIT
-# fin d'eviter tout problème de locales en reste en C de base.
-
-set -e
-export LANG=C
-
-# pour éliminer systématiquement les fichier temporaires créés ic
-function clean_tmp() {
-    if [ -n "$wfname" ]; then
-	rm $wfname
-    fi
-    if [ -n "$tmp_file" ]; then
-	rm $tmp_file
-    fi
-}
-
-test_mode=false
-memory_limit=256
-crop_x=256
-crop_y=256
-min_scale=0
-max_scale=8
-usage="$0 [-x <x_tile_size>] [-y <y_tile_size>] [-p <prefix_result>] [-t] [-h] [-m <min_zoom>] [-M <max_zoom>] <image_to_convert>\n   example: $0 -r test_res"
-
-if ! which anytopnm pnmscale convert > /dev/null; then
-    echo "il faut installer les paquets netpbm et imageMagick pour utiliser ce script !"
-fi
-
-while getopts m:M:x:y:p:ht prs
- do
- case $prs in
-    t)        test_mode=true;;
-    x)        crop_x=$OPTARG;;
-    y)        crop_y=$OPTARG;;
-    m)        min_scale=$OPTARG;;
-    M)        max_scale=$OPTARG;;
-    p)        prefix=$OPTARG;;
-    \? | h)   echo -e $usage
-              exit 2;;
- esac
-done
-shift `expr $OPTIND - 1`
-
-if [ -z "$1" ]; then
-    echo -e "usage :\n$usage"
-    exit 1
-elif [ ! -f "$1" ]; then
-    echo -e "le paramètre $1 ne correspond pas à un nom de fichier !"
-    exit 1
-fi
-
-fname=$1
-dir=$(dirname $fname)
-
-if [ -z "$prefix" ]; then
-    prefix=$(basename $1|sed 's/\..*$//')
-fi
-
-wfname=$(mktemp ${prefix}_XXXX.pnm)
-if ! $test_mode; then
-    anytopnm $fname > $wfname
-else
-    echo "anytopnm $fname > $wfname"
-fi
-
-echo "préfixe : "$prefix
-
-tmp_file=$(mktemp)
-
-for ((z=$min_scale; z <= $max_scale; z++))
-do
-    fprefix=${prefix}00$z
-    printf -v ratio %1.4lf $(echo "1 / (2^$z)" | bc -l)
-    echo génération du ratio $ratio
-    zwfname=$tmp_file
-
-    if $test_mode; then
-	if [ $ratio = 1.0000 ]; then
-	    zwfname=$wfname
-	else
-	    echo "pnmscale $ratio $wfname > $zwfname"
-	fi
-	echo convert $zwfname \
-	    -limit memory $memory_limit \
-            -crop ${crop_x}x${crop_x} \
-            -set filename:tile "%[fx:page.x/${crop_x}]_%[fx:page.y/${crop_y}]" \
-            +repage +adjoin "${fprefix}_%[filename:tile].jpg"
-    else
-	if [ $ratio = 1.0000 ]; then
-	    zwfname=$wfname
-	else
-	    if ! pnmscale $ratio $wfname > $zwfname; then
-		echo "operation 'pnmscale $ratio $wfname > $zwfname' en erreur"
-		exit 1
-	    fi
-	fi
-	if convert $zwfname \
-	    -limit memory $memory_limit \
-            -crop ${crop_x}x${crop_x} \
-            -set filename:tile "%[fx:page.x/${crop_x}]_%[fx:page.y/${crop_y}]" \
-            +repage +adjoin "${fprefix}_%[filename:tile].jpg"; then
-	    echo "Nombre des fichiers produits :" $(ls -la ${fprefix}_*| wc -l)
-	else
-	    echo "operation 'convert' en erreur"
-	    exit 2
-	fi
-    fi
-done
-
-if ! $test_mode; then
-## les lignes ci dessous sont destinnées à mettre des 0 en debut des numéros de ligne et de colonnes
-## Il y a certainement plus simple mais là c'est du rapide et efficace.
-    rename 's/_(\d\d)_(\d+\.jpg)$/_0$1_$2/' ${prefix}*
-    rename 's/_(\d)_(\d+\.jpg)$/_00$1_$2/' ${prefix}*
-    rename 's/_(\d+)_(\d\d)(\.jpg)$/_$1_0$2$3/' ${prefix}*
-    rename 's/_(\d+)_(\d)(\.jpg)$/_$1_00$2$3/' ${prefix}*
-fi

+ 4 - 6
panorama/models.py

@@ -11,6 +11,8 @@ from django.core.exceptions import ValidationError
 from django.core.validators import MinValueValidator, MaxValueValidator
 from django.core.validators import MinValueValidator, MaxValueValidator
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.encoding import python_2_unicode_compatible
 
 
+from .gen_tiles import gen_tiles
+
 
 
 EARTH_RADIUS = 6371009
 EARTH_RADIUS = 6371009
 
 
@@ -129,15 +131,11 @@ class Panorama(ReferencePoint):
                             str(self.pk))
                             str(self.pk))
 
 
     def generate_tiles(self):
     def generate_tiles(self):
-        # The trailing slash is necessary for the shell script.
-        tiles_dir = self.tiles_dir()  + "/"
         try:
         try:
-            os.makedirs(tiles_dir)
+            os.makedirs(self.tiles_dir())
         except OSError:
         except OSError:
             pass
             pass
-        script = os.path.join(settings.BASE_DIR, "panorama", "gen_tiles.sh")
-        ret = subprocess.call([script, "-p", tiles_dir, self.image.path])
-        return ret
+        gen_tiles(self.image.path, self.tiles_dir())
 
 
     def __str__(self):
     def __str__(self):
         return "Panorama : " + self.name
         return "Panorama : " + self.name

+ 1 - 2
panorama/static/panorama/js/pano.js

@@ -278,8 +278,7 @@ function zero_pad(number) {
 }
 }
 
 
 function get_file_name(x, y, z) { // recherche du fichier correspondant au zoom et à la position
 function get_file_name(x, y, z) { // recherche du fichier correspondant au zoom et à la position
-	//return img_prefix+'/'+zero_pad(z)+'_'+zero_pad(x)+'_'+zero_pad(y)+'.jpg';
-	return img_prefix+'/'+zero_pad(z)+'_'+x+'_'+y+'.jpg';
+	return img_prefix+'/'+zero_pad(z)+'-'+zero_pad(x)+'-'+zero_pad(y)+'.jpg';
 }
 }
 
 
 function get_base_name() {
 function get_base_name() {