Skip to content

Commit

Permalink
Fix Issue 730: artifacts and bad compression with pixWriteJp2k()
Browse files Browse the repository at this point in the history
* Anton Tykhyy reported problems with jp2k compression.
* Comparing convert with quality=42 in ImageMagick 7.1.1, one gets a smaller
  and more accurate color image from a "featureless" input 1.6Mpixel
  image than with leptonica's pixWriteJp2k() interface.
* In addition, leptonica was not robust to input of small images with
  a requested large number of resolution levels.
* Following the lead of the ImageMagick encoder, the requested number of
  levels of resolution, nlevels, is now limited to 6 or 7.
* For images with a min dimension smaller than 31, nlevels is reduced to 5
  or less; for those between 32 and 63, nlevels is reduced to 6 if necessary.
* The number of channels (1 for gray or 3 for rgb) is now set as an input
  parameter, to avoid false colors.
* Also changed version to non-release 1.84.2.
  • Loading branch information
DanBloomberg committed Feb 4, 2024
1 parent cb91576 commit ad52063
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 34 deletions.
2 changes: 1 addition & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ PROJECT_NAME = Leptonica
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 1.84.1
PROJECT_NUMBER = 1.84.2

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
AC_PREREQ([2.69])
AC_INIT([leptonica], [1.84.1])
AC_INIT([leptonica], [1.84.2])
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADERS([config_auto.h:config/config.h.in])
AC_CONFIG_SRCDIR([src/adaptmap.c])
Expand Down
2 changes: 1 addition & 1 deletion prog/jp2kio_reg.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ PIX *pix0, *pix1;
lept_mkdir("lept/jp2k");
pix0 = pixRead(fname);
if ((fp = fopenWriteStream("/tmp/lept/jp2k/wyom.j2k", "wb+")) != NULL) {
pixWriteStreamJp2k(fp, pix0, 34, 4, L_J2K_CODEC, 0, 0);
pixWriteStreamJp2k(fp, pix0, 34, 6, L_J2K_CODEC, 0, 0);
fclose(fp);
}
pix1 = pixRead("/tmp/lept/jp2k/wyom.j2k");
Expand Down
2 changes: 1 addition & 1 deletion src/allheaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

#define LIBLEPT_MAJOR_VERSION 1
#define LIBLEPT_MINOR_VERSION 84
#define LIBLEPT_PATCH_VERSION 1
#define LIBLEPT_PATCH_VERSION 2

#include "alltypes.h"

Expand Down
2 changes: 1 addition & 1 deletion src/allheaders_top.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

#define LIBLEPT_MAJOR_VERSION 1
#define LIBLEPT_MINOR_VERSION 84
#define LIBLEPT_PATCH_VERSION 1
#define LIBLEPT_PATCH_VERSION 2

#include "alltypes.h"

Expand Down
2 changes: 1 addition & 1 deletion src/environ.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ typedef uintptr_t l_uintptr_t;
* (HAVE_LIBJP2K == 1) that is >= 2.1, set the path to the openjpeg.h *
* header in angle brackets here. *
*-----------------------------------------------------------------------*/
#define LIBJP2K_HEADER <openjpeg-2.3/openjpeg.h>
#define LIBJP2K_HEADER <openjpeg-2.5/openjpeg.h>

#endif /* ! HAVE_CONFIG_H etc. */

Expand Down
64 changes: 45 additions & 19 deletions src/jp2kio.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ PIX *pix = NULL;
* \param[in] filename
* \param[in] pix any depth, cmap is OK
* \param[in] quality SNR > 0; 0 for default (34); 100 for lossless
* \param[in] nlevels resolution levels; <= 10; default = 5
* \param[in] nlevels resolution levels; 6 or 7; use 0 for default (6)
* \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default
* \param[in] debug output callback messages, etc
* \return 0 if OK; 1 on error
Expand All @@ -453,10 +453,17 @@ PIX *pix = NULL;
* SNR = 45 (nearly lossless)
* Use 0 for default; 100 for lossless.
* (2) The %nlevels parameter is the number of resolution levels
* to be written. For example, with nlevels == 5, images with
* reduction factors of 1, 2, 4, 8 and 16 are encoded, and retrieval
* is done at the level requested when reading. For default,
* use either 5 or 0.
* to be written. Except for very small images, we allow 6 or 7.
* For example, with %nlevels == 6, images with reduction factors
* of 1, 2, 4, 8, 16 and 32 are encoded, and retrieval is done at
* the level requested when reading. For default, use either 0 or 6.
* Small images can constrain %nlevels according to
* 2^(%nlevels - 1) <= Min(w, h)
* and if necessary %nlevels will be reduced to accommodate.
* For example, images with a minimum dimension between 32 and 63
* can support %nlevels = 6, with reductions up to 32x. An image
* with a minimum dimension smaller than 32 will not support
* 6 nlevels (reductions of 1, 2, 4, 8, 16 and 32).
* (3) By default, we use the JP2 codec.
* (4) The %hint parameter is not yet in use.
* (5) For now, we only support 1 "layer" for quality.
Expand Down Expand Up @@ -497,7 +504,7 @@ FILE *fp;
* \param[in] fp file stream
* \param[in] pix any depth, cmap is OK
* \param[in] quality SNR > 0; 0 for default (34); 100 for lossless
* \param[in] nlevels <= 10
* \param[in] nlevels resolution levels; 6 or 7; use 0 for default (6)
* \param[in] codec L_JP2_CODEC or L_J2K_CODEC
* \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default
* \param[in] debug output callback messages, etc
Expand All @@ -516,8 +523,8 @@ pixWriteStreamJp2k(FILE *fp,
l_int32 hint,
l_int32 debug)
{
l_int32 w, h, d, success;
l_float32 snr;
l_int32 i, w, h, d, depth, channels, success;
l_float64 snr;
const char *opjVersion;
PIX *pixs;
opj_cparameters_t parameters; /* compression parameters */
Expand All @@ -530,21 +537,26 @@ opj_image_t *image = NULL;
if (!pix)
return ERROR_INT("pix not defined", __func__, 1);

snr = (l_float32)quality;
if (snr <= 0) snr = 34.0; /* default */
if (snr < 27)
snr = (l_float64)quality;
if (snr <= 0.0) snr = 34.0; /* default */
if (snr < 27.0)
L_WARNING("SNR = %d < 27; very low\n", __func__, (l_int32)snr);
if (snr == 100) snr = 0; /* for lossless */
if (snr > 45) {
if (snr == 100.0) snr = 0.0; /* for lossless */
if (snr > 45.0) {
L_WARNING("SNR > 45; using lossless encoding\n", __func__);
snr = 0;
snr = 0.0;
}

if (nlevels <= 0) nlevels = 5; /* default */
if (nlevels > 10) {
L_WARNING("nlevels = %d > 10; setting to 10\n", __func__, nlevels);
nlevels = 10;
if (nlevels == 0) nlevels = 6; /* default */
if (nlevels < 6) {
L_WARNING("nlevels = %d < 6; setting to 6\n", __func__, nlevels);
nlevels = 6;
}
if (nlevels > 7) {
L_WARNING("nlevels = %d > 7; setting to 7\n", __func__, nlevels);
nlevels = 7;
}

if (codec != L_JP2_CODEC && codec != L_J2K_CODEC)
return ERROR_INT("valid codec not identified\n", __func__, 1);

Expand All @@ -570,6 +582,19 @@ opj_image_t *image = NULL;
__func__);
pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
}
depth = pixGetDepth(pixs); /* 8 or 32 */

/* Reduce nlevels if the image has at least one small dimension */
for (i = 1; i < 7; i++) {
if ((w < (1 << i)) || (h < (1 << i))) {
if (i < nlevels) {
L_INFO("small image: w = %d, h = %d; setting nlevels to %d\n",
__func__, w, h, i);
nlevels = i;
}
break;
}
}

/* Convert to opj image format. */
pixSetPadBits(pixs, 0);
Expand All @@ -581,10 +606,11 @@ opj_image_t *image = NULL;
opj_set_default_encoder_parameters(&parameters);
parameters.cp_fixed_quality = 1;
parameters.cp_disto_alloc = 0;
parameters.cp_fixed_alloc = 0;
parameters.tcp_distoratio[0] = snr;
parameters.tcp_numlayers = 1;
parameters.numresolution = nlevels;
channels = (depth == 32) ? 3 : 1;
parameters.tcp_mct = (channels == 3) ? 1 : 0;

/* Create comment for codestream */
if (parameters.cp_comment == NULL) {
Expand Down
2 changes: 1 addition & 1 deletion src/makefile.static
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ LIBRARIAN_SHARED = gcc -shared
# <libname>.X, and <libname>, where Y is the minor revision number.
MAJOR_REV = 1
MINOR_REV = 84
PATCH_REV = 1
PATCH_REV = 2

#########################################################

Expand Down
2 changes: 1 addition & 1 deletion src/writefile.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ pixWriteStream(FILE *fp,
return pixWriteStreamGif(fp, pix);

case IFF_JP2:
return pixWriteStreamJp2k(fp, pix, 34, 4, L_JP2_CODEC, 0, 0);
return pixWriteStreamJp2k(fp, pix, 34, 0, L_JP2_CODEC, 0, 0);

case IFF_WEBP:
return pixWriteStreamWebP(fp, pix, 80, 0);
Expand Down
2 changes: 1 addition & 1 deletion sw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void build(Solution &s)
t += "org.sw.demo.webmproject.webp"_dep;
};

auto &leptonica = s.addTarget<LibraryTarget>("danbloomberg.leptonica", "1.84.1");
auto &leptonica = s.addTarget<LibraryTarget>("danbloomberg.leptonica", "1.84.2");
leptonica += Git("https://github.com/DanBloomberg/leptonica");

{
Expand Down
15 changes: 9 additions & 6 deletions version-notes.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,16 @@ <h2 align=center> <IMG SRC="moller52.jpg" border=1 ALIGN_MIDDLE> </h2>

<pre>

1.85 not released
Use wrapper callSystemDebug() instead of system() in programs.
* Source files changed: gplot.c, partify.c, pdfio2.c, psio2.c,
utils2.c
1.84.2 not released
* Use wrapper callSystemDebug() instead of system() in programs.
* Fixed Issue #730: artifacts and bad compression with pixWriteJp2k.
Results are now identical with ImageMagick convert (to jp2).
* Source files changed: gplot.c, jp2kio.c partify.c, pdfio2.c,
psio2.c, utils2.c, writefile.c, allheaders.h, environ.h
* Prog files changed: libre_makefigs.c, cleanpdf.c croppdf.c,
compresspdf.c, alltests_reg.c, htmlviewer.c, pdfio2_reg.c,
printimage.c, printsplitimage.c, printtiff.c, psioseg_reg.c,
compresspdf.c, alltests_reg.c, htmlviewer.c, jp2kio_reg.c,
pdfio2_reg.c, printimage.c, printsplitimage.c,
printtiff.c, psioseg_reg.c,
splitpdf.c, tiffpdftest.c, xtractprotos.c

1.84.1 Jan 3, 2024
Expand Down

0 comments on commit ad52063

Please sign in to comment.