Updated openjp2 API for upstream openjpeg r2837 and r2847. #221

Removed "_v3" suffixes from glymur.lib.openjp2 functions.
Changed JP2k object to use both 2.0 and 2.1 means of setting cinema
compression parameter values.
This commit is contained in:
jevans 2014-04-24 20:06:15 -04:00 • committed by John Evans
commit 1e754f0e85
4 changed files with 139 additions and 63 deletions

View file

@ -44,6 +44,63 @@ CINEMA_MODE = {
'cinema2k_48': CINEMA2K_48, 'cinema2k_48': CINEMA2K_48,
'cinema4k_24': CINEMA4K_24, } 'cinema4k_24': CINEMA4K_24, }
PROFILE = {
# no profile, conform to 15444-1
'none': 0x0000,
# profile 0 as described in 15444-1,Table A.45
'0': 0x0001,
'zero': 0x0001,
0: 0x0001,
# profile 1 as described in 15444-1,Table A.45
'1': 0x0002,
'one': 0x0002,
1: 0x0002,
# at least 1 extension defined in 15444-2 (Part-2)
'2': 0x8000,
2: 0x8000,
'two': 0x8000,
# 2K cinema profile defined in 15444-1 AMD1
'cinema_2k': 0x0003,
# 4K cinema profile defined in 15444-1 AMD1
'cinema_4k': 0x0004,
# scalable 2K cinema profile defined in 15444-1 AMD2
'cinema_s2k': 0x0005,
# scalable 4K cinema profile defined in 15444-1 AMD2
'cinema_s4k': 0x0006,
# long term storage cinema profile defined in 15444-1 AMD2
'cinema_lts': 0x0007,
# Single Multi Tile Broadcast profile defined in 15444-1 AMD3
'bc_single': 0x0100,
# Multi Tile Broadcast profile defined in 15444-1 AMD3
'bc_multi': 0x0200,
# Multi Tile Reversible Broadcast profile defined in 15444-1 AMD3
'bc_multi_r': 0x0300,
# 2K Single Tile Lossy IMF profile defined in 15444-1 AMD 8
'imf_2k': 0x0400,
# 4K Single Tile Lossy IMF profile defined in 15444-1 AMD 8
'imf_4k': 0x0401,
# 8K Single Tile Lossy IMF profile defined in 15444-1 AMD 8
'imf_8k': 0x0402,
# 2K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8
'imf_2k_r': 0x0403,
# 4K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8
'imf_4k_r': 0x0800,
# 8K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8
'imf_8k_r': 0x0801,
}
# JPEG 2000 codestream and component size limits in cinema profiles
#
# Maximum codestream length for 24fps
OPJ_CINEMA_24_CS = 1302083
# Maximum codestream length for 48fps
OPJ_CINEMA_48_CS = 651041
# Maximum size per color component for 2K & 4K @ 24fps
OPJ_CINEMA_24_COMP = 1041666
# Maximum size per color component for 2K @ 48fps
OPJ_CINEMA_48_COMP = 520833
PROGRESSION_ORDER = { PROGRESSION_ORDER = {
'LRCP': LRCP, 'LRCP': LRCP,
'RLCP': RLCP, 'RLCP': RLCP,

View file

@ -29,8 +29,9 @@ 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, CINEMA_MODE from .core import PROGRESSION_ORDER
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
from . import core
from .jp2box import Jp2kBox from .jp2box import Jp2kBox
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
from .jp2box import ColourSpecificationBox, ContiguousCodestreamBox from .jp2box import ColourSpecificationBox, ContiguousCodestreamBox
@ -173,14 +174,34 @@ class Jp2k(Jp2kBox):
raise IOError(msg) raise IOError(msg)
if cinema_mode == 'cinema2k': if cinema_mode == 'cinema2k':
if fps == 24: if fps not in [24, 48]:
cparams.cp_cinema = CINEMA_MODE['cinema2k_24']
elif fps == 48:
cparams.cp_cinema = CINEMA_MODE['cinema2k_48']
else:
raise IOError('Cinema2K frame rate must be either 24 or 48.') raise IOError('Cinema2K frame rate must be either 24 or 48.')
if re.match("2.0", version.openjpeg_version) is not None:
# 2.0 API
if fps == 24:
cparams.cp_cinema = core.CINEMA_MODE['cinema2k_24']
else:
cparams.cp_cinema = core.CINEMA_MODE['cinema2k_48']
else:
# 2.1 API
if fps == 24:
cparams.rsiz = core.PROFILE['cinema_2k']
cparams.max_comp_size = core.CINEMA_24_COMP
cparams.max_cs_size = core.CINEMA_24_CS
else:
cparams.rsiz = core.PROFILE['cinema_2k']
cparams.max_comp_size = core.CINEMA_48_COMP
cparams.max_cs_size = core.CINEMA_48_CS
else: else:
cparams.cp_cinema = CINEMA_MODE['cinema4k_24'] # cinema4k
if re.match("2.0", version.openjpeg_version) is not None:
# 2.0 API
cparams.cp_cinema = core.CINEMA_MODE['cinema4k_24']
else:
# 2.1 API
cparams.rsiz = core.PROFILE['cinema_4k']
return return
@ -554,9 +575,9 @@ class Jp2k(Jp2kBox):
stack.callback(libc.fclose, fptr) stack.callback(libc.fclose, fptr)
else: else:
# Introduced in 2.1 devel series. # Introduced in 2.1 devel series.
strm = opj2.stream_create_default_file_stream_v3(self.filename, strm = opj2.stream_create_default_file_stream(self.filename,
False) False)
stack.callback(opj2.stream_destroy_v3, strm) stack.callback(opj2.stream_destroy, strm)
opj2.start_compress(codec, image, strm) opj2.start_compress(codec, image, strm)
opj2.encode(codec, strm) opj2.encode(codec, strm)
@ -909,12 +930,10 @@ class Jp2k(Jp2kBox):
layer=layer, tile=tile, area=area) layer=layer, tile=tile, area=area)
with ExitStack() as stack: with ExitStack() as stack:
if hasattr(opj2.OPENJP2, if re.match("2.1", version.openjpeg_version):
'opj_stream_create_default_file_stream_v3'):
filename = self.filename filename = self.filename
stream = opj2.stream_create_default_file_stream_v3(filename, stream = opj2.stream_create_default_file_stream(filename, True)
True) stack.callback(opj2.stream_destroy, stream)
stack.callback(opj2.stream_destroy_v3, stream)
else: else:
fptr = libc.fopen(self.filename, 'rb') fptr = libc.fopen(self.filename, 'rb')
stack.callback(libc.fclose, fptr) stack.callback(libc.fclose, fptr)
@ -1080,17 +1099,16 @@ class Jp2k(Jp2kBox):
layer=layer, tile=tile, area=area) layer=layer, tile=tile, area=area)
with ExitStack() as stack: with ExitStack() as stack:
if re.match("2.0", version.openjpeg_version): if re.match("2.1", version.openjpeg_version):
# API change in 2.1
filename = self.filename
stream = opj2.stream_create_default_file_stream(filename, True)
stack.callback(opj2.stream_destroy, stream)
else:
fptr = libc.fopen(self.filename, 'rb') fptr = libc.fopen(self.filename, 'rb')
stack.callback(libc.fclose, fptr) stack.callback(libc.fclose, fptr)
stream = opj2.stream_create_default_file_stream(fptr, True) stream = opj2.stream_create_default_file_stream(fptr, True)
stack.callback(opj2.stream_destroy, stream) stack.callback(opj2.stream_destroy, stream)
else:
# API change in 2.1+
filename = self.filename
stream = opj2.stream_create_default_file_stream_v3(filename,
True)
stack.callback(opj2.stream_destroy_v3, stream)
codec = opj2.create_decompress(self._codec_format) codec = opj2.create_decompress(self._codec_format)
stack.callback(opj2.destroy_codec, codec) stack.callback(opj2.destroy_codec, codec)

