Skip to content

Commit

Permalink
Merge pull request #5500 from scaramallion/dev-j2k
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk committed Apr 1, 2022
2 parents 78c389e + e0de5b6 commit 703f54c
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 14 deletions.
43 changes: 43 additions & 0 deletions Tests/test_file_jpeg2k.py
Expand Up @@ -209,6 +209,49 @@ def test_layers():
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():
# Arrange
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
Expand Down
22 changes: 19 additions & 3 deletions docs/handbook/image-file-formats.rst
Expand Up @@ -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.

**irreversible**
If ``True``, use the lossy Irreversible Color Transformation
followed by DWT 9-7. Defaults to ``False``, which means to use the
Reversible Color Transformation with DWT 5-3.
If ``True``, use the lossy discrete waveform transformation DWT 9-7.
Defaults to ``False``, which uses the lossless 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**
Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``,
Expand All @@ -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
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::

To enable JPEG 2000 support, you need to build and install the OpenJPEG
Expand Down
24 changes: 18 additions & 6 deletions docs/releasenotes/9.1.0.rst
Expand Up @@ -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
breaking backwards compatibility.

Added rawmode argument to Image.getpalette()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

By default, :py:meth:`~PIL.Image.Image.getpalette` returns RGB data from the palette.
A ``rawmode`` argument has been added, to allow the mode to be chosen instead. ``None``
can be used to return data in the current mode of the palette.
Added mct and no_jp2 options for saving JPEG 2000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The :py:meth:`PIL.Image.Image.save` method now supports the following options for
JPEG 2000:

**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
^^^^^^^^^^^^^^^
Expand Down
10 changes: 6 additions & 4 deletions src/PIL/Jpeg2KImagePlugin.py
Expand Up @@ -290,14 +290,14 @@ def _accept(prefix):


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"
else:
kind = "jp2"

# Get the keyword arguments
info = im.encoderinfo

offset = info.get("offset", None)
tile_offset = info.get("tile_offset", None)
tile_size = info.get("tile_size", None)
Expand All @@ -320,6 +320,7 @@ def _save(im, fp, filename):
irreversible = info.get("irreversible", False)
progression = info.get("progression", "LRCP")
cinema_mode = info.get("cinema_mode", "no")
mct = info.get("mct", 0)
fd = -1

if hasattr(fp, "fileno"):
Expand All @@ -340,6 +341,7 @@ def _save(im, fp, filename):
irreversible,
progression,
cinema_mode,
mct,
fd,
)

Expand Down
5 changes: 4 additions & 1 deletion src/encode.c
Expand Up @@ -1187,11 +1187,12 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
OPJ_PROG_ORDER prog_order;
char *cinema_mode = "no";
OPJ_CINEMA_MODE cine_mode;
char mct = 0;
Py_ssize_t fd = -1;

if (!PyArg_ParseTuple(
args,
"ss|OOOsOnOOOssn",
"ss|OOOsOnOOOssbn",
&mode,
&format,
&offset,
Expand All @@ -1205,6 +1206,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
&irreversible,
&progression,
&cinema_mode,
&mct,
&fd)) {
return NULL;
}
Expand Down Expand Up @@ -1302,6 +1304,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
context->irreversible = PyObject_IsTrue(irreversible);
context->progression = prog_order;
context->cinema_mode = cine_mode;
context->mct = mct;

return (PyObject *)encoder;
}
Expand Down
3 changes: 3 additions & 0 deletions src/libImaging/Jpeg2K.h
Expand Up @@ -82,6 +82,9 @@ typedef struct {
/* Compression style */
int irreversible;

/* Set multiple component transformation */
char mct;

/* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */
OPJ_PROG_ORDER progression;

Expand Down
3 changes: 3 additions & 0 deletions src/libImaging/Jpeg2KEncode.c
Expand Up @@ -435,6 +435,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
}

params.irreversible = context->irreversible;
if (components == 3) {
params.tcp_mct = context->mct;
}

params.prog_order = context->progression;

Expand Down

0 comments on commit 703f54c

Please sign in to comment.