Merge pull request #5500 from scaramallion/dev-j2k

This commit is contained in:
Hugo van Kemenade 2022-04-01 09:39:07 +03:00 committed by GitHub
commit 703f54c847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 13 deletions

View File

@ -209,6 +209,49 @@ def test_layers():
assert_image_similar(im, test_card, 0.4) assert_image_similar(im, test_card, 0.4)
@pytest.mark.parametrize(
"name, args, offset, data",
(
("foo.j2k", {}, 0, b"\xff\x4f"),
("foo.jp2", {}, 4, b"jP"),
(None, {"no_jp2": True}, 0, b"\xff\x4f"),
("foo.j2k", {"no_jp2": True}, 0, b"\xff\x4f"),
("foo.jp2", {"no_jp2": True}, 0, b"\xff\x4f"),
("foo.j2k", {"no_jp2": False}, 0, b"\xff\x4f"),
("foo.jp2", {"no_jp2": False}, 4, b"jP"),
("foo.jp2", {"no_jp2": False}, 4, b"jP"),
),
)
def test_no_jp2(name, args, offset, data):
out = BytesIO()
if name:
out.name = name
test_card.save(out, "JPEG2000", **args)
out.seek(offset)
assert out.read(2) == data
def test_mct():
# Three component
for val in (0, 1):
out = BytesIO()
test_card.save(out, "JPEG2000", mct=val, no_jp2=True)
assert out.getvalue()[59] == val
with Image.open(out) as im:
assert_image_similar(im, test_card, 1.0e-3)
# Single component should have MCT disabled
for val in (0, 1):
out = BytesIO()
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
jp2.save(out, "JPEG2000", mct=val, no_jp2=True)
assert out.getvalue()[53] == 0
with Image.open(out) as im:
assert_image_similar(im, jp2, 1.0e-3)
def test_rgba(): def test_rgba():
# Arrange # Arrange
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k: with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:

View File

@ -504,9 +504,18 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
and must be greater than the code-block size. and must be greater than the code-block size.
**irreversible** **irreversible**
If ``True``, use the lossy Irreversible Color Transformation If ``True``, use the lossy discrete waveform transformation DWT 9-7.
followed by DWT 9-7. Defaults to ``False``, which means to use the Defaults to ``False``, which uses the lossless DWT 5-3.
Reversible Color Transformation with DWT 5-3.
**mct**
If ``1`` then enable multiple component transformation when encoding,
otherwise use ``0`` for no component transformation (default). If MCT is
enabled and ``irreversible`` is ``True`` then the Irreversible Color
Transformation will be applied, otherwise encoding will use the
Reversible Color Transformation. MCT works best with a ``mode`` of
``RGB`` and is only applicable when the image data has 3 components.
.. versionadded:: 9.1.0
**progression** **progression**
Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``,
@ -526,6 +535,13 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
for compliant 4K files, *at least one* of the dimensions must match for compliant 4K files, *at least one* of the dimensions must match
4096 x 2160. 4096 x 2160.
**no_jp2**
If ``True`` then don't wrap the raw codestream in the JP2 file format when
saving, otherwise the extension of the filename will be used to determine
the format (default).
.. versionadded:: 9.1.0
.. note:: .. note::
To enable JPEG 2000 support, you need to build and install the OpenJPEG To enable JPEG 2000 support, you need to build and install the OpenJPEG

View File

@ -146,12 +146,24 @@ At present, the information within each block is merely returned as a dictionary
"data" entry. This will allow more useful information to be added in the future without "data" entry. This will allow more useful information to be added in the future without
breaking backwards compatibility. breaking backwards compatibility.
Added rawmode argument to Image.getpalette() Added mct and no_jp2 options for saving JPEG 2000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, :py:meth:`~PIL.Image.Image.getpalette` returns RGB data from the palette. The :py:meth:`PIL.Image.Image.save` method now supports the following options for
A ``rawmode`` argument has been added, to allow the mode to be chosen instead. ``None`` JPEG 2000:
can be used to return data in the current mode of the palette.
**mct**
If ``1`` then enable multiple component transformation when encoding,
otherwise use ``0`` for no component transformation (default). If MCT is
enabled and ``irreversible`` is ``True`` then the Irreversible Color
Transformation will be applied, otherwise encoding will use the
Reversible Color Transformation. MCT works best with a ``mode`` of
``RGB`` and is only applicable when the image data has 3 components.
**no_jp2**
If ``True`` then don't wrap the raw codestream in the JP2 file format when
saving, otherwise the extension of the filename will be used to determine
the format (default).
Added PyEncoder Added PyEncoder
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -290,14 +290,14 @@ def _accept(prefix):
def _save(im, fp, filename): def _save(im, fp, filename):
if filename.endswith(".j2k"): # Get the keyword arguments
info = im.encoderinfo
if filename.endswith(".j2k") or info.get("no_jp2", False):
kind = "j2k" kind = "j2k"
else: else:
kind = "jp2" kind = "jp2"
# Get the keyword arguments
info = im.encoderinfo
offset = info.get("offset", None) offset = info.get("offset", None)
tile_offset = info.get("tile_offset", None) tile_offset = info.get("tile_offset", None)
tile_size = info.get("tile_size", None) tile_size = info.get("tile_size", None)
@ -320,6 +320,7 @@ def _save(im, fp, filename):
irreversible = info.get("irreversible", False) irreversible = info.get("irreversible", False)
progression = info.get("progression", "LRCP") progression = info.get("progression", "LRCP")
cinema_mode = info.get("cinema_mode", "no") cinema_mode = info.get("cinema_mode", "no")
mct = info.get("mct", 0)
fd = -1 fd = -1
if hasattr(fp, "fileno"): if hasattr(fp, "fileno"):
@ -340,6 +341,7 @@ def _save(im, fp, filename):
irreversible, irreversible,
progression, progression,
cinema_mode, cinema_mode,
mct,
fd, fd,
) )

View File

@ -1187,11 +1187,12 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
OPJ_PROG_ORDER prog_order; OPJ_PROG_ORDER prog_order;
char *cinema_mode = "no"; char *cinema_mode = "no";
OPJ_CINEMA_MODE cine_mode; OPJ_CINEMA_MODE cine_mode;
char mct = 0;
Py_ssize_t fd = -1; Py_ssize_t fd = -1;
if (!PyArg_ParseTuple( if (!PyArg_ParseTuple(
args, args,
"ss|OOOsOnOOOssn", "ss|OOOsOnOOOssbn",
&mode, &mode,
&format, &format,
&offset, &offset,
@ -1205,6 +1206,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
&irreversible, &irreversible,
&progression, &progression,
&cinema_mode, &cinema_mode,
&mct,
&fd)) { &fd)) {
return NULL; return NULL;
} }
@ -1302,6 +1304,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
context->irreversible = PyObject_IsTrue(irreversible); context->irreversible = PyObject_IsTrue(irreversible);
context->progression = prog_order; context->progression = prog_order;
context->cinema_mode = cine_mode; context->cinema_mode = cine_mode;
context->mct = mct;
return (PyObject *)encoder; return (PyObject *)encoder;
} }

View File

@ -82,6 +82,9 @@ typedef struct {
/* Compression style */ /* Compression style */
int irreversible; int irreversible;
/* Set multiple component transformation */
char mct;
/* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */ /* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */
OPJ_PROG_ORDER progression; OPJ_PROG_ORDER progression;

View File

@ -435,6 +435,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
} }
params.irreversible = context->irreversible; params.irreversible = context->irreversible;
if (components == 3) {
params.tcp_mct = context->mct;
}
params.prog_order = context->progression; params.prog_order = context->progression;