TurboJPEG: Add restart markers when transforming

This commit is contained in:
DRC
2024-09-01 15:07:27 -04:00
parent a66398fee8
commit 37851a32c0
9 changed files with 112 additions and 44 deletions

View File

@@ -60,6 +60,10 @@ djpeg, and jpegtran.)
`TJDecompressor.saveImage()`) have been added to the Java API. These methods
work similarly to the `tj3LoadImage*()` and `tj3SaveImage*()` functions in the
C API.
- The TurboJPEG lossless transformation function and methods now add
restart markers to all destination images if
`TJPARAM_RESTARTBLOCKS`/`TJ.PARAM_RESTARTBLOCKS` or
`TJPARAM_RESTARTROWS`/`TJ.PARAM_RESTARTROWS` is set.
8. TJExample has been replaced with three programs (TJComp, TJDecomp, and
TJTran) that demonstrate how to approximate the functionality of cjpeg, djpeg,

View File

@@ -554,6 +554,8 @@ final class TJBench {
tjt.set(TJ.PARAM_FASTUPSAMPLE, fastUpsample ? 1 : 0);
tjt.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0);
tjt.set(TJ.PARAM_SCANLIMIT, maxScans);
tjt.set(TJ.PARAM_RESTARTBLOCKS, restartIntervalBlocks);
tjt.set(TJ.PARAM_RESTARTROWS, restartIntervalRows);
tjt.set(TJ.PARAM_MAXMEMORY, maxMemory);
tjt.set(TJ.PARAM_MAXPIXELS, maxPixels);
@@ -808,7 +810,7 @@ final class TJBench {
System.out.println("-quiet");
System.out.println(" Output results in tabular rather than verbose format");
System.out.println("-restart N");
System.out.println(" When compressing, add a restart marker every N MCU rows");
System.out.println(" When compressing or transforming, add a restart marker every N MCU rows");
System.out.println(" [default = 0 (no restart markers)]. Append 'B' to specify the restart");
System.out.println(" marker interval in MCUs (lossy only.)");
System.out.println("-strict");

View File

@@ -32,7 +32,6 @@
* functionality of the IJG's jpegtran program. jpegtran features that are not
* covered:
*
* - Adding restart markers to the output image
* - Scan scripts
* - Expanding the input image when cropping
* - Wiping a region of the input image
@@ -113,6 +112,9 @@ final class TJTran {
System.out.println(" Create a progressive output image instead of a single-scan output image");
System.out.println(" (can be combined with -arithmetic; implies -optimize unless -arithmetic is");
System.out.println(" also specified)");
System.out.println("-restart N");
System.out.println(" Add a restart marker every N MCU rows [default = 0 (no restart markers)].");
System.out.println(" Append 'B' to specify the restart marker interval in MCUs.");
System.out.println("-trim");
System.out.println(" If necessary, trim the partial iMCUs at the right or bottom edge of the");
System.out.println(" image to make the requested transform perfect\n");
@@ -142,7 +144,8 @@ final class TJTran {
int i;
int arithmetic = 0, maxMemory = -1, maxScans = -1, optimize = -1,
progressive = 0, subsamp;
progressive = 0, restartIntervalBlocks = -1, restartIntervalRows = -1,
subsamp;
TJTransform[] xform = new TJTransform[1];
xform[0] = new TJTransform();
byte[] srcBuf;
@@ -212,7 +215,7 @@ final class TJTran {
xform[0].options |= TJTransform.OPT_PERFECT;
else if (matchArg(argv[i], "-progressive", 2))
progressive = 1;
else if (matchArg(argv[i], "-rotate", 2) && i < argv.length - 1) {
else if (matchArg(argv[i], "-rotate", 3) && i < argv.length - 1) {
i++;
if (matchArg(argv[i], "90", 2))
xform[0].op = TJTransform.OP_ROT90;
@@ -222,6 +225,21 @@ final class TJTran {
xform[0].op = TJTransform.OP_ROT270;
else
usage();
} else if (matchArg(argv[i], "-restart", 2) && i < argv.length - 1) {
int temp = -1;
String arg = argv[++i];
Scanner scanner = new Scanner(arg).useDelimiter("b|B");
try {
temp = scanner.nextInt();
} catch (Exception e) {}
if (temp < 0 || temp > 65535 || scanner.hasNext())
usage();
if (arg.endsWith("B") || arg.endsWith("b"))
restartIntervalBlocks = temp;
else
restartIntervalRows = temp;
} else if (matchArg(argv[i], "-transverse", 7))
xform[0].op = TJTransform.OP_TRANSVERSE;
else if (matchArg(argv[i], "-trim", 4))
@@ -240,6 +258,10 @@ final class TJTran {
tjt.set(TJ.PARAM_OPTIMIZE, optimize);
if (maxScans >= 0)
tjt.set(TJ.PARAM_SCANLIMIT, maxScans);
if (restartIntervalBlocks >= 0)
tjt.set(TJ.PARAM_RESTARTBLOCKS, restartIntervalBlocks);
if (restartIntervalRows >= 0)
tjt.set(TJ.PARAM_RESTARTROWS, restartIntervalRows);
if (maxMemory >= 0)
tjt.set(TJ.PARAM_MAXMEMORY, maxMemory);

View File

@@ -670,6 +670,10 @@ static int decompTest(char *fileName)
THROW_TJ();
if (tj3Set(handle, TJPARAM_SCANLIMIT, maxScans) == -1)
THROW_TJ();
if (tj3Set(handle, TJPARAM_RESTARTBLOCKS, restartIntervalBlocks) == -1)
THROW_TJ();
if (tj3Set(handle, TJPARAM_RESTARTROWS, restartIntervalRows) == -1)
THROW_TJ();
if (tj3Set(handle, TJPARAM_MAXMEMORY, maxMemory) == -1)
THROW_TJ();
if (tj3Set(handle, TJPARAM_MAXPIXELS, maxPixels) == -1)
@@ -949,7 +953,7 @@ static void usage(char *progName)
printf("-quiet\n");
printf(" Output results in tabular rather than verbose format\n");
printf("-restart N\n");
printf(" When compressing, add a restart marker every N MCU rows\n");
printf(" When compressing or transforming, add a restart marker every N MCU rows\n");
printf(" [default = 0 (no restart markers)]. Append 'B' to specify the restart\n");
printf(" marker interval in MCUs (lossy only.)\n");
printf("-strict\n");

View File

@@ -32,7 +32,6 @@
* functionality of the IJG's jpegtran program. jpegtran features that are not
* covered:
*
* - Adding restart markers to the output image
* - Scan scripts
* - Expanding the input image when cropping
* - Wiping a region of the input image
@@ -136,6 +135,9 @@ static void usage(char *programName)
printf(" Create a progressive output image instead of a single-scan output image\n");
printf(" (can be combined with -arithmetic; implies -optimize unless -arithmetic is\n");
printf(" also specified)\n");
printf("-restart N\n");
printf(" Add a restart marker every N MCU rows [default = 0 (no restart markers)].\n");
printf(" Append 'B' to specify the restart marker interval in MCUs.\n");
printf("-strict\n");
printf(" Treat all warnings as fatal; abort immediately if incomplete or corrupt\n");
printf(" data is encountered in the input image, rather than trying to salvage the\n");
@@ -152,7 +154,8 @@ int main(int argc, char **argv)
{
int i, retval = 0;
int arithmetic = 0, maxMemory = -1, maxScans = -1, optimize = -1,
progressive = 0, stopOnWarning = -1, subsamp;
progressive = 0, restartIntervalBlocks = -1, restartIntervalRows = -1,
stopOnWarning = -1, subsamp;
tjtransform xform;
tjhandle tjInstance = NULL;
FILE *jpegFile = NULL;
@@ -208,7 +211,7 @@ int main(int argc, char **argv)
xform.options |= TJXOPT_PERFECT;
else if (MATCH_ARG(argv[i], "-progressive", 2))
progressive = 1;
else if (MATCH_ARG(argv[i], "-rotate", 2) && i < argc - 1) {
else if (MATCH_ARG(argv[i], "-rotate", 3) && i < argc - 1) {
i++;
if (MATCH_ARG(argv[i], "90", 2))
xform.op = TJXOP_ROT90;
@@ -218,6 +221,18 @@ int main(int argc, char **argv)
xform.op = TJXOP_ROT270;
else
usage(argv[0]);
} else if (MATCH_ARG(argv[i], "-restart", 2) && i < argc - 1) {
int tempi = -1, nscan; char tempc = 0;
if ((nscan = sscanf(argv[++i], "%d%c", &tempi, &tempc)) < 1 ||
tempi < 0 || tempi > 65535 ||
(nscan == 2 && tempc != 'B' && tempc != 'b'))
usage(argv[0]);
if (tempc == 'B' || tempc == 'b')
restartIntervalBlocks = tempi;
else
restartIntervalRows = tempi;
} else if (MATCH_ARG(argv[i], "-strict", 2))
stopOnWarning = 1;
else if (MATCH_ARG(argv[i], "-transverse", 7))
@@ -242,6 +257,12 @@ int main(int argc, char **argv)
THROW_TJ("setting TJPARAM_OPTIMIZE");
if (maxScans >= 0 && tj3Set(tjInstance, TJPARAM_SCANLIMIT, maxScans) < 0)
THROW_TJ("setting TJPARAM_SCANLIMIT");
if (restartIntervalBlocks >= 0 &&
tj3Set(tjInstance, TJPARAM_RESTARTBLOCKS, restartIntervalBlocks) < 0)
THROW_TJ("setting TJPARAM_RESTARTBLOCKS");
if (restartIntervalRows >= 0 &&
tj3Set(tjInstance, TJPARAM_RESTARTROWS, restartIntervalRows) < 0)
THROW_TJ("setting TJPARAM_RESTARTROWS");
if (maxMemory >= 0 && tj3Set(tjInstance, TJPARAM_MAXMEMORY, maxMemory) < 0)
THROW_TJ("setting TJPARAM_MAXMEMORY");

View File

@@ -2791,6 +2791,8 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
cinfo->arith_code = TRUE;
cinfo->optimize_coding = FALSE;
}
cinfo->restart_interval = this->restartIntervalBlocks;
cinfo->restart_in_rows = this->restartIntervalRows;
if (!(t[i].options & TJXOPT_NOOUTPUT)) {
jpeg_write_coefficients(cinfo, dstcoefs);
jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?

View File

@@ -793,7 +793,8 @@ enum TJPARAM {
*/
TJPARAM_LOSSLESSPT,
/**
* JPEG restart marker interval in MCUs [lossy compression only]
* JPEG restart marker interval in MCUs [lossy compression,
* lossless transformation]
*
* The nature of entropy coding is such that a corrupt JPEG image cannot
* be decompressed beyond the point of corruption unless it contains restart
@@ -819,7 +820,8 @@ enum TJPARAM {
*/
TJPARAM_RESTARTBLOCKS,
/**
* JPEG restart marker interval in MCU rows [compression only]
* JPEG restart marker interval in MCU rows [compression,
* lossless transformation]
*
* See #TJPARAM_RESTARTBLOCKS for a description of restart markers and MCUs.
* An MCU row is a row of MCUs spanning the entire width of the image.

View File

@@ -139,25 +139,29 @@ ENTROPYARG=${ENTROPYARG//-arithmetic/-a}
LOSSLSARG=${LOSSLSARG//-lossless/-l}
XFORMNAME=(hflip vflip transpose transverse rot90 rot180 rot270)
XFORMARGS=("-f h" "-f v" -t -transv "-ro 90" "-ro 180" "-ro 270")
XFORMARGS=("-f h -r 2" "-f v" "-t -r 1b" -transv "-ro 90 -r 1" "-ro 180" "-ro 270")
# Standard tests
for image in $IMAGES; do
cp $IMGDIR/$image $OUTDIR
basename=`basename $image .${EXT}`
r1barg=
if [ "$LOSSLSARG" != "-l" ]; then
r1barg="-r 1b"
fi
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -gr -outf $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x4 -outf $OUTDIR/${basename}_441_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 4x1 -outf $OUTDIR/${basename}_411_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x4 -r 2 -outf $OUTDIR/${basename}_441_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 4x1 -r 1 -outf $OUTDIR/${basename}_411_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 2x2 -outf $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x2 -outf $OUTDIR/${basename}_440_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x2 $r1barg -outf $OUTDIR/${basename}_440_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 2x1 -outf $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc fa $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x1 -outf $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -gr -outf $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x4 -outf $OUTDIR/${basename}_441_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 4x1 -outf $OUTDIR/${basename}_411_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x4 -r 2 -outf $OUTDIR/${basename}_441_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 4x1 -r 1 -outf $OUTDIR/${basename}_411_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 2x2 -outf $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x2 -outf $OUTDIR/${basename}_440_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x2 $r1barg -outf $OUTDIR/${basename}_440_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 2x1 -outf $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
runme $EXEDIR/cjpeg -q 95 -pre $PRECISION -dc i $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sa 1x1 -outf $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT}
for samp in $ALLSUBSAMP; do
@@ -177,9 +181,9 @@ for image in $IMAGES; do
fi
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
if [ "$LOSSLSARG" != "-l" ]; then
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -su 440 -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -su 411 -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -su 441 -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -su 440 -r 1b -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -su 411 -r 1 -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
runme $TJBENCH $OUTDIR/$image $TJQUAL -pre $PRECISION -su 441 -r 2 -rgb -q -be 0.01 -w 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG
fi
for samp in $ALLSUBSAMP; do
if [ "$LOSSLSARG" = "-l" ]; then

View File

@@ -70,6 +70,8 @@ for precision in 8 12; do
EXTRA_ARGS=-a
elif [ $sampi = 3 ]; then
EXTRA_ARGS=-o
elif [ $sampi = 5 ]; then
EXTRA_ARGS="-r 1"
fi
runme $EXEDIR/cjpeg -pre $precision -sa ${SAMPOPT[$sampi]} $EXTRA_ARGS \
-outf $OUTDIR/`basename $RGBIMG .ppm`-${SUBSAMPOPT[$sampi]}.jpg $RGBIMG
@@ -120,31 +122,36 @@ for precision in 8 12; do
if [[ "$progarg" = "-p" && "$optarg" = "-o" ]]; then
continue
fi
for trimarg in "" "-tri"; do
if [ "$trimarg" = "-tri" ]; then
if [[ "$xformarg" = "-t" || "$xformarg" = "" ]]; then
continue
fi
if [ "$croparg" != "" ]; then
continue
fi
for restartarg in "" "-r 1" "-r 1b"; do
if [[ "$restartarg" = "-r 1b" && "$croparg" != "" ]]; then
continue
fi
runme $TJTRAN $ariarg $copyarg $croparg $xformarg \
$grayarg $optarg $progarg $trimarg \
$OUTDIR/${basename}-$subsamp.jpg \
$OUTDIR/${basename}-tjtran.jpg
runme $EXEDIR/jpegtran $ariarg $copyarg $croparg \
$xformarg $grayarg $optarg $progarg $trimarg \
-outf $OUTDIR/${basename}-jpegtran.jpg \
$OUTDIR/${basename}-$subsamp.jpg
$EXEDIR/test/md5sum $OUTDIR/${basename}-tjtran.jpg \
$OUTDIR/${basename}-jpegtran.jpg |
sed "s@$OUTDIR@\$OUTDIR@g"
cmp $OUTDIR/${basename}-tjtran.jpg \
$OUTDIR/${basename}-jpegtran.jpg
rm $OUTDIR/${basename}-tjtran.jpg \
$OUTDIR/${basename}-jpegtran.jpg
echo
for trimarg in "" "-tri"; do
if [ "$trimarg" = "-tri" ]; then
if [[ "$xformarg" = "-t" || "$xformarg" = "" ]]; then
continue
fi
if [ "$croparg" != "" ]; then
continue
fi
fi
runme $TJTRAN $ariarg $copyarg $croparg $xformarg \
$grayarg $optarg $progarg $restartarg $trimarg \
$OUTDIR/${basename}-$subsamp.jpg \
$OUTDIR/${basename}-tjtran.jpg
runme $EXEDIR/jpegtran $ariarg $copyarg $croparg \
$xformarg $grayarg $optarg $progarg $restartarg \
$trimarg -outf $OUTDIR/${basename}-jpegtran.jpg \
$OUTDIR/${basename}-$subsamp.jpg
$EXEDIR/test/md5sum $OUTDIR/${basename}-tjtran.jpg \
$OUTDIR/${basename}-jpegtran.jpg |
sed "s@$OUTDIR@\$OUTDIR@g"
cmp $OUTDIR/${basename}-tjtran.jpg \
$OUTDIR/${basename}-jpegtran.jpg
rm $OUTDIR/${basename}-tjtran.jpg \
$OUTDIR/${basename}-jpegtran.jpg
echo
done
done
done
done