View file

@ -5,6 +5,7 @@ Wraps individual functions in openjp2 library.
# pylint: disable=C0302,R0903,W0201 # pylint: disable=C0302,R0903,W0201
import ctypes import ctypes
import re
import sys import sys
from .config import glymur_config from .config import glymur_config
@ -12,7 +13,11 @@ OPENJP2, OPENJPEG = glymur_config()
def version(): def version():
"""Wrapper for opj_version library routine.""" """Wrapper for opj_version library routine."""
OPENJP2.opj_version.restype = ctypes.c_char_p try:
OPENJP2.opj_version.restype = ctypes.c_char_p
except:
return "0.0.0"
library_version = OPENJP2.opj_version() library_version = OPENJP2.opj_version()
if sys.hexversion >= 0x03000000: if sys.hexversion >= 0x03000000:
return library_version.decode('utf-8') return library_version.decode('utf-8')
@ -43,6 +48,13 @@ JPWL_MAX_NO_TILESPECS = 16
TRUE = 1 TRUE = 1
FALSE = 0 FALSE = 0
#PROFILE = {'none': 0, # No profile
# 0: 1, # Profile 0
# 1: 2, # Profile 1
# 'part2': 0x8000, # At least one extension
# 'Cinema2K': 0x0003, # 2K cinema profile
# 'Cinema4K': 0x0004, # 4K cinema profile
# supported color spaces # supported color spaces
CLRSPC_UNKNOWN = -1 CLRSPC_UNKNOWN = -1
CLRSPC_UNSPECIFIED = 0 CLRSPC_UNSPECIFIED = 0
@ -368,6 +380,16 @@ class CompressionParametersType(ctypes.Structure):
# based encoding without offset concerning all the components. # based encoding without offset concerning all the components.
("mct_data", ctypes.c_void_p)] ("mct_data", ctypes.c_void_p)]
if _MINOR == '1':
# Maximum size (in bytes) for the whole codestream.
# If == 0, codestream size limitation is not considered.
# If it does not comply with tcp_rates, max_cs_size prevails and a
# warning is issued.
_fields_.append(("max_cs_size", ctypes.c_int32))
# To be used to combine OPJ_PROFILE_*, OPJ_EXTENSION_* and (sub)levels
# values.
_fields_.append(("rsiz", ctypes.c_uint16))
class ImageCompType(ctypes.Structure): class ImageCompType(ctypes.Structure):
"""Defines a single image component. """Defines a single image component.
@ -1278,7 +1300,7 @@ def start_compress(codec, image, stream):
OPENJP2.opj_start_compress(codec, image, stream) OPENJP2.opj_start_compress(codec, image, stream)
def stream_create_default_file_stream(fptr, isa_read_stream): def _stream_create_default_file_stream_2p0(fptr, isa_read_stream):
"""Wraps openjp2 library function opj_stream_create_default_vile_stream. """Wraps openjp2 library function opj_stream_create_default_vile_stream.
Sets the stream to be a file stream. This is valid only for version 2.0.0 Sets the stream to be a file stream. This is valid only for version 2.0.0
@ -1303,12 +1325,11 @@ def stream_create_default_file_stream(fptr, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream) stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream)
return stream return stream
def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
def stream_create_default_file_stream_v3(fname, isa_read_stream): """Wraps openjp2 library function opj_stream_create_default_vile_stream.
"""Wraps openjp2 library function opj_stream_create_default_vile_stream_v3.
Sets the stream to be a file stream. This function is only valid for the Sets the stream to be a file stream. This function is only valid for the
trunk/development 2.0+ version of the openjp2 library. 2.1 version of the openjp2 library.
Parameters Parameters
---------- ----------
@ -1323,14 +1344,19 @@ def stream_create_default_file_stream_v3(fname, isa_read_stream):
An OpenJPEG file stream. An OpenJPEG file stream.
""" """
ARGTYPES = [ctypes.c_char_p, ctypes.c_int32] ARGTYPES = [ctypes.c_char_p, ctypes.c_int32]
OPENJP2.opj_stream_create_default_file_stream_v3.argtypes = ARGTYPES OPENJP2.opj_stream_create_default_file_stream.argtypes = ARGTYPES
OPENJP2.opj_stream_create_default_file_stream_v3.restype = STREAM_TYPE_P OPENJP2.opj_stream_create_default_file_stream.restype = STREAM_TYPE_P
read_stream = 1 if isa_read_stream else 0 read_stream = 1 if isa_read_stream else 0
file_argument = ctypes.c_char_p(fname.encode()) file_argument = ctypes.c_char_p(fname.encode())
stream = OPENJP2.opj_stream_create_default_file_stream_v3(file_argument, stream = OPENJP2.opj_stream_create_default_file_stream(file_argument,
read_stream) read_stream)
return stream return stream
if re.match(r'''2.0''', version()):
stream_create_default_file_stream = _stream_create_default_file_stream_2p0
else:
stream_create_default_file_stream = _stream_create_default_file_stream_2p1
def stream_destroy(stream): def stream_destroy(stream):
"""Wraps openjp2 library function opj_stream_destroy. """Wraps openjp2 library function opj_stream_destroy.
@ -1347,21 +1373,6 @@ def stream_destroy(stream):
OPENJP2.opj_stream_destroy(stream) OPENJP2.opj_stream_destroy(stream)
def stream_destroy_v3(stream):
"""Wraps openjp2 library function opj_stream_destroy_v3.
Destroys the stream created by create_stream_v3.
Parameters
----------
stream : STREAM_TYPE_P
The file stream.
"""
OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P]
OPENJP2.opj_stream_destroy_v3.restype = ctypes.c_void_p
OPENJP2.opj_stream_destroy_v3(stream)
def write_tile(codec, tile_index, data, data_size, stream): def write_tile(codec, tile_index, data, data_size, stream):
"""Wraps openjp2 library function opj_write_tile. """Wraps openjp2 library function opj_write_tile.
@ -1403,13 +1414,3 @@ def write_tile(codec, tile_index, data, data_size, stream):
def set_error_message(msg): def set_error_message(msg):
"""The openjpeg error handler has recorded an error message.""" """The openjpeg error handler has recorded an error message."""
ERROR_MSG_LST.append(msg) ERROR_MSG_LST.append(msg)
def version():
"""Wrapper for opj_version library routine."""
OPENJP2.opj_version.restype = ctypes.c_char_p
library_version = OPENJP2.opj_version()
if sys.hexversion >= 0x03000000:
return library_version.decode('utf-8')
else:
return library_version

