Can write and parse Cinema2K image. #139

Using scikit-image to read the test data.  Testing still not complete.
This commit is contained in:
John Evans 2014-03-06 07:10:29 -05:00
commit afa611ae54
3 changed files with 127 additions and 10 deletions

View file

@ -10,6 +10,26 @@ RPCL = 2
PCRL = 3
CPRL = 4
STD = 0
CINEMA2K = 3
CINEMA4K = 4
RSIZ = {
'STD': STD,
'CINEMA2K': CINEMA2K,
'CINEMA4K': CINEMA4K}
OFF = 0
CINEMA2K_24 = 1
CINEMA2K_48 = 2
CINEMA4K_24 = 3
CINEMA_MODE = {
'off': OFF,
'cinema2k_24': CINEMA2K_24,
'cinema2k_48': CINEMA2K_48,
'cinema4k_24': CINEMA4K_24, }
PROGRESSION_ORDER = {
'LRCP': LRCP,
'RLCP': RLCP,

View file

@ -28,7 +28,7 @@ import numpy as np
from .codestream import Codestream
from .core import SRGB, GREYSCALE
from .core import PROGRESSION_ORDER
from .core import PROGRESSION_ORDER, RSIZ, CINEMA_MODE
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
from .jp2box import Jp2kBox
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
@ -39,6 +39,8 @@ from .lib import openjp2 as opj2
from . import version
from .lib import c as libc
CINEMA_24_CS = 1302083
JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
'uuid']
@ -220,14 +222,17 @@ class Jp2k(Jp2kBox):
cparams.cp_disto_alloc = 1
if 'cinema2K' in kwargs:
cparams.cp_rsiz = kwargs['cinema2K']
# TODO: error if either 24 or 48
cparams.cp_cinema = kwargs['cinema2K']
cparams.cp_rsiz = RSIZ['CINEMA2K']
# No tiling
cparams.tile_size_on = opj2.FALSE
cparams.cp_tdx = 1
cparams.cp_tdy = 1
# One tile part for each component.
cparams.tp_cflag = ord('C')
cparams.tp_flag = ord('C')
cparams.tp_on = 1
# tile and image shall be as (0,0)
@ -254,7 +259,7 @@ class Jp2k(Jp2kBox):
cparams.irreversible = 1
# number of layers
if cparams.tcp_layers > 1:
if cparams.tcp_numlayers > 1:
# TODO: warning or error
cparams.tcp_numlayers = 1
@ -266,8 +271,8 @@ class Jp2k(Jp2kBox):
cparams.csty |= 0x01
cparams.res_spec = cparams.numresolution - 1
for j in range(cparams.res_spec):
cparams.prcw_init[i] = 256
cparams.prch_init[i] = 256
cparams.prcw_init[j] = 256
cparams.prch_init[j] = 256
# Progression order shall be CPRL
cparams.prog_order = PROGRESSION_ORDER['CPRL']
@ -530,6 +535,29 @@ class Jp2k(Jp2kBox):
self.parse()
def _set_cinema_rate(self, cparams, image):
max_rate = 0
temp_rate = 0
cparams.cp_disto_alloc = 1
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)
if cparams.tcp_rates[0] == 0:
cparams.tcp_rates[0] = max_rate
else:
temp_rate = rate_numerator / (cparams.tcp_rates[0] * 8 * num_pixels)
if temp_rate > CINEMA_24_CS:
# TODO warning, reset
cparams.tcp_rates[0] = max_rate
else:
# TODO warning
pass
cparams.max_comp_size = COMP_24_CS
def _write_openjp2(self, img_array, verbose=False, **kwargs):
"""
Write JPEG 2000 file using OpenJPEG 2.0 interface.
@ -549,6 +577,9 @@ class Jp2k(Jp2kBox):
_populate_image_struct(cparams, image, img_array)
if 'cinema2K' in kwargs:
self._set_cinema_rate(cparams, image)
codec = opj2.create_compress(cparams.codec_fmt)
stack.callback(opj2.destroy_codec, codec)

View file

@ -12,6 +12,13 @@ import sys
import tempfile
import unittest
try:
import skimage.io
skimage.io.use_plugin('freeimage', 'imread')
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = True
except ImportError:
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = False
from .fixtures import read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG
from .fixtures import OPJ_DATA_ROOT, opj_data_file
@ -38,15 +45,74 @@ class TestSuiteWrite(unittest.TestCase):
def tearDown(self):
pass
@unittest.skip("Cannot read input image using PILLOW???")
@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):
relfile = 'input/nonregression/X_4_2K_24_185_CBR_WB_000.tif'
import pdb; pdb.set_trace()
infile = opj_data_file(relfile)
data = read_image(infile)
data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
j.write(data, 'cinema2K', 24)
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),
(1998, 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),
(1998, 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.CRLP)
self.assertEqual(codestream.segment[2].layers, 3) # layers = 3
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)
def test_NR_ENC_Bretagne1_ppm_1_encode(self):
"""NR-ENC-Bretagne1.ppm-1-encode"""