From dc0c1950a259602c149abc3cb300254c6daab5be Mon Sep 17 00:00:00 2001 From: jevans Date: Sun, 19 Oct 2014 13:00:45 -0400 Subject: [PATCH 1/7] printing of default decompression parameters --- glymur/lib/openjp2.py | 7 +++++++ glymur/test/fixtures.py | 19 +++++++++++++++++++ glymur/test/test_printing.py | 29 ++++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index f3f5b5c..d5322ec 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -201,6 +201,13 @@ class DecompressionParametersType(ctypes.Structure): # maximum number of tiles ("flags", ctypes.c_uint32)] + def __str__(self): + msg = "{0}:\n".format(self.__class__) + for field_name, _ in self._fields_: + msg += " {0}: {1}\n".format( + field_name, getattr(self, field_name)) + return msg + class CompressionParametersType(ctypes.Structure): """Compression parameters. diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 4b3972c..1727d6f 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -900,3 +900,22 @@ goodstuff_with_full_header = r"""Codestream: Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)] SOD marker segment @ (164, 0) EOC marker segment @ (115218, 0)""" + +decompression_parameters_type = """: + cp_reduce: 0 + cp_layer: 0 + infile: b'' + outfile: b'' + decod_format: -1 + cod_format: -1 + DA_x0: 0 + DA_x1: 0 + DA_y0: 0 + DA_y1: 0 + m_verbose: 0 + tile_index: 0 + nb_tile_to_decode: 0 + jpwl_correct: 0 + jpwl_exp_comps: 0 + jpwl_max_tiles: 0 + flags: 0""" diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 49bbe6d..8c548b7 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -32,12 +32,13 @@ import lxml.etree as ET import glymur from glymur import Jp2k, command_line from . import fixtures -from .fixtures import OPJ_DATA_ROOT, opj_data_file -from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG -from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34 +from .fixtures import ( + OPJ_DATA_ROOT, opj_data_file, + WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG, + WINDOWS_TMP_FILE_MSG, text_gbr_27, text_gbr_33, text_gbr_34 +) - -@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") +@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) class TestPrinting(unittest.TestCase): """Tests for verifying how printing works.""" def setUp(self): @@ -617,6 +618,24 @@ class TestPrinting(unittest.TestCase): self.assertEqual(actual, expected) +@unittest.skipIf(re.match('1|2.0', glymur.version.openjpeg_version), + "Requires openjpeg 2.1.0 or higher") +class TestPrintingOpenjp2(unittest.TestCase): + """Tests for verifying how printing works on openjp2 library structures.""" + def setUp(self): + self.jp2file = glymur.data.nemo() + + def tearDown(self): + pass + + def test_decompression_parameters(self): + """printing DecompressionParametersType""" + dparams = glymur.lib.openjp2.set_default_decoder_parameters() + with patch('sys.stdout', new=StringIO()) as fake_out: + print(dparams) + actual = fake_out.getvalue().strip() + expected = fixtures.decompression_parameters_type + self.assertEqual(actual, expected) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") From 1a2aa8397b8c9fe5dd81ab5404447600bad8f1db Mon Sep 17 00:00:00 2001 From: jevans Date: Sun, 19 Oct 2014 13:13:35 -0400 Subject: [PATCH 2/7] fix library path issue when no configuration file exists, #265 --- glymur/lib/config.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/glymur/lib/config.py b/glymur/lib/config.py index a475233..431a00c 100644 --- a/glymur/lib/config.py +++ b/glymur/lib/config.py @@ -117,15 +117,17 @@ def read_config_file(libname): None if no location is specified, otherwise a path to the library """ filename = glymurrc_fname() - if filename is not None: - # Read the configuration file for the library location. - parser = ConfigParser() - parser.read(filename) - try: - path = parser.get('library', libname) - except NoOptionError: - path = None + if filename is None: + # There's no library file path to return in this case. + return None + # Read the configuration file for the library location. + parser = ConfigParser() + parser.read(filename) + try: + path = parser.get('library', libname) + except NoOptionError: + path = None return path def glymur_config(): From 3d60d3087143a3bd29a7dc34c82626b5fb3e014e Mon Sep 17 00:00:00 2001 From: jevans Date: Sun, 19 Oct 2014 18:13:59 -0400 Subject: [PATCH 3/7] added print support for PocType --- glymur/lib/openjp2.py | 7 +++++++ glymur/test/fixtures.py | 38 ++++++++++++++++++++++++++++++++++++ glymur/test/test_printing.py | 11 +++++++++++ 3 files changed, 56 insertions(+) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index d5322ec..d37bbb8 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -138,6 +138,13 @@ class PocType(ctypes.Structure): ("tx0_t", ctypes.c_uint32), ("ty0_t", ctypes.c_uint32)] + def __str__(self): + msg = "{0}:\n".format(self.__class__) + for field_name, _ in self._fields_: + msg += " {0}: {1}\n".format( + field_name, getattr(self, field_name)) + return msg + class DecompressionParametersType(ctypes.Structure): """Decompression parameters. diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 1727d6f..3a28626 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -919,3 +919,41 @@ decompression_parameters_type = """: + resno0: 0 + compno0: 0 + layno1: 0 + resno1: 0 + compno1: 0 + layno0: 0 + precno0: 0 + precno1: 0 + prg1: 0 + prg: 0 + progorder: b'' + tile: 0 + tx0: 0 + tx1: 0 + ty0: 0 + ty1: 0 + layS: 0 + resS: 0 + compS: 0 + prcS: 0 + layE: 0 + resE: 0 + compE: 0 + prcE: 0 + txS: 0 + txE: 0 + tyS: 0 + tyE: 0 + dx: 0 + dy: 0 + lay_t: 0 + res_t: 0 + comp_t: 0 + prec_t: 0 + tx0_t: 0 + ty0_t: 0""" diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 8c548b7..8dc0f48 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -618,6 +618,7 @@ class TestPrinting(unittest.TestCase): self.assertEqual(actual, expected) +@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here") @unittest.skipIf(re.match('1|2.0', glymur.version.openjpeg_version), "Requires openjpeg 2.1.0 or higher") class TestPrintingOpenjp2(unittest.TestCase): @@ -637,6 +638,16 @@ class TestPrintingOpenjp2(unittest.TestCase): expected = fixtures.decompression_parameters_type self.assertEqual(actual, expected) + def test_progression_order_changes(self): + """printing PocType""" + ptype = glymur.lib.openjp2.PocType() + with patch('sys.stdout', new=StringIO()) as fake_out: + print(ptype) + actual = fake_out.getvalue().strip() + expected = fixtures.default_progression_order_changes_type + self.assertEqual(actual, expected) + + @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") From 67456c3b89429f1a9b98ba674caeda6a22e3dad8 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 20 Oct 2014 17:01:57 -0400 Subject: [PATCH 4/7] untested __str__ for compression parameters type --- glymur/lib/openjp2.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index d37bbb8..8c6defa 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -7,6 +7,7 @@ Wraps individual functions in openjp2 library. import ctypes import re import sys +import textwrap from .config import glymur_config @@ -406,6 +407,29 @@ class CompressionParametersType(ctypes.Structure): # values. _fields_.append(("rsiz", ctypes.c_uint16)) + def __str__(self): + msg = "{0}:\n".format(self.__class__) + for field_name, _ in self._fields_: + + if field_name == 'poc': + msg += " numpocs: {1}\n".format(self.numpocs) + for j in range(self.numpocs): + msg += " [#{0}]:".format(j) + msg += " {0}".format(str(self.poc[j])) + msg += textwrap.indent(textstr, ' ' * 12) + + elif field_name in ['tcp_rates', 'tcp_distoratio']: + lst = [] + arr = getattr(self, field_name) + lst = [arr[j] for j in range(self.tcp_numlayers)] + msg += " {0}: {1}\n".format(field_name, lst) + + else: + msg += " {0}: {1}\n".format( + field_name, getattr(self, field_name)) + return msg + + class ImageCompType(ctypes.Structure): """Defines a single image component. From 6abf184ad7875c2f6a687147dbd13a577e1e187f Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 20 Oct 2014 19:53:42 -0400 Subject: [PATCH 5/7] added compression parameters printing --- glymur/lib/openjp2.py | 20 +++++- glymur/lib/test/fixtures.py | 118 +++++++++++++++++++++++++++++++ glymur/lib/test/test_printing.py | 57 +++++++++++++++ glymur/test/fixtures.py | 56 --------------- glymur/test/test_printing.py | 30 -------- 5 files changed, 194 insertions(+), 87 deletions(-) create mode 100644 glymur/lib/test/fixtures.py create mode 100644 glymur/lib/test/test_printing.py diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index 8c6defa..10b6c3d 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -412,7 +412,7 @@ class CompressionParametersType(ctypes.Structure): for field_name, _ in self._fields_: if field_name == 'poc': - msg += " numpocs: {1}\n".format(self.numpocs) + msg += " numpocs: {0}\n".format(self.numpocs) for j in range(self.numpocs): msg += " [#{0}]:".format(j) msg += " {0}".format(str(self.poc[j])) @@ -424,6 +424,24 @@ class CompressionParametersType(ctypes.Structure): lst = [arr[j] for j in range(self.tcp_numlayers)] msg += " {0}: {1}\n".format(field_name, lst) + elif field_name in ['prcw_init', 'prch_init']: + pass + + elif field_name == 'res_spec': + prcw_init = [self.prcw_init[j] for j in range(self.res_spec)] + prch_init = [self.prch_init[j] for j in range(self.res_spec)] + msg += " res_spec: {0}\n".format(self.res_spec) + msg += " prch_init: {0}\n".format(prch_init) + msg += " prcw_init: {0}\n".format(prcw_init) + + elif field_name in [ + 'jpwl_hprot_tph_tileno', 'jpwl_hprot_tph', + 'jpwl_pprot_tileno', 'jpwl_pprot_packno', 'jpwl_pprot', + 'jpwl_sens_tph_tileno', 'jpwl_sens_tph']: + arr = getattr(self, field_name) + lst = [arr[j] for j in range(JPWL_MAX_NO_TILESPECS)] + msg += " {0}: {1}\n".format(field_name, lst) + else: msg += " {0}: {1}\n".format( field_name, getattr(self, field_name)) diff --git a/glymur/lib/test/fixtures.py b/glymur/lib/test/fixtures.py new file mode 100644 index 0000000..dc94e7c --- /dev/null +++ b/glymur/lib/test/fixtures.py @@ -0,0 +1,118 @@ +decompression_parameters_type = """: + cp_reduce: 0 + cp_layer: 0 + infile: b'' + outfile: b'' + decod_format: -1 + cod_format: -1 + DA_x0: 0 + DA_x1: 0 + DA_y0: 0 + DA_y1: 0 + m_verbose: 0 + tile_index: 0 + nb_tile_to_decode: 0 + jpwl_correct: 0 + jpwl_exp_comps: 0 + jpwl_max_tiles: 0 + flags: 0""" + +default_progression_order_changes_type = """: + resno0: 0 + compno0: 0 + layno1: 0 + resno1: 0 + compno1: 0 + layno0: 0 + precno0: 0 + precno1: 0 + prg1: 0 + prg: 0 + progorder: b'' + tile: 0 + tx0: 0 + tx1: 0 + ty0: 0 + ty1: 0 + layS: 0 + resS: 0 + compS: 0 + prcS: 0 + layE: 0 + resE: 0 + compE: 0 + prcE: 0 + txS: 0 + txE: 0 + tyS: 0 + tyE: 0 + dx: 0 + dy: 0 + lay_t: 0 + res_t: 0 + comp_t: 0 + prec_t: 0 + tx0_t: 0 + ty0_t: 0""" + +default_compression_parameters_type = """: + tile_size_on: 0 + cp_tx0: 0 + cp_ty0: 0 + cp_tdx: 0 + cp_tdy: 0 + cp_disto_alloc: 0 + cp_fixed_alloc: 0 + cp_fixed_quality: 0 + cp_matrice: None + cp_comment: None + csty: 0 + prog_order: 0 + numpocs: 0 + numpocs: 0 + tcp_numlayers: 0 + tcp_rates: [] + tcp_distoratio: [] + numresolution: 6 + cblockw_init: 64 + cblockh_init: 64 + mode: 0 + irreversible: 0 + roi_compno: -1 + roi_shift: 0 + res_spec: 0 + prch_init: [] + prcw_init: [] + infile: b'' + outfile: b'' + index_on: 0 + index: b'' + image_offset_x0: 0 + image_offset_y0: 0 + subsampling_dx: 1 + subsampling_dy: 1 + decod_format: -1 + cod_format: -1 + jpwl_epc_on: 0 + jpwl_hprot_mh: 0 + jpwl_hprot_tph_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + jpwl_hprot_tph: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + jpwl_pprot_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + jpwl_pprot_packno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + jpwl_pprot: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + jpwl_sens_size: 0 + jpwl_sens_addr: 0 + jpwl_sens_range: 0 + jpwl_sens_mh: 0 + jpwl_sens_tph_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + jpwl_sens_tph: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + cp_cinema: 0 + max_comp_size: 0 + cp_rsiz: 0 + tp_on: 0 + tp_flag: 0 + tcp_mct: 0 + jpip_on: 0 + mct_data: None + max_cs_size: 0 + rsiz: 0""" diff --git a/glymur/lib/test/test_printing.py b/glymur/lib/test/test_printing.py new file mode 100644 index 0000000..5204311 --- /dev/null +++ b/glymur/lib/test/test_printing.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +"""Test suite for printing. +""" + +import re +import sys +import unittest + +if sys.hexversion < 0x03000000: + from mock import patch + from StringIO import StringIO +else: + from unittest.mock import patch + from io import StringIO + +import glymur +from . import fixtures + +@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here") +@unittest.skipIf(re.match('1|2.0', glymur.version.openjpeg_version), + "Requires openjpeg 2.1.0 or higher") +class TestPrintingOpenjp2(unittest.TestCase): + """Tests for verifying how printing works on openjp2 library structures.""" + def setUp(self): + self.jp2file = glymur.data.nemo() + + def tearDown(self): + pass + + def test_decompression_parameters(self): + """printing DecompressionParametersType""" + dparams = glymur.lib.openjp2.set_default_decoder_parameters() + with patch('sys.stdout', new=StringIO()) as fake_out: + print(dparams) + actual = fake_out.getvalue().strip() + expected = fixtures.decompression_parameters_type + self.assertEqual(actual, expected) + + def test_progression_order_changes(self): + """printing PocType""" + ptype = glymur.lib.openjp2.PocType() + with patch('sys.stdout', new=StringIO()) as fake_out: + print(ptype) + actual = fake_out.getvalue().strip() + expected = fixtures.default_progression_order_changes_type + self.assertEqual(actual, expected) + + def test_default_compression_parameters(self): + """printing default compression parameters""" + cparams = glymur.lib.openjp2.set_default_encoder_parameters() + with patch('sys.stdout', new=StringIO()) as fake_out: + print(cparams) + actual = fake_out.getvalue().strip() + expected = fixtures.default_compression_parameters_type + self.assertEqual(actual, expected) + + diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 3a28626..481da2d 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -901,59 +901,3 @@ goodstuff_with_full_header = r"""Codestream: SOD marker segment @ (164, 0) EOC marker segment @ (115218, 0)""" -decompression_parameters_type = """: - cp_reduce: 0 - cp_layer: 0 - infile: b'' - outfile: b'' - decod_format: -1 - cod_format: -1 - DA_x0: 0 - DA_x1: 0 - DA_y0: 0 - DA_y1: 0 - m_verbose: 0 - tile_index: 0 - nb_tile_to_decode: 0 - jpwl_correct: 0 - jpwl_exp_comps: 0 - jpwl_max_tiles: 0 - flags: 0""" - -default_progression_order_changes_type = """: - resno0: 0 - compno0: 0 - layno1: 0 - resno1: 0 - compno1: 0 - layno0: 0 - precno0: 0 - precno1: 0 - prg1: 0 - prg: 0 - progorder: b'' - tile: 0 - tx0: 0 - tx1: 0 - ty0: 0 - ty1: 0 - layS: 0 - resS: 0 - compS: 0 - prcS: 0 - layE: 0 - resE: 0 - compE: 0 - prcE: 0 - txS: 0 - txE: 0 - tyS: 0 - tyE: 0 - dx: 0 - dy: 0 - lay_t: 0 - res_t: 0 - comp_t: 0 - prec_t: 0 - tx0_t: 0 - ty0_t: 0""" diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 8dc0f48..b6eca2f 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -618,36 +618,6 @@ class TestPrinting(unittest.TestCase): self.assertEqual(actual, expected) -@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here") -@unittest.skipIf(re.match('1|2.0', glymur.version.openjpeg_version), - "Requires openjpeg 2.1.0 or higher") -class TestPrintingOpenjp2(unittest.TestCase): - """Tests for verifying how printing works on openjp2 library structures.""" - def setUp(self): - self.jp2file = glymur.data.nemo() - - def tearDown(self): - pass - - def test_decompression_parameters(self): - """printing DecompressionParametersType""" - dparams = glymur.lib.openjp2.set_default_decoder_parameters() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(dparams) - actual = fake_out.getvalue().strip() - expected = fixtures.decompression_parameters_type - self.assertEqual(actual, expected) - - def test_progression_order_changes(self): - """printing PocType""" - ptype = glymur.lib.openjp2.PocType() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(ptype) - actual = fake_out.getvalue().strip() - expected = fixtures.default_progression_order_changes_type - self.assertEqual(actual, expected) - - @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") From a783f6f1393b7ae04245335516bc05ece5308024 Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 20 Oct 2014 21:50:26 -0400 Subject: [PATCH 6/7] internalized some private functions into Jp2k class methods --- glymur/jp2k.py | 290 +++++++++++++++++++++++-------------------------- 1 file changed, 136 insertions(+), 154 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 0bda856..aadb5ed 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -157,7 +157,7 @@ class Jp2k(Jp2kBox): msg += "profile if the file type box brand is 'jp2 '." warnings.warn(msg) - def _set_cinema_params(self, cparams, cinema_mode, fps): + def _set_cinema_params(self, cinema_mode, fps): """Populate compression parameters structure for cinema2K. Parameters @@ -176,7 +176,7 @@ class Jp2k(Jp2kBox): raise IOError(msg) # Cinema modes imply MCT. - cparams.tcp_mct = 1 + self._cparams.tcp_mct = 1 if cinema_mode == 'cinema2k': if fps not in [24, 48]: @@ -185,30 +185,28 @@ class Jp2k(Jp2kBox): if re.match("2.0", version.openjpeg_version) is not None: # 2.0 API if fps == 24: - cparams.cp_cinema = core.OPJ_CINEMA2K_24 + self._cparams.cp_cinema = core.OPJ_CINEMA2K_24 else: - cparams.cp_cinema = core.OPJ_CINEMA2K_48 + self._cparams.cp_cinema = core.OPJ_CINEMA2K_48 else: # 2.1 API if fps == 24: - cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K - cparams.max_comp_size = core.OPJ_CINEMA_24_COMP - cparams.max_cs_size = core.OPJ_CINEMA_24_CS + self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K + self._cparams.max_comp_size = core.OPJ_CINEMA_24_COMP + self._cparams.max_cs_size = core.OPJ_CINEMA_24_CS else: - cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K - cparams.max_comp_size = core.OPJ_CINEMA_48_COMP - cparams.max_cs_size = core.OPJ_CINEMA_48_CS + self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K + self._cparams.max_comp_size = core.OPJ_CINEMA_48_COMP + self._cparams.max_cs_size = core.OPJ_CINEMA_48_CS else: # cinema4k if re.match("2.0", version.openjpeg_version) is not None: # 2.0 API - cparams.cp_cinema = core.OPJ_CINEMA4K_24 + self._cparams.cp_cinema = core.OPJ_CINEMA4K_24 else: # 2.1 API - cparams.rsiz = core.OPJ_PROFILE_CINEMA_4K - - return + self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_4K def _populate_cparams(self, img_array, **kwargs): """Directs processing of write method arguments. @@ -219,11 +217,6 @@ class Jp2k(Jp2kBox): image data to be written to file kwargs : dictionary non-image keyword inputs provided to write method - - Returns - ------- - cparams : CompressionParametersType(ctypes.Structure) - corresponds to cparameters_t openjpeg datatype """ if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and (len(set(kwargs)) > 1)): @@ -258,12 +251,14 @@ class Jp2k(Jp2kBox): cparams.irreversible = 1 if 'cinema2k' in kwargs: - self._set_cinema_params(cparams, 'cinema2k', kwargs['cinema2k']) - return cparams + self._cparams = cparams + self._set_cinema_params('cinema2k', kwargs['cinema2k']) + return if 'cinema4k' in kwargs: - self._set_cinema_params(cparams, 'cinema4k', kwargs['cinema4k']) - return cparams + self._cparams = cparams + self._set_cinema_params('cinema4k', kwargs['cinema4k']) + return if 'cbsize' in kwargs: cparams.cblockw_init = kwargs['cbsize'][1] @@ -337,7 +332,7 @@ class Jp2k(Jp2kBox): self._validate_compression_params(img_array, cparams, **kwargs) - return cparams + self._cparams = cparams def write(self, img_array, verbose=False, **kwargs): """Write image data to a JP2/JPX/J2k file. Intended usage of the @@ -412,14 +407,14 @@ class Jp2k(Jp2kBox): "in order to write images.") self._determine_colorspace(img_array, **kwargs) - cparams = self._populate_cparams(img_array, **kwargs) + self._populate_cparams(img_array, **kwargs) if opj2.OPENJP2 is not None: - self._write_openjp2(img_array, cparams, verbose=verbose) + self._write_openjp2(img_array, verbose=verbose) else: - self._write_openjpeg(img_array, cparams, verbose=verbose) + self._write_openjpeg(img_array, verbose=verbose) - def _write_openjpeg(self, img_array, cparams, verbose=False): + def _write_openjpeg(self, img_array, verbose=False): """ Write JPEG 2000 file using OpenJPEG 1.5 interface. """ @@ -429,21 +424,21 @@ class Jp2k(Jp2kBox): img_array.shape[1], 1) - comptparms = _populate_comptparms(img_array, cparams) + self._populate_comptparms(img_array) with ExitStack() as stack: - image = opj.image_create(comptparms, self._colorspace) + image = opj.image_create(self._comptparms, self._colorspace) stack.callback(opj.image_destroy, image) numrows, numcols, numlayers = img_array.shape # set image offset and reference grid - image.contents.x0 = cparams.image_offset_x0 - image.contents.y0 = cparams.image_offset_y0 + image.contents.x0 = self._cparams.image_offset_x0 + image.contents.y0 = self._cparams.image_offset_y0 image.contents.x1 = image.contents.x0 \ - + (numcols - 1) * cparams.subsampling_dx + 1 + + (numcols - 1) * self._cparams.subsampling_dx + 1 image.contents.y1 = image.contents.y0 \ - + (numrows - 1) * cparams.subsampling_dy + 1 + + (numrows - 1) * self._cparams.subsampling_dy + 1 # Stage the image data to the openjpeg data structure. for k in range(0, numlayers): @@ -453,7 +448,7 @@ class Jp2k(Jp2kBox): src = layer.ctypes.data ctypes.memmove(dest, src, layer.nbytes) - cinfo = opj.create_compress(cparams.codec_fmt) + cinfo = opj.create_compress(self._cparams.codec_fmt) stack.callback(opj.destroy_compress, cinfo) # Setup the info, warning, and error handlers. @@ -467,7 +462,7 @@ class Jp2k(Jp2kBox): event_mgr.error_handler = ctypes.cast(_ERROR_CALLBACK, ctypes.c_void_p) - opj.setup_encoder(cinfo, ctypes.byref(cparams), image) + opj.setup_encoder(cinfo, ctypes.byref(self._cparams), image) cio = opj.cio_open(cinfo) stack.callback(opj.cio_close, cio) @@ -588,7 +583,7 @@ class Jp2k(Jp2kBox): self._colorspace = _COLORSPACE_MAP[colorspace.lower()] - def _write_openjp2(self, img_array, cparams, verbose=False): + def _write_openjp2(self, img_array, verbose=False): """ Write JPEG 2000 file using OpenJPEG 2.x interface. """ @@ -597,15 +592,15 @@ class Jp2k(Jp2kBox): numrows, numcols = img_array.shape img_array = img_array.reshape(numrows, numcols, 1) - comptparms = _populate_comptparms(img_array, cparams) + self._populate_comptparms(img_array) with ExitStack() as stack: - image = opj2.image_create(comptparms, self._colorspace) + image = opj2.image_create(self._comptparms, self._colorspace) stack.callback(opj2.image_destroy, image) - _populate_image_struct(cparams, image, img_array) + self._populate_image_struct(image, img_array) - codec = opj2.create_compress(cparams.codec_fmt) + codec = opj2.create_compress(self._cparams.codec_fmt) stack.callback(opj2.destroy_codec, codec) info_handler = _INFO_CALLBACK if verbose else None @@ -613,7 +608,7 @@ class Jp2k(Jp2kBox): opj2.set_warning_handler(codec, _WARNING_CALLBACK) opj2.set_error_handler(codec, _ERROR_CALLBACK) - opj2.setup_encoder(codec, cparams, image) + opj2.setup_encoder(codec, self._cparams, image) if re.match("2.0", version.openjpeg_version) is not None: fptr = libc.fopen(self.filename, 'wb') @@ -1020,13 +1015,13 @@ class Jp2k(Jp2kBox): """ self._subsampling_sanity_check() - dparameters = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef) + self._populate_dparams(rlevel, ignore_pclr_cmap_cdef) with ExitStack() as stack: try: - dparameters.decod_format = self._codec_format + self._dparams.decod_format = self._codec_format - dinfo = opj.create_decompress(dparameters.decod_format) + dinfo = opj.create_decompress(self._dparams.decod_format) event_mgr = opj.EventMgrType() info_handler = ctypes.cast(_INFO_CALLBACK, ctypes.c_void_p) @@ -1037,7 +1032,7 @@ class Jp2k(Jp2kBox): ctypes.c_void_p) opj.set_event_mgr(dinfo, ctypes.byref(event_mgr)) - opj.setup_decoder(dinfo, dparameters) + opj.setup_decoder(dinfo, self._dparams) with open(self.filename, 'rb') as fptr: src = fptr.read() @@ -1104,8 +1099,8 @@ class Jp2k(Jp2kBox): """ self._subsampling_sanity_check() - dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef, - layer=layer, tile=tile, area=area) + self._populate_dparams(rlevel, ignore_pclr_cmap_cdef, + layer=layer, tile=tile, area=area) with ExitStack() as stack: if re.match("2.1", version.openjpeg_version): @@ -1127,16 +1122,17 @@ class Jp2k(Jp2kBox): else: opj2.set_info_handler(codec, None) - opj2.setup_decoder(codec, dparam) + opj2.setup_decoder(codec, self._dparams) image = opj2.read_header(stream, codec) stack.callback(opj2.image_destroy, image) - if dparam.nb_tile_to_decode: - opj2.get_decoded_tile(codec, stream, image, dparam.tile_index) + if self._dparams.nb_tile_to_decode: + opj2.get_decoded_tile(codec, stream, image, + self._dparams.tile_index) else: opj2.set_decode_area(codec, image, - dparam.DA_x0, dparam.DA_y0, - dparam.DA_x1, dparam.DA_y1) + self._dparams.DA_x0, self._dparams.DA_y0, + self._dparams.DA_x1, self._dparams.DA_y1) opj2.decode(codec, stream, image) opj2.end_decompress(codec, stream) @@ -1147,8 +1143,8 @@ class Jp2k(Jp2kBox): return img_array - def _populate_dparam(self, rlevel, ignore_pclr_cmap_cdef, tile=None, - layer=None, area=None): + def _populate_dparams(self, rlevel, ignore_pclr_cmap_cdef, tile=None, + layer=None, area=None): """Populate decompression structure with appropriate input parameters. Parameters @@ -1165,11 +1161,6 @@ class Jp2k(Jp2kBox): ignore_pclr_cmap_cdef : bool Whether or not to ignore the pclr, cmap, or cdef boxes during any color transformation. Defaults to False. - - Returns - ------- - dparam : DecompressionParametersType (ctypes) - Corresponds to openjp2 decompression parameters structure. """ if opj2.OPENJP2 is not None: dparam = opj2.set_default_decoder_parameters() @@ -1220,7 +1211,7 @@ class Jp2k(Jp2kBox): # Return raw codestream components. dparam.flags |= 1 - return dparam + self._dparams = dparam def read_bands(self, rlevel=0, layer=0, area=None, tile=None, verbose=False, ignore_pclr_cmap_cdef=False): @@ -1268,8 +1259,8 @@ class Jp2k(Jp2kBox): "OpenJPEG installed before using this " "functionality.") - dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef, - layer=layer, tile=tile, area=area) + self._populate_dparams(rlevel, ignore_pclr_cmap_cdef, + layer=layer, tile=tile, area=area) with ExitStack() as stack: if re.match("2.1", version.openjpeg_version): @@ -1292,16 +1283,17 @@ class Jp2k(Jp2kBox): else: opj2.set_info_handler(codec, None) - opj2.setup_decoder(codec, dparam) + opj2.setup_decoder(codec, self._dparams) image = opj2.read_header(stream, codec) stack.callback(opj2.image_destroy, image) - if dparam.nb_tile_to_decode: - opj2.get_decoded_tile(codec, stream, image, dparam.tile_index) + if self._dparams.nb_tile_to_decode: + opj2.get_decoded_tile(codec, stream, image, + self._dparams.tile_index) else: opj2.set_decode_area(codec, image, - dparam.DA_x0, dparam.DA_y0, - dparam.DA_x1, dparam.DA_y1) + self._dparams.DA_x0, self._dparams.DA_y0, + self._dparams.DA_x1, self._dparams.DA_y1) opj2.decode(codec, stream, image) opj2.end_decompress(codec, stream) @@ -1362,7 +1354,83 @@ class Jp2k(Jp2kBox): return codestream + def _populate_image_struct(self, image, imgdata): + """Populates image struct needed for compression. + + Parameters + ---------- + image : ImageType(ctypes.Structure) + Corresponds to image_t type in openjp2 headers. + img_array : ndarray + Image data to be written to file. + """ + + numrows, numcols, num_comps = imgdata.shape + + # set image offset and reference grid + image.contents.x0 = self._cparams.image_offset_x0 + image.contents.y0 = self._cparams.image_offset_y0 + image.contents.x1 = (image.contents.x0 + + (numcols - 1) * self._cparams.subsampling_dx + 1) + image.contents.y1 = (image.contents.y0 + + (numrows - 1) * self._cparams.subsampling_dy + 1) + + # Stage the image data to the openjpeg data structure. + for k in range(0, num_comps): + if re.match("2.0", version.openjpeg_version) is not None: + # 2.0 API + if self._cparams.cp_cinema: + image.contents.comps[k].prec = 12 + image.contents.comps[k].bpp = 12 + else: + # 2.1 API + if self._cparams.rsiz in (core.OPJ_PROFILE_CINEMA_2K, + core.OPJ_PROFILE_CINEMA_4K): + image.contents.comps[k].prec = 12 + image.contents.comps[k].bpp = 12 + + layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32) + dest = image.contents.comps[k].data + src = layer.ctypes.data + ctypes.memmove(dest, src, layer.nbytes) + + return image + + def _populate_comptparms(self, img_array): + """Instantiate and populate comptparms structure. + + This structure defines the image components. + + Parameters + ---------- + img_array : ndarray + Image data to be written to file. + """ + # Only two precisions are possible. + if img_array.dtype == np.uint8: + comp_prec = 8 + else: + comp_prec = 16 + numrows, numcols, num_comps = img_array.shape + if version.openjpeg_version_tuple[0] == 1: + comptparms = (opj.ImageComptParmType * num_comps)() + else: + comptparms = (opj2.ImageComptParmType * num_comps)() + for j in range(num_comps): + comptparms[j].dx = self._cparams.subsampling_dx + comptparms[j].dy = self._cparams.subsampling_dy + comptparms[j].w = numcols + comptparms[j].h = numrows + comptparms[j].x0 = self._cparams.image_offset_x0 + comptparms[j].y0 = self._cparams.image_offset_y0 + comptparms[j].prec = comp_prec + comptparms[j].bpp = comp_prec + comptparms[j].sgnd = 0 + + self._comptparms = comptparms + + def _component2dtype(component): """Take an OpenJPEG component structure and determine the numpy datatype. @@ -1704,92 +1772,6 @@ def extract_image_bands(image): return data -def _populate_comptparms(img_array, cparams): - """Instantiate and populate comptparms structure. - - This structure defines the image components. - - Parameters - ---------- - img_array : ndarray - Image data to be written to file. - cparams : CompressionParametersType(ctypes.Structure) - Corresponds to cparameters_t type in openjp2 headers. - - Returns - ------- - comptparms : ImageCompType(ctypes.Structure) - Corresponds to image_comp_t type in openjp2 headers. - """ - # Only two precisions are possible. - if img_array.dtype == np.uint8: - comp_prec = 8 - else: - comp_prec = 16 - - numrows, numcols, num_comps = img_array.shape - if version.openjpeg_version_tuple[0] == 1: - comptparms = (opj.ImageComptParmType * num_comps)() - else: - comptparms = (opj2.ImageComptParmType * num_comps)() - for j in range(num_comps): - comptparms[j].dx = cparams.subsampling_dx - comptparms[j].dy = cparams.subsampling_dy - comptparms[j].w = numcols - comptparms[j].h = numrows - comptparms[j].x0 = cparams.image_offset_x0 - comptparms[j].y0 = cparams.image_offset_y0 - comptparms[j].prec = comp_prec - comptparms[j].bpp = comp_prec - comptparms[j].sgnd = 0 - - return comptparms - - -def _populate_image_struct(cparams, image, imgdata): - """Populates image struct needed for compression. - - Parameters - ---------- - cparams : CompressionParametersType(ctypes.Structure) - Corresponds to cparameters_t type in openjp2 headers. - image : ImageType(ctypes.Structure) - Corresponds to image_t type in openjp2 headers. - imgarray : ndarray - Image data to be written to file. - """ - - numrows, numcols, num_comps = imgdata.shape - - # set image offset and reference grid - image.contents.x0 = cparams.image_offset_x0 - image.contents.y0 = cparams.image_offset_y0 - image.contents.x1 = (image.contents.x0 + - (numcols - 1) * cparams.subsampling_dx + 1) - image.contents.y1 = (image.contents.y0 + - (numrows - 1) * cparams.subsampling_dy + 1) - - # Stage the image data to the openjpeg data structure. - for k in range(0, num_comps): - if re.match("2.0", version.openjpeg_version) is not None: - # 2.0 API - if cparams.cp_cinema: - image.contents.comps[k].prec = 12 - image.contents.comps[k].bpp = 12 - else: - # 2.1 API - if cparams.rsiz in (core.OPJ_PROFILE_CINEMA_2K, - core.OPJ_PROFILE_CINEMA_4K): - image.contents.comps[k].prec = 12 - image.contents.comps[k].bpp = 12 - - layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32) - dest = image.contents.comps[k].data - src = layer.ctypes.data - ctypes.memmove(dest, src, layer.nbytes) - - return image - # Setup the default callback handlers. See the callback functions subsection # in the ctypes section of the Python documentation for a solid explanation of From 0815db6c0dc3c8e5314bdc5a6ead1f4ea24b757b Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 21 Oct 2014 10:30:20 -0400 Subject: [PATCH 7/7] added test for image component parameters type --- glymur/lib/openjp2.py | 7 +++++++ glymur/lib/test/fixtures.py | 11 +++++++++++ glymur/lib/test/test_printing.py | 10 ++++++++++ 3 files changed, 28 insertions(+) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index 10b6c3d..d43d7f7 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -548,6 +548,13 @@ class ImageComptParmType(ctypes.Structure): # signed (1) / unsigned (0) ("sgnd", ctypes.c_uint32)] + def __str__(self): + msg = "{0}:\n".format(self.__class__) + for field_name, _ in self._fields_: + msg += " {0}: {1}\n".format( + field_name, getattr(self, field_name)) + return msg + class TccpInfo(ctypes.Structure): """Tile-component coding parameters information. diff --git a/glymur/lib/test/fixtures.py b/glymur/lib/test/fixtures.py index dc94e7c..4f2bbfb 100644 --- a/glymur/lib/test/fixtures.py +++ b/glymur/lib/test/fixtures.py @@ -116,3 +116,14 @@ default_compression_parameters_type = """: + dx: 0 + dy: 0 + w: 0 + h: 0 + x0: 0 + y0: 0 + prec: 0 + bpp: 0 + sgnd: 0""" diff --git a/glymur/lib/test/test_printing.py b/glymur/lib/test/test_printing.py index 5204311..73c89a6 100644 --- a/glymur/lib/test/test_printing.py +++ b/glymur/lib/test/test_printing.py @@ -54,4 +54,14 @@ class TestPrintingOpenjp2(unittest.TestCase): expected = fixtures.default_compression_parameters_type self.assertEqual(actual, expected) + def test_default_component_parameters(self): + """printing default image component parameters""" + icpt = glymur.lib.openjp2.ImageComptParmType() + with patch('sys.stdout', new=StringIO()) as fake_out: + print(icpt) + actual = fake_out.getvalue().strip() + expected = fixtures.default_image_component_parameters + self.assertEqual(actual, expected) + +