View file

@ -79,7 +79,7 @@ class TestOpenJP2(unittest.TestCase):
openjp2.set_warning_handler(codec, None) openjp2.set_warning_handler(codec, None)
openjp2.set_error_handler(codec, None) openjp2.set_error_handler(codec, None)
stream = openjp2.stream_create_default_file_stream_v3(filename, True) stream = openjp2.stream_create_default_file_stream(filename, True)
openjp2.setup_decoder(codec, dparam) openjp2.setup_decoder(codec, dparam)
image = openjp2.read_header(stream, codec) image = openjp2.read_header(stream, codec)
@ -100,7 +100,7 @@ class TestOpenJP2(unittest.TestCase):
openjp2.end_decompress(codec, stream) openjp2.end_decompress(codec, stream)
openjp2.destroy_codec(codec) openjp2.destroy_codec(codec)
openjp2.stream_destroy_v3(stream) openjp2.stream_destroy(stream)
openjp2.image_destroy(image) openjp2.image_destroy(image)
def test_tte0(self): def test_tte0(self):
@ -308,7 +308,7 @@ def tile_encoder(**kwargs):
openjp2.setup_encoder(codec, l_param, l_image) openjp2.setup_encoder(codec, l_param, l_image)
stream = openjp2.stream_create_default_file_stream_v3(kwargs['filename'], stream = openjp2.stream_create_default_file_stream(kwargs['filename'],
False) False)
openjp2.start_compress(codec, l_image, stream) openjp2.start_compress(codec, l_image, stream)
@ -316,7 +316,7 @@ def tile_encoder(**kwargs):
openjp2.write_tile(codec, j, data, tile_size, stream) openjp2.write_tile(codec, j, data, tile_size, stream)
openjp2.end_compress(codec, stream) openjp2.end_compress(codec, stream)
openjp2.stream_destroy_v3(stream) openjp2.stream_destroy(stream)
openjp2.destroy_codec(codec) openjp2.destroy_codec(codec)
openjp2.image_destroy(l_image) openjp2.image_destroy(l_image)
@ -325,7 +325,7 @@ def tile_decoder(**kwargs):
Reads a tile. That's all it does. Reads a tile. That's all it does.
""" """
stream = openjp2.stream_create_default_file_stream_v3(kwargs['filename'], stream = openjp2.stream_create_default_file_stream(kwargs['filename'],
True) True)
dparam = openjp2.set_default_decoder_parameters() dparam = openjp2.set_default_decoder_parameters()
@ -361,7 +361,7 @@ def tile_decoder(**kwargs):
openjp2.end_decompress(codec, stream) openjp2.end_decompress(codec, stream)
openjp2.destroy_codec(codec) openjp2.destroy_codec(codec)
openjp2.stream_destroy_v3(stream) openjp2.stream_destroy(stream)
openjp2.image_destroy(image) openjp2.image_destroy(image)
def ttx0_setup(filename): def ttx0_setup(filename):