Jpeg2KImagePlugin.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #
  2. # The Python Imaging Library
  3. # $Id$
  4. #
  5. # JPEG2000 file handling
  6. #
  7. # History:
  8. # 2014-03-12 ajh Created
  9. #
  10. # Copyright (c) 2014 Coriolis Systems Limited
  11. # Copyright (c) 2014 Alastair Houghton
  12. #
  13. # See the README file for information on usage and redistribution.
  14. #
  15. import io
  16. import os
  17. import struct
  18. from . import Image, ImageFile
  19. # __version__ is deprecated and will be removed in a future version. Use
  20. # PIL.__version__ instead.
  21. __version__ = "0.1"
  22. def _parse_codestream(fp):
  23. """Parse the JPEG 2000 codestream to extract the size and component
  24. count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
  25. hdr = fp.read(2)
  26. lsiz = struct.unpack(">H", hdr)[0]
  27. siz = hdr + fp.read(lsiz - 2)
  28. lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from(
  29. ">HHIIIIIIIIH", siz
  30. )
  31. ssiz = [None] * csiz
  32. xrsiz = [None] * csiz
  33. yrsiz = [None] * csiz
  34. for i in range(csiz):
  35. ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 36 + 3 * i)
  36. size = (xsiz - xosiz, ysiz - yosiz)
  37. if csiz == 1:
  38. if (yrsiz[0] & 0x7F) > 8:
  39. mode = "I;16"
  40. else:
  41. mode = "L"
  42. elif csiz == 2:
  43. mode = "LA"
  44. elif csiz == 3:
  45. mode = "RGB"
  46. elif csiz == 4:
  47. mode = "RGBA"
  48. else:
  49. mode = None
  50. return (size, mode)
  51. def _parse_jp2_header(fp):
  52. """Parse the JP2 header box to extract size, component count and
  53. color space information, returning a (size, mode, mimetype) tuple."""
  54. # Find the JP2 header box
  55. header = None
  56. mimetype = None
  57. while True:
  58. lbox, tbox = struct.unpack(">I4s", fp.read(8))
  59. if lbox == 1:
  60. lbox = struct.unpack(">Q", fp.read(8))[0]
  61. hlen = 16
  62. else:
  63. hlen = 8
  64. if lbox < hlen:
  65. raise SyntaxError("Invalid JP2 header length")
  66. if tbox == b"jp2h":
  67. header = fp.read(lbox - hlen)
  68. break
  69. elif tbox == b"ftyp":
  70. if fp.read(4) == b"jpx ":
  71. mimetype = "image/jpx"
  72. fp.seek(lbox - hlen - 4, os.SEEK_CUR)
  73. else:
  74. fp.seek(lbox - hlen, os.SEEK_CUR)
  75. if header is None:
  76. raise SyntaxError("could not find JP2 header")
  77. size = None
  78. mode = None
  79. bpc = None
  80. nc = None
  81. hio = io.BytesIO(header)
  82. while True:
  83. lbox, tbox = struct.unpack(">I4s", hio.read(8))
  84. if lbox == 1:
  85. lbox = struct.unpack(">Q", hio.read(8))[0]
  86. hlen = 16
  87. else:
  88. hlen = 8
  89. content = hio.read(lbox - hlen)
  90. if tbox == b"ihdr":
  91. height, width, nc, bpc, c, unkc, ipr = struct.unpack(">IIHBBBB", content)
  92. size = (width, height)
  93. if unkc:
  94. if nc == 1 and (bpc & 0x7F) > 8:
  95. mode = "I;16"
  96. elif nc == 1:
  97. mode = "L"
  98. elif nc == 2:
  99. mode = "LA"
  100. elif nc == 3:
  101. mode = "RGB"
  102. elif nc == 4:
  103. mode = "RGBA"
  104. break
  105. elif tbox == b"colr":
  106. meth, prec, approx = struct.unpack_from(">BBB", content)
  107. if meth == 1:
  108. cs = struct.unpack_from(">I", content, 3)[0]
  109. if cs == 16: # sRGB
  110. if nc == 1 and (bpc & 0x7F) > 8:
  111. mode = "I;16"
  112. elif nc == 1:
  113. mode = "L"
  114. elif nc == 3:
  115. mode = "RGB"
  116. elif nc == 4:
  117. mode = "RGBA"
  118. break
  119. elif cs == 17: # grayscale
  120. if nc == 1 and (bpc & 0x7F) > 8:
  121. mode = "I;16"
  122. elif nc == 1:
  123. mode = "L"
  124. elif nc == 2:
  125. mode = "LA"
  126. break
  127. elif cs == 18: # sYCC
  128. if nc == 3:
  129. mode = "RGB"
  130. elif nc == 4:
  131. mode = "RGBA"
  132. break
  133. if size is None or mode is None:
  134. raise SyntaxError("Malformed jp2 header")
  135. return (size, mode, mimetype)
  136. ##
  137. # Image plugin for JPEG2000 images.
  138. class Jpeg2KImageFile(ImageFile.ImageFile):
  139. format = "JPEG2000"
  140. format_description = "JPEG 2000 (ISO 15444)"
  141. def _open(self):
  142. sig = self.fp.read(4)
  143. if sig == b"\xff\x4f\xff\x51":
  144. self.codec = "j2k"
  145. self._size, self.mode = _parse_codestream(self.fp)
  146. else:
  147. sig = sig + self.fp.read(8)
  148. if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a":
  149. self.codec = "jp2"
  150. header = _parse_jp2_header(self.fp)
  151. self._size, self.mode, self.custom_mimetype = header
  152. else:
  153. raise SyntaxError("not a JPEG 2000 file")
  154. if self.size is None or self.mode is None:
  155. raise SyntaxError("unable to determine size/mode")
  156. self.reduce = 0
  157. self.layers = 0
  158. fd = -1
  159. length = -1
  160. try:
  161. fd = self.fp.fileno()
  162. length = os.fstat(fd).st_size
  163. except Exception:
  164. fd = -1
  165. try:
  166. pos = self.fp.tell()
  167. self.fp.seek(0, io.SEEK_END)
  168. length = self.fp.tell()
  169. self.fp.seek(pos)
  170. except Exception:
  171. length = -1
  172. self.tile = [
  173. (
  174. "jpeg2k",
  175. (0, 0) + self.size,
  176. 0,
  177. (self.codec, self.reduce, self.layers, fd, length),
  178. )
  179. ]
  180. def load(self):
  181. if self.reduce:
  182. power = 1 << self.reduce
  183. adjust = power >> 1
  184. self._size = (
  185. int((self.size[0] + adjust) / power),
  186. int((self.size[1] + adjust) / power),
  187. )
  188. if self.tile:
  189. # Update the reduce and layers settings
  190. t = self.tile[0]
  191. t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
  192. self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
  193. return ImageFile.ImageFile.load(self)
  194. def _accept(prefix):
  195. return (
  196. prefix[:4] == b"\xff\x4f\xff\x51"
  197. or prefix[:12] == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a"
  198. )
  199. # ------------------------------------------------------------
  200. # Save support
  201. def _save(im, fp, filename):
  202. if filename.endswith(".j2k"):
  203. kind = "j2k"
  204. else:
  205. kind = "jp2"
  206. # Get the keyword arguments
  207. info = im.encoderinfo
  208. offset = info.get("offset", None)
  209. tile_offset = info.get("tile_offset", None)
  210. tile_size = info.get("tile_size", None)
  211. quality_mode = info.get("quality_mode", "rates")
  212. quality_layers = info.get("quality_layers", None)
  213. if quality_layers is not None and not (
  214. isinstance(quality_layers, (list, tuple))
  215. and all(
  216. [
  217. isinstance(quality_layer, (int, float))
  218. for quality_layer in quality_layers
  219. ]
  220. )
  221. ):
  222. raise ValueError("quality_layers must be a sequence of numbers")
  223. num_resolutions = info.get("num_resolutions", 0)
  224. cblk_size = info.get("codeblock_size", None)
  225. precinct_size = info.get("precinct_size", None)
  226. irreversible = info.get("irreversible", False)
  227. progression = info.get("progression", "LRCP")
  228. cinema_mode = info.get("cinema_mode", "no")
  229. fd = -1
  230. if hasattr(fp, "fileno"):
  231. try:
  232. fd = fp.fileno()
  233. except Exception:
  234. fd = -1
  235. im.encoderconfig = (
  236. offset,
  237. tile_offset,
  238. tile_size,
  239. quality_mode,
  240. quality_layers,
  241. num_resolutions,
  242. cblk_size,
  243. precinct_size,
  244. irreversible,
  245. progression,
  246. cinema_mode,
  247. fd,
  248. )
  249. ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)])
  250. # ------------------------------------------------------------
  251. # Registry stuff
  252. Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept)
  253. Image.register_save(Jpeg2KImageFile.format, _save)
  254. Image.register_extensions(
  255. Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]
  256. )
  257. Image.register_mime(Jpeg2KImageFile.format, "image/jp2")