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
|
PCRL = 3
|
||||||
CPRL = 4
|
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 = {
|
PROGRESSION_ORDER = {
|
||||||
'LRCP': LRCP,
|
'LRCP': LRCP,
|
||||||
'RLCP': RLCP,
|
'RLCP': RLCP,
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import numpy as np
|
||||||
|
|
||||||
from .codestream import Codestream
|
from .codestream import Codestream
|
||||||
from .core import SRGB, GREYSCALE
|
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 .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
|
||||||
from .jp2box import Jp2kBox
|
from .jp2box import Jp2kBox
|
||||||
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
|
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
|
||||||
|
|
@ -39,6 +39,8 @@ from .lib import openjp2 as opj2
|
||||||
from . import version
|
from . import version
|
||||||
from .lib import c as libc
|
from .lib import c as libc
|
||||||
|
|
||||||
|
CINEMA_24_CS = 1302083
|
||||||
|
|
||||||
JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
|
JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
|
||||||
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
|
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
|
||||||
'uuid']
|
'uuid']
|
||||||
|
|
@ -220,14 +222,17 @@ class Jp2k(Jp2kBox):
|
||||||
cparams.cp_disto_alloc = 1
|
cparams.cp_disto_alloc = 1
|
||||||
|
|
||||||
if 'cinema2K' in kwargs:
|
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
|
# No tiling
|
||||||
cparams.tile_size_on = opj2.FALSE
|
cparams.tile_size_on = opj2.FALSE
|
||||||
cparams.cp_tdx = 1
|
cparams.cp_tdx = 1
|
||||||
cparams.cp_tdy = 1
|
cparams.cp_tdy = 1
|
||||||
|
|
||||||
# One tile part for each component.
|
# One tile part for each component.
|
||||||
cparams.tp_cflag = ord('C')
|
cparams.tp_flag = ord('C')
|
||||||
cparams.tp_on = 1
|
cparams.tp_on = 1
|
||||||
|
|
||||||
# tile and image shall be as (0,0)
|
# tile and image shall be as (0,0)
|
||||||
|
|
@ -254,7 +259,7 @@ class Jp2k(Jp2kBox):
|
||||||
cparams.irreversible = 1
|
cparams.irreversible = 1
|
||||||
|
|
||||||
# number of layers
|
# number of layers
|
||||||
if cparams.tcp_layers > 1:
|
if cparams.tcp_numlayers > 1:
|
||||||
# TODO: warning or error
|
# TODO: warning or error
|
||||||
cparams.tcp_numlayers = 1
|
cparams.tcp_numlayers = 1
|
||||||
|
|
||||||
|
|
@ -266,8 +271,8 @@ class Jp2k(Jp2kBox):
|
||||||
cparams.csty |= 0x01
|
cparams.csty |= 0x01
|
||||||
cparams.res_spec = cparams.numresolution - 1
|
cparams.res_spec = cparams.numresolution - 1
|
||||||
for j in range(cparams.res_spec):
|
for j in range(cparams.res_spec):
|
||||||
cparams.prcw_init[i] = 256
|
cparams.prcw_init[j] = 256
|
||||||
cparams.prch_init[i] = 256
|
cparams.prch_init[j] = 256
|
||||||
|
|
||||||
# Progression order shall be CPRL
|
# Progression order shall be CPRL
|
||||||
cparams.prog_order = PROGRESSION_ORDER['CPRL']
|
cparams.prog_order = PROGRESSION_ORDER['CPRL']
|
||||||
|
|
@ -530,6 +535,29 @@ class Jp2k(Jp2kBox):
|
||||||
self.parse()
|
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):
|
def _write_openjp2(self, img_array, verbose=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Write JPEG 2000 file using OpenJPEG 2.0 interface.
|
Write JPEG 2000 file using OpenJPEG 2.0 interface.
|
||||||
|
|
@ -549,6 +577,9 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
_populate_image_struct(cparams, image, img_array)
|
_populate_image_struct(cparams, image, img_array)
|
||||||
|
|
||||||
|
if 'cinema2K' in kwargs:
|
||||||
|
self._set_cinema_rate(cparams, image)
|
||||||
|
|
||||||
codec = opj2.create_compress(cparams.codec_fmt)
|
codec = opj2.create_compress(cparams.codec_fmt)
|
||||||
stack.callback(opj2.destroy_codec, codec)
|
stack.callback(opj2.destroy_codec, codec)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
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 read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
||||||
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||||
|
|
||||||
|
|
@ -38,15 +45,74 @@ class TestSuiteWrite(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
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):
|
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'
|
relfile = 'input/nonregression/X_4_2K_24_185_CBR_WB_000.tif'
|
||||||
import pdb; pdb.set_trace()
|
|
||||||
infile = opj_data_file(relfile)
|
infile = opj_data_file(relfile)
|
||||||
data = read_image(infile)
|
data = skimage.io.imread(infile)
|
||||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
j = Jp2k(tfile.name, 'wb')
|
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):
|
def test_NR_ENC_Bretagne1_ppm_1_encode(self):
|
||||||
"""NR-ENC-Bretagne1.ppm-1-encode"""
|
"""NR-ENC-Bretagne1.ppm-1-encode"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue