refactored private methods used by Jp2k write method, closes #269
This commit is contained in:
parent
2cbc0f3d1b
commit
edde7d36b1
1 changed files with 148 additions and 209 deletions
357
glymur/jp2k.py
357
glymur/jp2k.py
|
|
@ -45,6 +45,11 @@ JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
|
||||||
'uuid']
|
'uuid']
|
||||||
JPX_IDS = ['asoc', 'nlst']
|
JPX_IDS = ['asoc', 'nlst']
|
||||||
|
|
||||||
|
_COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
|
||||||
|
'gray': opj2.CLRSPC_GRAY,
|
||||||
|
'grey': opj2.CLRSPC_GRAY,
|
||||||
|
'ycc': opj2.CLRSPC_YCC}
|
||||||
|
|
||||||
class Jp2k(Jp2kBox):
|
class Jp2k(Jp2kBox):
|
||||||
"""JPEG 2000 file.
|
"""JPEG 2000 file.
|
||||||
|
|
||||||
|
|
@ -74,6 +79,7 @@ class Jp2k(Jp2kBox):
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.box = []
|
self.box = []
|
||||||
self._codec_format = None
|
self._codec_format = None
|
||||||
|
self._colorspace = None
|
||||||
|
|
||||||
# Parse the file for JP2/JPX contents only if we are reading it.
|
# Parse the file for JP2/JPX contents only if we are reading it.
|
||||||
if mode == 'rb':
|
if mode == 'rb':
|
||||||
|
|
@ -167,11 +173,14 @@ class Jp2k(Jp2kBox):
|
||||||
fps : int
|
fps : int
|
||||||
Frames per second, should be either 24 or 48.
|
Frames per second, should be either 24 or 48.
|
||||||
"""
|
"""
|
||||||
if re.match("(1.5|2.0.0)", version.openjpeg_version) is not None:
|
if re.match("1.5|2.0.0", version.openjpeg_version) is not None:
|
||||||
msg = "Writing Cinema2K or Cinema4K files is not supported with "
|
msg = "Writing Cinema2K or Cinema4K files is not supported with "
|
||||||
msg += 'openjpeg library versions less than 2.0.1.'
|
msg += 'openjpeg library versions less than 2.0.1.'
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
# Cinema modes imply MCT.
|
||||||
|
cparams.tcp_mct = 1
|
||||||
|
|
||||||
if cinema_mode == 'cinema2k':
|
if cinema_mode == 'cinema2k':
|
||||||
if fps not in [24, 48]:
|
if fps not in [24, 48]:
|
||||||
raise IOError('Cinema2K frame rate must be either 24 or 48.')
|
raise IOError('Cinema2K frame rate must be either 24 or 48.')
|
||||||
|
|
@ -204,52 +213,30 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _populate_cparams(self, **kwargs):
|
def _populate_cparams(self, img_array, **kwargs):
|
||||||
"""Populate compression parameters structure from input arguments.
|
"""Directs processing of write method arguments.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
cbsize : tuple, optional
|
img_array : ndarray
|
||||||
Code block size (DY, DX).
|
image data to be written to file
|
||||||
cratios : iterable
|
kwargs : dictionary
|
||||||
Compression ratios for successive layers.
|
non-image keyword inputs provided to write method
|
||||||
eph : bool, optional
|
|
||||||
If true, write SOP marker after each header packet.
|
|
||||||
grid_offset : tuple, optional
|
|
||||||
Offset (DY, DX) of the origin of the image in the reference grid.
|
|
||||||
mct : bool, optional
|
|
||||||
Specifies usage of the multi component transform. If not
|
|
||||||
specified, defaults to True if the colorspace is RGB.
|
|
||||||
modesw : int, optional
|
|
||||||
Mode switch.
|
|
||||||
1 = BYPASS(LAZY)
|
|
||||||
2 = RESET
|
|
||||||
4 = RESTART(TERMALL)
|
|
||||||
8 = VSC
|
|
||||||
16 = ERTERM(SEGTERM)
|
|
||||||
32 = SEGMARK(SEGSYM)
|
|
||||||
numres : int, optional
|
|
||||||
Number of resolutions.
|
|
||||||
prog : str, optional
|
|
||||||
Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL".
|
|
||||||
psnr : iterable, optional
|
|
||||||
Different PSNR for successive layers.
|
|
||||||
psizes : list, optional
|
|
||||||
List of precinct sizes. Each precinct size tuple is defined in
|
|
||||||
(height x width).
|
|
||||||
sop : bool, optional
|
|
||||||
If true, write SOP marker before each packet.
|
|
||||||
subsam : tuple, optional
|
|
||||||
Subsampling factors (dy, dx).
|
|
||||||
tilesize : tuple, optional
|
|
||||||
Numeric tuple specifying tile size in terms of (numrows, numcols),
|
|
||||||
not (X, Y).
|
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
cparams : CompressionParametersType(ctypes.Structure)
|
cparams : CompressionParametersType(ctypes.Structure)
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
corresponds to cparameters_t openjpeg datatype
|
||||||
"""
|
"""
|
||||||
|
if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and
|
||||||
|
(len(set(kwargs)) > 1)):
|
||||||
|
msg = "Cannot specify cinema2k/cinema4k along with other options."
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
if 'cratios' in kwargs and 'psnr' in kwargs:
|
||||||
|
msg = "Cannot specify cratios and psnr together."
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
if version.openjpeg_version_tuple[0] == 1:
|
if version.openjpeg_version_tuple[0] == 1:
|
||||||
cparams = opj.set_default_encoder_parameters()
|
cparams = opj.set_default_encoder_parameters()
|
||||||
else:
|
else:
|
||||||
|
|
@ -336,49 +323,9 @@ class Jp2k(Jp2kBox):
|
||||||
cparams.cp_tdy = kwargs['tilesize'][0]
|
cparams.cp_tdy = kwargs['tilesize'][0]
|
||||||
cparams.tile_size_on = opj2.TRUE
|
cparams.tile_size_on = opj2.TRUE
|
||||||
|
|
||||||
return cparams
|
|
||||||
|
|
||||||
def _process_write_inputs(self, img_array, colorspace=None, **kwargs):
|
|
||||||
"""Directs processing of write method arguments.
|
|
||||||
|
|
||||||
It's somewhat awkward to process all the kwargs arguments at once.
|
|
||||||
The "colorspace" is not a parameter that gets processed into the
|
|
||||||
compression parameters structure, and it unfortunately must be handled
|
|
||||||
in the middle of the compression parameter processing.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
img_array : ndarray
|
|
||||||
Image data to be written to file.
|
|
||||||
colorspace : str, optional
|
|
||||||
Either 'rgb' or 'gray'.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
cparams : CompressionParametersType(ctypes.Structure)
|
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
|
||||||
colorspace : int
|
|
||||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
|
||||||
"""
|
|
||||||
if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and
|
|
||||||
(len(set(kwargs)) > 1)):
|
|
||||||
msg = "Cannot specify cinema2k/cinema4k along with other options."
|
|
||||||
raise IOError(msg)
|
|
||||||
|
|
||||||
if 'cratios' in kwargs and 'psnr' in kwargs:
|
|
||||||
msg = "Cannot specify cratios and psnr together."
|
|
||||||
raise IOError(msg)
|
|
||||||
|
|
||||||
cparams = self._populate_cparams(**kwargs)
|
|
||||||
_validate_compression_params(img_array, cparams)
|
|
||||||
|
|
||||||
colorspace = _unpack_colorspace(colorspace, img_array, cparams)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mct = kwargs['mct']
|
mct = kwargs['mct']
|
||||||
if mct and colorspace == opj2.CLRSPC_GRAY:
|
if mct and self._colorspace == opj2.CLRSPC_GRAY:
|
||||||
# Cannot check for this in the validate routine, as we need
|
|
||||||
# to know what the target colorspace has been determined to be.
|
|
||||||
msg = "Cannot specify usage of the multi component transform "
|
msg = "Cannot specify usage of the multi component transform "
|
||||||
msg += "if the colorspace is gray."
|
msg += "if the colorspace is gray."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
@ -386,12 +333,14 @@ class Jp2k(Jp2kBox):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If the multi component transform was not specified, we infer
|
# If the multi component transform was not specified, we infer
|
||||||
# that it should be used if the color space is RGB.
|
# that it should be used if the color space is RGB.
|
||||||
if colorspace == opj2.CLRSPC_SRGB:
|
if self._colorspace == opj2.CLRSPC_SRGB:
|
||||||
cparams.tcp_mct = 1
|
cparams.tcp_mct = 1
|
||||||
else:
|
else:
|
||||||
cparams.tcp_mct = 0
|
cparams.tcp_mct = 0
|
||||||
|
|
||||||
return cparams, colorspace
|
self._validate_compression_params(img_array, cparams, **kwargs)
|
||||||
|
|
||||||
|
return cparams
|
||||||
|
|
||||||
def write(self, img_array, verbose=False, **kwargs):
|
def write(self, img_array, verbose=False, **kwargs):
|
||||||
"""Write image data to a JP2/JPX/J2k file. Intended usage of the
|
"""Write image data to a JP2/JPX/J2k file. Intended usage of the
|
||||||
|
|
@ -466,21 +415,23 @@ class Jp2k(Jp2kBox):
|
||||||
glymur.LibraryNotFoundError
|
glymur.LibraryNotFoundError
|
||||||
If glymur is unable to load the openjp2 library.
|
If glymur is unable to load the openjp2 library.
|
||||||
"""
|
"""
|
||||||
if opj2.OPENJP2 is not None:
|
if opj2.OPENJP2 is None and opj.OPENJPEG is None:
|
||||||
self._write_openjp2(img_array, verbose=verbose, **kwargs)
|
|
||||||
elif opj.OPENJPEG is not None:
|
|
||||||
self._write_openjpeg(img_array, verbose=verbose, **kwargs)
|
|
||||||
else:
|
|
||||||
raise LibraryNotFoundError("You must have at least version 1.5 of "
|
raise LibraryNotFoundError("You must have at least version 1.5 of "
|
||||||
"OpenJPEG before using this "
|
"OpenJPEG before using this "
|
||||||
"functionality.")
|
"functionality.")
|
||||||
|
|
||||||
def _write_openjpeg(self, img_array, verbose=False, **kwargs):
|
self._determine_colorspace(img_array, **kwargs)
|
||||||
|
cparams = self._populate_cparams(img_array, **kwargs)
|
||||||
|
|
||||||
|
if opj2.OPENJP2 is not None:
|
||||||
|
self._write_openjp2(img_array, cparams, verbose=verbose)
|
||||||
|
else:
|
||||||
|
self._write_openjpeg(img_array, cparams, verbose=verbose)
|
||||||
|
|
||||||
|
def _write_openjpeg(self, img_array, cparams, verbose=False):
|
||||||
"""
|
"""
|
||||||
Write JPEG 2000 file using OpenJPEG 1.5 interface.
|
Write JPEG 2000 file using OpenJPEG 1.5 interface.
|
||||||
"""
|
"""
|
||||||
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
|
||||||
|
|
||||||
if img_array.ndim == 2:
|
if img_array.ndim == 2:
|
||||||
# Force the image to be 3D. Just makes things easier later on.
|
# Force the image to be 3D. Just makes things easier later on.
|
||||||
img_array = img_array.reshape(img_array.shape[0],
|
img_array = img_array.reshape(img_array.shape[0],
|
||||||
|
|
@ -490,7 +441,7 @@ class Jp2k(Jp2kBox):
|
||||||
comptparms = _populate_comptparms(img_array, cparams)
|
comptparms = _populate_comptparms(img_array, cparams)
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
image = opj.image_create(comptparms, colorspace)
|
image = opj.image_create(comptparms, self._colorspace)
|
||||||
stack.callback(opj.image_destroy, image)
|
stack.callback(opj.image_destroy, image)
|
||||||
|
|
||||||
numrows, numcols, numlayers = img_array.shape
|
numrows, numcols, numlayers = img_array.shape
|
||||||
|
|
@ -542,13 +493,114 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
self.parse()
|
self.parse()
|
||||||
|
|
||||||
|
def _validate_compression_params(self, img_array, cparams, **kwargs):
|
||||||
|
"""Check that the compression parameters are valid.
|
||||||
|
|
||||||
def _write_openjp2(self, img_array, verbose=False, **kwargs):
|
Parameters
|
||||||
|
----------
|
||||||
|
img_array : ndarray
|
||||||
|
Image data to be written to file.
|
||||||
|
cparams : CompressionParametersType(ctypes.Structure)
|
||||||
|
Corresponds to cparameters_t type in openjp2 headers.
|
||||||
"""
|
"""
|
||||||
Write JPEG 2000 file using OpenJPEG 2.0 interface.
|
# Cannot specify a colorspace with J2K.
|
||||||
"""
|
if cparams.codec_fmt == opj2.CODEC_J2K and 'colorspace' in kwargs:
|
||||||
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
msg = 'Do not specify a colorspace when writing a raw '
|
||||||
|
msg += 'codestream.'
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
# Code block size
|
||||||
|
code_block_specified = False
|
||||||
|
if cparams.cblockw_init != 0 and cparams.cblockh_init != 0:
|
||||||
|
# These fields ARE zero if uninitialized.
|
||||||
|
width = cparams.cblockw_init
|
||||||
|
height = cparams.cblockh_init
|
||||||
|
code_block_specified = True
|
||||||
|
if height * width > 4096 or height < 4 or width < 4:
|
||||||
|
msg = "Code block area cannot exceed 4096. "
|
||||||
|
msg += "Code block height and width must be larger than 4."
|
||||||
|
raise IOError(msg)
|
||||||
|
if ((math.log(height, 2) != math.floor(math.log(height, 2)) or
|
||||||
|
math.log(width, 2) != math.floor(math.log(width, 2)))):
|
||||||
|
msg = "Bad code block size ({0}, {1}), "
|
||||||
|
msg += "must be powers of 2."
|
||||||
|
raise IOError(msg.format(height, width))
|
||||||
|
|
||||||
|
# Precinct size
|
||||||
|
if cparams.res_spec != 0:
|
||||||
|
# precinct size was not specified if this field is zero.
|
||||||
|
for j in range(cparams.res_spec):
|
||||||
|
prch = cparams.prch_init[j]
|
||||||
|
prcw = cparams.prcw_init[j]
|
||||||
|
if j == 0 and code_block_specified:
|
||||||
|
height, width = cparams.cblockh_init, cparams.cblockw_init
|
||||||
|
if height * 2 > prch or width * 2 > prcw:
|
||||||
|
msg = "Highest Resolution precinct size must be at "
|
||||||
|
msg += "least twice that of the code block dimensions."
|
||||||
|
raise IOError(msg)
|
||||||
|
if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or
|
||||||
|
math.log(prcw, 2) != math.floor(math.log(prcw, 2)))):
|
||||||
|
msg = "Bad precinct sizes ({0}, {1}), "
|
||||||
|
msg += "must be powers of 2."
|
||||||
|
raise IOError(msg.format(prch, prcw))
|
||||||
|
|
||||||
|
# What would the point of 1D images be?
|
||||||
|
if img_array.ndim == 1 or img_array.ndim > 3:
|
||||||
|
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
if re.match("2.0.0", version.openjpeg_version) is not None:
|
||||||
|
if (((img_array.ndim != 2) and
|
||||||
|
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
||||||
|
msg = "Writing images is restricted to single-channel "
|
||||||
|
msg += "greyscale images or three-channel RGB images when "
|
||||||
|
msg += "the OpenJPEG library version is the official 2.0.0 "
|
||||||
|
msg += "release."
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
|
||||||
|
msg = "Only uint8 and uint16 images are currently supported."
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
def _determine_colorspace(self, img_array, colorspace=None, **kwargs):
|
||||||
|
"""Determine the colorspace from the supplied inputs.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
img_array : ndarray
|
||||||
|
Image data to be written to file.
|
||||||
|
colorspace : str, optional
|
||||||
|
Either 'rgb' or 'gray'.
|
||||||
|
"""
|
||||||
|
if colorspace is None:
|
||||||
|
# Must infer the colorspace from the image dimensions.
|
||||||
|
if img_array.ndim < 3:
|
||||||
|
# A single channel image is grayscale.
|
||||||
|
self._colorspace = opj2.CLRSPC_GRAY
|
||||||
|
elif img_array.shape[2] == 1 or img_array.shape[2] == 2:
|
||||||
|
# A single channel image or an image with two channels is going
|
||||||
|
# to be greyscale.
|
||||||
|
self._colorspace = opj2.CLRSPC_GRAY
|
||||||
|
else:
|
||||||
|
# Anything else must be RGB, right?
|
||||||
|
self._colorspace = opj2.CLRSPC_SRGB
|
||||||
|
else:
|
||||||
|
if colorspace.lower() not in ('rgb', 'grey', 'gray'):
|
||||||
|
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
||||||
|
raise IOError(msg)
|
||||||
|
elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3:
|
||||||
|
msg = 'RGB colorspace requires at least 3 components.'
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
# Turn the colorspace from a string to the enumerated value that
|
||||||
|
# the library expects.
|
||||||
|
self._colorspace = _COLORSPACE_MAP[colorspace.lower()]
|
||||||
|
|
||||||
|
|
||||||
|
def _write_openjp2(self, img_array, cparams, verbose=False):
|
||||||
|
"""
|
||||||
|
Write JPEG 2000 file using OpenJPEG 2.x interface.
|
||||||
|
"""
|
||||||
if img_array.ndim == 2:
|
if img_array.ndim == 2:
|
||||||
# Force the image to be 3D. Just makes things easier later on.
|
# Force the image to be 3D. Just makes things easier later on.
|
||||||
numrows, numcols = img_array.shape
|
numrows, numcols = img_array.shape
|
||||||
|
|
@ -557,7 +609,7 @@ class Jp2k(Jp2kBox):
|
||||||
comptparms = _populate_comptparms(img_array, cparams)
|
comptparms = _populate_comptparms(img_array, cparams)
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
image = opj2.image_create(comptparms, colorspace)
|
image = opj2.image_create(comptparms, self._colorspace)
|
||||||
stack.callback(opj2.image_destroy, image)
|
stack.callback(opj2.image_destroy, image)
|
||||||
|
|
||||||
_populate_image_struct(cparams, image, img_array)
|
_populate_image_struct(cparams, image, img_array)
|
||||||
|
|
@ -1674,50 +1726,6 @@ def extract_image_bands(image):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def _unpack_colorspace(colorspace, img_array, cparams):
|
|
||||||
"""Determine the colorspace from the supplied inputs.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
colorspace : int
|
|
||||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
|
||||||
img_array : ndarray
|
|
||||||
Image data to be written to file.
|
|
||||||
cparams : CompressionParametersType(ctypes.Structure)
|
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
|
||||||
"""
|
|
||||||
if colorspace is None:
|
|
||||||
# Must infer the colorspace from the image dimensions.
|
|
||||||
if img_array.ndim < 3:
|
|
||||||
# A single channel image is grayscale.
|
|
||||||
colorspace = opj2.CLRSPC_GRAY
|
|
||||||
elif img_array.shape[2] == 1 or img_array.shape[2] == 2:
|
|
||||||
# A single channel image or an image with two channels is going
|
|
||||||
# to be greyscale.
|
|
||||||
colorspace = opj2.CLRSPC_GRAY
|
|
||||||
else:
|
|
||||||
# Anything else must be RGB, right?
|
|
||||||
colorspace = opj2.CLRSPC_SRGB
|
|
||||||
else:
|
|
||||||
# Supplied a string colorspace, so we must validate it.
|
|
||||||
if cparams.codec_fmt == opj2.CODEC_J2K:
|
|
||||||
msg = 'Do not specify a colorspace when writing a raw '
|
|
||||||
msg += 'codestream.'
|
|
||||||
raise IOError(msg)
|
|
||||||
if colorspace.lower() not in ('rgb', 'grey', 'gray'):
|
|
||||||
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
|
||||||
raise IOError(msg)
|
|
||||||
elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3:
|
|
||||||
msg = 'RGB colorspace requires at least 3 components.'
|
|
||||||
raise IOError(msg)
|
|
||||||
|
|
||||||
# Turn the colorspace from a string to the enumerated value that
|
|
||||||
# the library expects.
|
|
||||||
colorspace = _COLORSPACE_MAP[colorspace.lower()]
|
|
||||||
|
|
||||||
return colorspace
|
|
||||||
|
|
||||||
|
|
||||||
def _populate_comptparms(img_array, cparams):
|
def _populate_comptparms(img_array, cparams):
|
||||||
"""Instantiate and populate comptparms structure.
|
"""Instantiate and populate comptparms structure.
|
||||||
|
|
||||||
|
|
@ -1805,75 +1813,6 @@ def _populate_image_struct(cparams, image, imgdata):
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def _validate_compression_params(img_array, cparams):
|
|
||||||
"""Check that the compression parameters are valid.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
img_array : ndarray
|
|
||||||
Image data to be written to file.
|
|
||||||
cparams : CompressionParametersType(ctypes.Structure)
|
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Code block size
|
|
||||||
code_block_specified = False
|
|
||||||
if cparams.cblockw_init != 0 and cparams.cblockh_init != 0:
|
|
||||||
# These fields ARE zero if uninitialized.
|
|
||||||
width = cparams.cblockw_init
|
|
||||||
height = cparams.cblockh_init
|
|
||||||
code_block_specified = True
|
|
||||||
if height * width > 4096 or height < 4 or width < 4:
|
|
||||||
msg = "Code block area cannot exceed 4096. "
|
|
||||||
msg += "Code block height and width must be larger than 4."
|
|
||||||
raise IOError(msg)
|
|
||||||
if ((math.log(height, 2) != math.floor(math.log(height, 2)) or
|
|
||||||
math.log(width, 2) != math.floor(math.log(width, 2)))):
|
|
||||||
msg = "Bad code block size ({0}, {1}), "
|
|
||||||
msg += "must be powers of 2."
|
|
||||||
raise IOError(msg.format(height, width))
|
|
||||||
|
|
||||||
# Precinct size
|
|
||||||
if cparams.res_spec != 0:
|
|
||||||
# precinct size was not specified if this field is zero.
|
|
||||||
for j in range(cparams.res_spec):
|
|
||||||
prch = cparams.prch_init[j]
|
|
||||||
prcw = cparams.prcw_init[j]
|
|
||||||
if j == 0 and code_block_specified:
|
|
||||||
height, width = cparams.cblockh_init, cparams.cblockw_init
|
|
||||||
if height * 2 > prch or width * 2 > prcw:
|
|
||||||
msg = "Highest Resolution precinct size must be at "
|
|
||||||
msg += "least twice that of the code block dimensions."
|
|
||||||
raise IOError(msg)
|
|
||||||
if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or
|
|
||||||
math.log(prcw, 2) != math.floor(math.log(prcw, 2)))):
|
|
||||||
msg = "Bad precinct sizes ({0}, {1}), "
|
|
||||||
msg += "must be powers of 2."
|
|
||||||
raise IOError(msg.format(prch, prcw))
|
|
||||||
|
|
||||||
# What would the point of 1D images be?
|
|
||||||
if img_array.ndim == 1 or img_array.ndim > 3:
|
|
||||||
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
|
||||||
raise IOError(msg)
|
|
||||||
|
|
||||||
if re.match("2.0.0", version.openjpeg_version) is not None:
|
|
||||||
if (((img_array.ndim != 2) and
|
|
||||||
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
|
||||||
msg = "Writing images is restricted to single-channel "
|
|
||||||
msg += "greyscale images or three-channel RGB images when "
|
|
||||||
msg += "the OpenJPEG library version is the official 2.0.0 "
|
|
||||||
msg += "release."
|
|
||||||
raise IOError(msg)
|
|
||||||
|
|
||||||
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
|
|
||||||
msg = "Only uint8 and uint16 images are currently supported."
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
|
|
||||||
_COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
|
|
||||||
'gray': opj2.CLRSPC_GRAY,
|
|
||||||
'grey': opj2.CLRSPC_GRAY,
|
|
||||||
'ycc': opj2.CLRSPC_YCC}
|
|
||||||
|
|
||||||
# Setup the default callback handlers. See the callback functions subsection
|
# Setup the default callback handlers. See the callback functions subsection
|
||||||
# in the ctypes section of the Python documentation for a solid explanation of
|
# in the ctypes section of the Python documentation for a solid explanation of
|
||||||
# what's going on here.
|
# what's going on here.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue