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,
'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 = {
'LRCP': LRCP,
'RLCP': RLCP,

View file

@ -29,8 +29,9 @@ import numpy as np
from .codestream import Codestream
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 . import core
from .jp2box import Jp2kBox
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
from .jp2box import ColourSpecificationBox, ContiguousCodestreamBox
@ -173,14 +174,34 @@ class Jp2k(Jp2kBox):
raise IOError(msg)
if cinema_mode == 'cinema2k':
if fps == 24:
cparams.cp_cinema = CINEMA_MODE['cinema2k_24']
elif fps == 48:
cparams.cp_cinema = CINEMA_MODE['cinema2k_48']
else:
if fps not in [24, 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:
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
@ -554,9 +575,9 @@ class Jp2k(Jp2kBox):
stack.callback(libc.fclose, fptr)
else:
# Introduced in 2.1 devel series.
strm = opj2.stream_create_default_file_stream_v3(self.filename,
False)
stack.callback(opj2.stream_destroy_v3, strm)
strm = opj2.stream_create_default_file_stream(self.filename,
False)
stack.callback(opj2.stream_destroy, strm)
opj2.start_compress(codec, image, strm)
opj2.encode(codec, strm)
@ -909,12 +930,10 @@ class Jp2k(Jp2kBox):
layer=layer, tile=tile, area=area)
with ExitStack() as stack:
if hasattr(opj2.OPENJP2,
'opj_stream_create_default_file_stream_v3'):
if re.match("2.1", version.openjpeg_version):
filename = self.filename
stream = opj2.stream_create_default_file_stream_v3(filename,
True)
stack.callback(opj2.stream_destroy_v3, stream)
stream = opj2.stream_create_default_file_stream(filename, True)
stack.callback(opj2.stream_destroy, stream)
else:
fptr = libc.fopen(self.filename, 'rb')
stack.callback(libc.fclose, fptr)
@ -1080,17 +1099,16 @@ class Jp2k(Jp2kBox):
layer=layer, tile=tile, area=area)
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')
stack.callback(libc.fclose, fptr)
stream = opj2.stream_create_default_file_stream(fptr, True)
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)
stack.callback(opj2.destroy_codec, codec)

View file

@ -5,6 +5,7 @@ Wraps individual functions in openjp2 library.
# pylint: disable=C0302,R0903,W0201
import ctypes
import re
import sys
from .config import glymur_config
@ -12,7 +13,11 @@ OPENJP2, OPENJPEG = glymur_config()
def version():
"""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()
if sys.hexversion >= 0x03000000:
return library_version.decode('utf-8')
@ -43,6 +48,13 @@ JPWL_MAX_NO_TILESPECS = 16
TRUE = 1
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
CLRSPC_UNKNOWN = -1
CLRSPC_UNSPECIFIED = 0
@ -368,6 +380,16 @@ class CompressionParametersType(ctypes.Structure):
# based encoding without offset concerning all the components.
("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):
"""Defines a single image component.
@ -1278,7 +1300,7 @@ def 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.
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)
return stream
def stream_create_default_file_stream_v3(fname, isa_read_stream):
"""Wraps openjp2 library function opj_stream_create_default_vile_stream_v3.
def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
"""Wraps openjp2 library function opj_stream_create_default_vile_stream.
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
----------
@ -1323,13 +1344,18 @@ def stream_create_default_file_stream_v3(fname, isa_read_stream):
An OpenJPEG file stream.
"""
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_v3.restype = STREAM_TYPE_P
OPENJP2.opj_stream_create_default_file_stream.argtypes = ARGTYPES
OPENJP2.opj_stream_create_default_file_stream.restype = STREAM_TYPE_P
read_stream = 1 if isa_read_stream else 0
file_argument = ctypes.c_char_p(fname.encode())
stream = OPENJP2.opj_stream_create_default_file_stream_v3(file_argument,
read_stream)
stream = OPENJP2.opj_stream_create_default_file_stream(file_argument,
read_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):
@ -1347,21 +1373,6 @@ def 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):
"""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):
"""The openjpeg error handler has recorded an error message."""
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_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)
image = openjp2.read_header(stream, codec)
@ -100,7 +100,7 @@ class TestOpenJP2(unittest.TestCase):
openjp2.end_decompress(codec, stream)
openjp2.destroy_codec(codec)
openjp2.stream_destroy_v3(stream)
openjp2.stream_destroy(stream)
openjp2.image_destroy(image)
def test_tte0(self):
@ -308,7 +308,7 @@ def tile_encoder(**kwargs):
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)
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.end_compress(codec, stream)
openjp2.stream_destroy_v3(stream)
openjp2.stream_destroy(stream)
openjp2.destroy_codec(codec)
openjp2.image_destroy(l_image)
@ -325,7 +325,7 @@ def tile_decoder(**kwargs):
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)
dparam = openjp2.set_default_decoder_parameters()
@ -361,7 +361,7 @@ def tile_decoder(**kwargs):
openjp2.end_decompress(codec, stream)
openjp2.destroy_codec(codec)
openjp2.stream_destroy_v3(stream)
openjp2.stream_destroy(stream)
openjp2.image_destroy(image)
def ttx0_setup(filename):