Refactoring. #206

This commit is contained in:
jevans 2014-03-26 21:04:28 -04:00
commit 6815a46902

View file

@ -18,7 +18,6 @@ else:
from collections import Counter from collections import Counter
import ctypes import ctypes
import itertools
import math import math
import os import os
import re import re
@ -30,7 +29,7 @@ import numpy as np
from .codestream import Codestream from .codestream import Codestream
from .core import SRGB, GREYSCALE from .core import SRGB, GREYSCALE
from .core import PROGRESSION_ORDER, RSIZ, CINEMA_MODE from .core import PROGRESSION_ORDER, CINEMA_MODE
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
from .jp2box import Jp2kBox from .jp2box import Jp2kBox
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
@ -639,6 +638,74 @@ class Jp2k(Jp2kBox):
>>> jp2 = j2k.wrap(tfile.name) >>> jp2 = j2k.wrap(tfile.name)
""" """
if boxes is None: if boxes is None:
boxes = self._get_default_jp2_boxes()
_validate_jp2_box_sequence(boxes)
with open(filename, 'wb') as ofile:
for box in boxes:
if box.box_id != 'jp2c':
box.write(ofile)
else:
self._write_wrapped_codestream(ofile, box)
ofile.flush()
jp2 = Jp2k(filename)
return jp2
def _write_wrapped_codestream(self, ofile, box):
"""Write wrapped codestream."""
# Codestreams require a bit more care.
# Am I a raw codestream?
if len(self.box) == 0:
# Yes, just write the codestream box header plus all
# of myself out to file.
ofile.write(struct.pack('>I', self.length + 8))
ofile.write(b'jp2c')
with open(self.filename, 'rb') as ifile:
ofile.write(ifile.read())
return
# OK, I'm a jp2/jpx file. Need to find out where the raw codestream
# actually starts.
offset = box.offset
if offset == -1:
if self.box[1].brand == 'jpx ':
msg = "The codestream box must have its offset and "
msg += "length attributes fully specified if the file "
msg += "type brand is JPX."
raise IOError(msg)
# Find the first codestream in the file.
jp2c = [box for box in self.box if box.box_id == 'jp2c']
offset = jp2c[0].offset
# Ready to write the codestream.
with open(self.filename, 'rb') as ifile:
ifile.seek(offset)
# Verify that the specified codestream is right.
read_buffer = ifile.read(8)
L, T = struct.unpack_from('>I4s', read_buffer, 0)
if T != b'jp2c':
msg = "Unable to locate the specified codestream."
raise IOError(msg)
if L == 0:
# The length of the box is presumed to last until the end of
# the file. Compute the effective length of the box.
L = os.path.getsize(ifile.name) - ifile.tell() + 8
elif L == 1:
# The length of the box is in the XL field, a 64-bit value.
read_buffer = ifile.read(8)
L, = struct.unpack('>Q', read_buffer)
ifile.seek(offset)
read_buffer = ifile.read(L)
ofile.write(read_buffer)
def _get_default_jp2_boxes(self):
"""Create a default set of JP2 boxes."""
# Try to create a reasonable default. # Try to create a reasonable default.
boxes = [JPEG2000SignatureBox(), boxes = [JPEG2000SignatureBox(),
FileTypeBox(), FileTypeBox(),
@ -660,73 +727,11 @@ class Jp2k(Jp2kBox):
jp2hs = [box for box in self.box if box.box_id == 'jp2h'] jp2hs = [box for box in self.box if box.box_id == 'jp2h']
colorspace = jp2hs[0].box[1].colorspace colorspace = jp2hs[0].box[1].colorspace
boxes[2].box = [ImageHeaderBox(height=height, boxes[2].box = [ImageHeaderBox(height=height, width=width,
width=width,
num_components=num_components), num_components=num_components),
ColourSpecificationBox(colorspace=colorspace)] ColourSpecificationBox(colorspace=colorspace)]
_validate_jp2_box_sequence(boxes) return boxes
with open(filename, 'wb') as ofile:
for box in boxes:
if box.box_id != 'jp2c':
box.write(ofile)
else:
# Codestreams require a bit more care.
if len(self.box) == 0:
# Am I a raw codestream? If so, then it is pretty
# easy, just write the codestream box header plus all
# of myself out to file.
ofile.write(struct.pack('>I', self.length + 8))
ofile.write(b'jp2c')
with open(self.filename, 'rb') as ifile:
ofile.write(ifile.read())
else:
# OK, I'm a jp2/jpx file. Need to find out where the
# raw codestream actually starts.
offset = box.offset
length = box.length
if offset == -1:
if self.box[1].brand == 'jpx ':
msg = "The codestream box must have its offset "
msg += "and length attributes fully specified "
msg += "if the file type brand is JPX."
raise IOError(msg)
# Find the first codestream in the file.
jp2c = [box for box in self.box
if box.box_id == 'jp2c']
offset = jp2c[0].offset
length = jp2c[0].length
# Verify that the specified codestream is right.
with open(self.filename, 'rb') as ifile:
ifile.seek(offset)
read_buffer = ifile.read(8)
L, T = struct.unpack_from('>I4s', read_buffer, 0)
if T != b'jp2c':
msg = "Unable to locate the specified codestream."
raise IOError(msg)
if L == 0:
# The length of the box is presumed to last
# until the end of the file. Compute the
# effective length of the box.
L = os.path.getsize(ifile.name) - fptr.tell() + 8
elif L == 1:
# The length of the box is in the XL field, a
# 64-bit value.
read_buffer = ifile.read(8)
L, = struct.unpack('>Q', read_buffer)
ifile.seek(offset)
read_buffer = ifile.read(L)
ofile.write(read_buffer)
ofile.flush()
jp2 = Jp2k(filename)
return jp2
def read(self, **kwargs): def read(self, **kwargs):
"""Read a JPEG 2000 image. """Read a JPEG 2000 image.
@ -801,7 +806,8 @@ class Jp2k(Jp2kBox):
msg += "the read_bands method instead." msg += "the read_bands method instead."
raise RuntimeError(msg) raise RuntimeError(msg)
def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False, verbose=False): def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False,
verbose=False):
"""Read a JPEG 2000 image using libopenjpeg. """Read a JPEG 2000 image using libopenjpeg.
Parameters Parameters
@ -970,7 +976,8 @@ class Jp2k(Jp2kBox):
return img_array return img_array
def _populate_dparam(self, layer, rlevel, area, tile, ignore_pclr_cmap_cdef): def _populate_dparam(self, layer, rlevel, area, tile,
ignore_pclr_cmap_cdef):
"""Populate decompression structure with appropriate input parameters. """Populate decompression structure with appropriate input parameters.
Parameters Parameters
@ -1244,11 +1251,11 @@ def _validate_jp2_box_sequence(boxes):
_validate_jpx_box_sequence(boxes) _validate_jpx_box_sequence(boxes)
else: else:
count = _collect_box_count(boxes) count = _collect_box_count(boxes)
for id in count.keys(): for box_id in count.keys():
if id not in JP2_IDS: if box_id not in JP2_IDS:
msg = "The presence of a '{0}' box requires that the file type " msg = "The presence of a '{0}' box requires that the file type "
msg += "brand be set to 'jpx '." msg += "brand be set to 'jpx '."
raise IOError(msg.format(id)) raise IOError(msg.format(box_id))
def _validate_jpx_box_sequence(boxes): def _validate_jpx_box_sequence(boxes):
"""Run through series of tests for JPX box legality.""" """Run through series of tests for JPX box legality."""