tasks.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals, division, print_function, absolute_import
  3. import os
  4. import shutil
  5. import subprocess
  6. import tempfile
  7. from celery import shared_task, chain
  8. from .gen_tiles import gen_tiles
  9. from .hugin import parse_pto, compute_connected_components, filter_images
  10. @shared_task
  11. def generate_tiles(*args, **kwargs):
  12. return gen_tiles(*args, **kwargs)
  13. # Pipeline of hugin-based panorama generation
  14. # TODO: do something with the return code of the subprocess calls
  15. @shared_task
  16. def pto_gen(output_pto, images):
  17. # Projection type 1 is cylindrical
  18. subprocess.call(["pto_gen", "-p", "1", "-o", output_pto] + images)
  19. return output_pto
  20. @shared_task
  21. def cpfind(input_pto, output_pto):
  22. subprocess.call(["cpfind", "-o", output_pto, "--multirow", "--celeste", input_pto])
  23. return output_pto
  24. @shared_task
  25. def prune_images(input_pto, output_pto):
  26. """
  27. Given a pto and control points, keep the largest connected components
  28. of linked images. This allows to discard images that are not
  29. connected to the rest of the images.
  30. Implementation details: the python interface to Hugin (hsi) could be
  31. used, but it does not export precisely the bits we need to compute the
  32. graph of linked images (the CPGraph class). Thus, the simplest
  33. solution is to parse the .pto file by hand.
  34. """
  35. with open(input_pto) as f:
  36. components = compute_connected_components(parse_pto(f))
  37. component = max(components, key=len)
  38. filter_images(input_pto, component, output_pto)
  39. print("Keeping largest component ({} images)".format(len(component)))
  40. return output_pto
  41. @shared_task
  42. def cpclean(input_pto, output_pto):
  43. subprocess.call(["cpclean", "-o", output_pto, input_pto])
  44. return output_pto
  45. @shared_task
  46. def linefind(input_pto, output_pto):
  47. subprocess.call(["linefind", "-o", output_pto, input_pto])
  48. return output_pto
  49. @shared_task
  50. def autooptimiser(input_pto, output_pto):
  51. subprocess.call(["autooptimiser", "-a", "-m", "-l", "-s", "-o", output_pto, input_pto])
  52. return output_pto
  53. @shared_task
  54. def autocrop(input_pto, output_pto):
  55. subprocess.call(["pano_modify", "--canvas=AUTO", "--crop=AUTO", "-o", output_pto, input_pto])
  56. return output_pto
  57. @shared_task
  58. def generate_pano(input_pto, dirname, output_image):
  59. project_name = os.path.join(dirname, "pano")
  60. # hugin_executor is only available since hugin 2015.0
  61. # return subprocess.call(["hugin_executor", "--prefix", project_name, input_pto])
  62. makefile_name = os.path.join(dirname, "celery_pano.mk")
  63. subprocess.call(["pto2mk", "-o", makefile_name, "-p", project_name, input_pto])
  64. subprocess.call(["make", "-f", makefile_name])
  65. shutil.move(project_name + ".tif", output_image)
  66. @shared_task
  67. def panorama_pipeline(images, output_image):
  68. """Automatically assemble a panorama, using Hugin in headless mode.
  69. [output_image] is the name of the desired output image. Must be a .tif
  70. image for now.
  71. """
  72. d = tempfile.mkdtemp(prefix='celery_hugin')
  73. pto = lambda filename: os.path.join(d, filename + ".pto")
  74. pipeline = pto_gen.s(pto("pto_gen"), images) | \
  75. cpfind.s(pto("cpfind")) | \
  76. prune_images.s(pto("prune_images")) | \
  77. cpclean.s(pto("cpclean")) | \
  78. linefind.s(pto("linefind")) | \
  79. autooptimiser.s(pto("autooptimiser")) | \
  80. autocrop.s(pto("autocrop")) | \
  81. generate_pano.s(d, output_image)
  82. return pipeline()