From 80326718a595fbfad9f93b6d8975801cd98908a2 Mon Sep 17 00:00:00 2001 From: jevans Date: Thu, 6 Mar 2014 21:13:40 -0500 Subject: [PATCH] Finished Cinema2K upstream tests. #139 --- CHANGES.txt | 3 +- docs/source/changelog.rst | 3 +- glymur/jp2k.py | 25 ++++-- glymur/test/test_opj_suite_write.py | 123 ++++++++++++++++++++++++---- 4 files changed, 127 insertions(+), 27 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 24d9616..c906d29 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ -Feb 09, 2014 - Changed constructor for ChannelDefinition box. Removed support +Mar 06, 2014 - Added Cinema2K write support. + Changed constructor for ChannelDefinition box. Removed support for Python 2.6. Added write support for JP2 UUID, DataEntryURL, Palette and Component Mapping boxes, JPX Association, NumberList and DataReference boxes. Added read support for JPX free, diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index bbba98e..320ff0f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -5,10 +5,11 @@ ChangeLog 0.6.0 (pending) =============== + * Added Cinema2K write support. * Added lxml requirement. * added set_printoptions, get_printoptions function * dropped support for Python 2.6, added support for Python 3.4 - * dropped windows support + * dropped windows support (it might work, it might not, I don't much care) * added write support for JP2 UUID, dataEntryURL, palette, and component mapping boxes * added read/write support for JPX free, number list, and data reference boxes * Added read support for JPX fragment list and fragment table boxes diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 255e2be..627082b 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -163,9 +163,12 @@ class Jp2k(Jp2kBox): fps : int Frames per second, should be either 24 or 48. """ - if fps not in [24, 48]: + if fps == 24: + 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.') - cparams.cp_cinema = fps cparams.cp_rsiz = RSIZ['CINEMA2K'] # No tiling @@ -205,7 +208,7 @@ class Jp2k(Jp2kBox): # TODO: warning or error cparams.tcp_numlayers = 1 - if cparams.numresolution > 6: + if cparams.numresolution > 6: #TODO only for cinema2k # TODO: warning or error cparams.numresolution = 6 @@ -219,7 +222,7 @@ class Jp2k(Jp2kBox): # Progression order shall be CPRL cparams.prog_order = PROGRESSION_ORDER['CPRL'] - # progression order changes not allowed for 2K + # progression order changes not allowed for 2K # TODO not for 4K: cparams.numpocs = 0 def _populate_cparams(self, **kwargs): @@ -554,8 +557,12 @@ class Jp2k(Jp2kBox): if cparams.cp_cinema in [CINEMA_MODE['cinema2k_24'], CINEMA_MODE['cinema2k_48']]: num_pixels = image.contents.comps[0].w * image.contents.comps[0].h - rate_numerator = num_pixels * image.contents.comps[0].prec - max_rate = rate_numerator / (CINEMA_24_CS * 8 * num_pixels) + num_samples = num_pixels * image.contents.numcomps + rate_numerator = num_samples * image.contents.comps[0].prec + rate_denominator = CINEMA_24_CS * 8 + rate_denominator *= image.contents.comps[0].dx + rate_denominator *= image.contents.comps[0].dy + max_rate = rate_numerator / rate_denominator if cparams.tcp_rates[0] == 0: cparams.tcp_rates[0] = max_rate else: @@ -567,7 +574,7 @@ class Jp2k(Jp2kBox): # TODO warning pass - cparams.max_comp_size = COMP_24_CS + cparams.max_comp_size = CINEMA_24_CS def _write_openjp2(self, img_array, verbose=False, **kwargs): """ @@ -1608,6 +1615,10 @@ def _populate_image_struct(cparams, image, imgdata): # Stage the image data to the openjpeg data structure. for k in range(0, num_comps): + if cparams.cp_cinema: + 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 diff --git a/glymur/test/test_opj_suite_write.py b/glymur/test/test_opj_suite_write.py index 21c2578..ca3bc83 100644 --- a/glymur/test/test_opj_suite_write.py +++ b/glymur/test/test_opj_suite_write.py @@ -44,6 +44,108 @@ class TestSuiteWrite(unittest.TestCase): def tearDown(self): pass + @unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT, + "Cannot read input image without scikit-image/freeimage") + def test_NR_ENC_X_6_2K_24_FULL_CBR_CIRCLE_000_tif_17_encode(self): + relfile = 'input/nonregression/X_6_2K_24_FULL_CBR_CIRCLE_000.tif' + infile = opj_data_file(relfile) + data = skimage.io.imread(infile) + with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + j.write(data, cinema2k=24) + + codestream = j.get_codestream() + + # SIZ: Image and tile size + # Profile: "3" means cinema2K + self.assertEqual(codestream.segment[1].rsiz, 3) + # Reference grid size + self.assertEqual((codestream.segment[1].xsiz, + codestream.segment[1].ysiz), + (2048, 1080)) + # Reference grid offset + self.assertEqual((codestream.segment[1].xosiz, + codestream.segment[1].yosiz), (0, 0)) + # Tile size + self.assertEqual((codestream.segment[1].xtsiz, + codestream.segment[1].ytsiz), + (2048, 1080)) + # Tile offset + self.assertEqual((codestream.segment[1].xtosiz, + codestream.segment[1].ytosiz), + (0, 0)) + # bitdepth + self.assertEqual(codestream.segment[1].bitdepth, (12, 12, 12)) + # signed + self.assertEqual(codestream.segment[1].signed, + (False, False, False)) + # subsampling + self.assertEqual(list(zip(codestream.segment[1].xrsiz, + codestream.segment[1].yrsiz)), + [(1, 1)] * 3) + + # COD: Coding style default + self.assertFalse(codestream.segment[2].scod & 2) # no sop + self.assertFalse(codestream.segment[2].scod & 4) # no eph + self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CPRL) + self.assertEqual(codestream.segment[2].layers, 1) + self.assertEqual(codestream.segment[2].spcod[3], 1) # mct + self.assertEqual(codestream.segment[2].spcod[4], 5) # levels + self.assertEqual(tuple(codestream.segment[2].code_block_size), + (32, 32)) # cblksz + + + @unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT, + "Cannot read input image without scikit-image/freeimage") + def test_NR_ENC_X_5_2K_24_235_CBR_STEM24_000_tif_16_encode(self): + relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif' + infile = opj_data_file(relfile) + data = skimage.io.imread(infile) + with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + j.write(data, cinema2k=24) + + codestream = j.get_codestream() + + # SIZ: Image and tile size + # Profile: "3" means cinema2K + self.assertEqual(codestream.segment[1].rsiz, 3) + # Reference grid size + self.assertEqual((codestream.segment[1].xsiz, + codestream.segment[1].ysiz), + (2048, 857)) + # Reference grid offset + self.assertEqual((codestream.segment[1].xosiz, + codestream.segment[1].yosiz), (0, 0)) + # Tile size + self.assertEqual((codestream.segment[1].xtsiz, + codestream.segment[1].ytsiz), + (2048, 857)) + # Tile offset + self.assertEqual((codestream.segment[1].xtosiz, + codestream.segment[1].ytosiz), + (0, 0)) + # bitdepth + self.assertEqual(codestream.segment[1].bitdepth, (12, 12, 12)) + # signed + self.assertEqual(codestream.segment[1].signed, + (False, False, False)) + # subsampling + self.assertEqual(list(zip(codestream.segment[1].xrsiz, + codestream.segment[1].yrsiz)), + [(1, 1)] * 3) + + # COD: Coding style default + self.assertFalse(codestream.segment[2].scod & 2) # no sop + self.assertFalse(codestream.segment[2].scod & 4) # no eph + self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CPRL) + self.assertEqual(codestream.segment[2].layers, 1) + self.assertEqual(codestream.segment[2].spcod[3], 1) # mct + self.assertEqual(codestream.segment[2].spcod[4], 5) # levels + self.assertEqual(tuple(codestream.segment[2].code_block_size), + (32, 32)) # cblksz + + @unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT, "Cannot read input image without scikit-image/freeimage") def test_NR_ENC_X_4_2K_24_185_CBR_WB_000_tif_15_encode(self): @@ -87,27 +189,12 @@ class TestSuiteWrite(unittest.TestCase): # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop self.assertFalse(codestream.segment[2].scod & 4) # no eph - self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CRLP) - self.assertEqual(codestream.segment[2].layers, 3) # layers = 3 + self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CPRL) + self.assertEqual(codestream.segment[2].layers, 1) self.assertEqual(codestream.segment[2].spcod[3], 1) # mct self.assertEqual(codestream.segment[2].spcod[4], 5) # levels self.assertEqual(tuple(codestream.segment[2].code_block_size), - (64, 64)) # cblksz - # Selective arithmetic coding bypass - self.assertFalse(codestream.segment[2].spcod[7] & 0x01) - # Reset context probabilities - self.assertFalse(codestream.segment[2].spcod[7] & 0x02) - # Termination on each coding pass - self.assertFalse(codestream.segment[2].spcod[7] & 0x04) - # Vertically causal context - self.assertFalse(codestream.segment[2].spcod[7] & 0x08) - # Predictable termination - self.assertFalse(codestream.segment[2].spcod[7] & 0x0010) - # Segmentation symbols - self.assertFalse(codestream.segment[2].spcod[7] & 0x0020) - self.assertEqual(codestream.segment[2].spcod[8], - glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) - self.assertEqual(len(codestream.segment[2].spcod), 9) + (32, 32)) # cblksz @unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT,