MpoImagePlugin.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # MPO file handling
  6. #
  7. # See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the
  8. # Camera & Imaging Products Association)
  9. #
  10. # The multi-picture object combines multiple JPEG images (with a modified EXIF
  11. # data format) into a single file. While it can theoretically be used much like
  12. # a GIF animation, it is commonly used to represent 3D photographs and is (as
  13. # of this writing) the most commonly used format by 3D cameras.
  14. #
  15. # History:
  16. # 2014-03-13 Feneric Created
  17. #
  18. # See the README file for information on usage and redistribution.
  19. #
  20. from . import Image, ImageFile, JpegImagePlugin
  21. from ._binary import i16be as i16
  22. # __version__ is deprecated and will be removed in a future version. Use
  23. # PIL.__version__ instead.
  24. __version__ = "0.1"
  25. def _accept(prefix):
  26. return JpegImagePlugin._accept(prefix)
  27. def _save(im, fp, filename):
  28. # Note that we can only save the current frame at present
  29. return JpegImagePlugin._save(im, fp, filename)
  30. ##
  31. # Image plugin for MPO images.
  32. class MpoImageFile(JpegImagePlugin.JpegImageFile):
  33. format = "MPO"
  34. format_description = "MPO (CIPA DC-007)"
  35. _close_exclusive_fp_after_loading = False
  36. def _open(self):
  37. self.fp.seek(0) # prep the fp in order to pass the JPEG test
  38. JpegImagePlugin.JpegImageFile._open(self)
  39. self._after_jpeg_open()
  40. def _after_jpeg_open(self, mpheader=None):
  41. self.mpinfo = mpheader if mpheader is not None else self._getmp()
  42. self.__framecount = self.mpinfo[0xB001]
  43. self.__mpoffsets = [
  44. mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002]
  45. ]
  46. self.__mpoffsets[0] = 0
  47. # Note that the following assertion will only be invalid if something
  48. # gets broken within JpegImagePlugin.
  49. assert self.__framecount == len(self.__mpoffsets)
  50. del self.info["mpoffset"] # no longer needed
  51. self.__fp = self.fp # FIXME: hack
  52. self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame
  53. self.__frame = 0
  54. self.offset = 0
  55. # for now we can only handle reading and individual frame extraction
  56. self.readonly = 1
  57. def load_seek(self, pos):
  58. self.__fp.seek(pos)
  59. @property
  60. def n_frames(self):
  61. return self.__framecount
  62. @property
  63. def is_animated(self):
  64. return self.__framecount > 1
  65. def seek(self, frame):
  66. if not self._seek_check(frame):
  67. return
  68. self.fp = self.__fp
  69. self.offset = self.__mpoffsets[frame]
  70. self.fp.seek(self.offset + 2) # skip SOI marker
  71. if i16(self.fp.read(2)) == 0xFFE1: # APP1
  72. n = i16(self.fp.read(2)) - 2
  73. self.info["exif"] = ImageFile._safe_read(self.fp, n)
  74. exif = self.getexif()
  75. if 40962 in exif and 40963 in exif:
  76. self._size = (exif[40962], exif[40963])
  77. elif "exif" in self.info:
  78. del self.info["exif"]
  79. self.tile = [("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))]
  80. self.__frame = frame
  81. def tell(self):
  82. return self.__frame
  83. def _close__fp(self):
  84. try:
  85. if self.__fp != self.fp:
  86. self.__fp.close()
  87. except AttributeError:
  88. pass
  89. finally:
  90. self.__fp = None
  91. @staticmethod
  92. def adopt(jpeg_instance, mpheader=None):
  93. """
  94. Transform the instance of JpegImageFile into
  95. an instance of MpoImageFile.
  96. After the call, the JpegImageFile is extended
  97. to be an MpoImageFile.
  98. This is essentially useful when opening a JPEG
  99. file that reveals itself as an MPO, to avoid
  100. double call to _open.
  101. """
  102. jpeg_instance.__class__ = MpoImageFile
  103. jpeg_instance._after_jpeg_open(mpheader)
  104. return jpeg_instance
  105. # ---------------------------------------------------------------------
  106. # Registry stuff
  107. # Note that since MPO shares a factory with JPEG, we do not need to do a
  108. # separate registration for it here.
  109. # Image.register_open(MpoImageFile.format,
  110. # JpegImagePlugin.jpeg_factory, _accept)
  111. Image.register_save(MpoImageFile.format, _save)
  112. Image.register_extension(MpoImageFile.format, ".mpo")
  113. Image.register_mime(MpoImageFile.format, "image/mpo")