Can write and parse Cinema2K image. #139
Using scikit-image to read the test data. Testing still not complete.
This commit is contained in:
parent
b6a17d60f1
commit
afa611ae54
3 changed files with 127 additions and 10 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue