Skip to content

Commit

Permalink
add parameter / switches for progressive coding
Browse files Browse the repository at this point in the history
sjpeg new flag: -p <mode>, -progressive
vjpeg new key: 'p'
EncoderParam new field : progressive_mode

Just a placeholder for now.
  • Loading branch information
skal65535 committed Apr 24, 2024
1 parent 9990bdc commit 93080b4
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 41 deletions.
97 changes: 58 additions & 39 deletions examples/sjpeg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ int main(int argc, char * argv[]) {
const char* xmp_file = nullptr;
const char* icc_file = nullptr;
const char* exif_file = nullptr;
EncoderParam param;
EncoderParam params;
float reduction = 100;
float quality = 75;
bool use_reduction = true; // until '-q' is used...
Expand Down Expand Up @@ -132,6 +132,9 @@ int main(int argc, char * argv[]) {
" -qmin <float> ...... minimum acceptable quality factor during search\n"
" -qmax <float> ...... maximum acceptable quality factor during search\n"
" -tolerance <float> . tolerance for convergence during search\n"
" -p <mode> .......... enable progressive coding.\n"
" 'mode' is one of 2_SCANS, 3_SCANS, OPTIMAL.\n"
" -progressive ....... equivalent to -p 2_SCANS\n"
"\n"
" -gray .............. shortcut for '-yuv_mode 4'\n"
" -444 ............... shortcut for '-yuv_mode 3'\n"
Expand All @@ -144,7 +147,7 @@ int main(int argc, char * argv[]) {

// in order to gather information, plug a search hook
ALT_HOOK_CLASS hook;
param.search_hook = &hook;
params.search_hook = &hook;

// parse command line
if (argc <= 1) {
Expand Down Expand Up @@ -184,27 +187,27 @@ int main(int argc, char * argv[]) {
} else if (!strcmp(argv[c], "-no_limit")) {
limit_quantization = false;
} else if (!strcmp(argv[c], "-no_adapt")) {
param.adaptive_quantization = false;
params.adaptive_quantization = false;
} else if (!strcmp(argv[c], "-no_optim")) {
param.Huffman_compress = false;
params.Huffman_compress = false;
} else if (!strcmp(argv[c], "-adapt_bias")) {
param.adaptive_bias = true;
params.adaptive_bias = true;
} else if (!strcmp(argv[c], "-trellis")) {
param.use_trellis = true;
params.use_trellis = true;
} else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) {
param.target_mode = EncoderParam::TARGET_PSNR;
param.target_value = atof(argv[++c]);
params.target_mode = EncoderParam::TARGET_PSNR;
params.target_value = atof(argv[++c]);
} else if (!strcmp(argv[c], "-tolerance")) {
param.tolerance = atof(argv[++c]);
params.tolerance = atof(argv[++c]);
} else if (!strcmp(argv[c], "-qmin")) {
param.qmin = atof(argv[++c]);
params.qmin = atof(argv[++c]);
} else if (!strcmp(argv[c], "-qmax")) {
param.qmax = atof(argv[++c]);
params.qmax = atof(argv[++c]);
} else if (!strcmp(argv[c], "-size") && c + 1 < argc) {
param.target_mode = EncoderParam::TARGET_SIZE;
param.target_value = atof(argv[++c]);
params.target_mode = EncoderParam::TARGET_SIZE;
params.target_value = atof(argv[++c]);
} else if (!strcmp(argv[c], "-pass") && c + 1 < argc) {
param.passes = atoi(argv[++c]);
params.passes = atoi(argv[++c]);
} else if (!strcmp(argv[c], "-no_metadata")) {
no_metadata = true;
} else if (!strcmp(argv[c], "-yuv_mode") && c + 1 < argc) {
Expand All @@ -214,15 +217,29 @@ int main(int argc, char * argv[]) {
argv[c - 1], argv[c]);
return 1;
}
param.yuv_mode = (SjpegYUVMode)mode;
params.yuv_mode = (SjpegYUVMode)mode;
} else if (!strcmp(argv[c], "-444")) {
param.yuv_mode = SJPEG_YUV_444;
params.yuv_mode = SJPEG_YUV_444;
} else if (!strcmp(argv[c], "-sharp")) {
param.yuv_mode = SJPEG_YUV_SHARP;
params.yuv_mode = SJPEG_YUV_SHARP;
} else if (!strcmp(argv[c], "-420")) {
param.yuv_mode = SJPEG_YUV_420;
params.yuv_mode = SJPEG_YUV_420;
} else if (!strcmp(argv[c], "-gray")) {
param.yuv_mode = SJPEG_YUV_400;
params.yuv_mode = SJPEG_YUV_400;
} else if (!strcmp(argv[c], "-progressive")) {
params.progressive_mode = EncoderParam::PROGRESSIVE_2_SCANS;
} else if (!strcmp(argv[c], "-p") && c + 1 < argc) {
++c;
if (!strcmp(argv[c], "2_SCANS")) {
params.progressive_mode = EncoderParam::PROGRESSIVE_2_SCANS;
} else if (!strcmp(argv[c], "3_SCANS")) {
params.progressive_mode = EncoderParam::PROGRESSIVE_3_SCANS;
} else if (!strcmp(argv[c], "OPTIMAL")) {
params.progressive_mode = EncoderParam::PROGRESSIVE_OPTIMAL;
} else {
fprintf(stdout, "Error: invalid progressive mode '%s'\n", argv[c]);
return 1;
}
} else if (!strcmp(argv[c], "-i") || !strcmp(argv[c], "-info")) {
info = true;
} else if (!strcmp(argv[c], "-quiet")) {
Expand Down Expand Up @@ -250,9 +267,9 @@ int main(int argc, char * argv[]) {
return -1;
}
// finish param set up
const bool use_search = (param.target_mode != EncoderParam::TARGET_NONE);
if (use_search && param.passes <= 1) {
param.passes = 10;
const bool use_search = (params.target_mode != EncoderParam::TARGET_NONE);
if (use_search && params.passes <= 1) {
params.passes = 10;
}
// Read input file into the buffer in_bytes[]
const std::string input = ReadFile(input_file);
Expand All @@ -274,14 +291,14 @@ int main(int argc, char * argv[]) {
use_reduction = false;
}
if (use_reduction) { // use 'reduction' factor for JPEG source
param.SetQuantization(quant_matrices, reduction);
param.SetLimitQuantization(true);
params.SetQuantization(quant_matrices, reduction);
params.SetLimitQuantization(true);
} else { // the '-q' option has been used.
param.SetQuality(quality);
params.SetQuality(quality);
if (is_jpeg) {
param.SetMinQuantization(quant_matrices);
params.SetMinQuantization(quant_matrices);
} else {
param.SetLimitQuantization(false);
params.SetLimitQuantization(false);
}
}

Expand All @@ -291,12 +308,12 @@ int main(int argc, char * argv[]) {
return 0;
}
int W, H;
vector<uint8_t> in_bytes = ReadImage(input, &W, &H, &param);
vector<uint8_t> in_bytes = ReadImage(input, &W, &H, &params);
if (in_bytes.size() == 0) return 1;

if (xmp_file != nullptr) param.xmp = ReadFile(xmp_file);
if (icc_file != nullptr) param.iccp = ReadFile(icc_file);
if (exif_file != nullptr) param.exif = ReadFile(exif_file);
if (xmp_file != nullptr) params.xmp = ReadFile(xmp_file);
if (icc_file != nullptr) params.iccp = ReadFile(icc_file);
if (exif_file != nullptr) params.exif = ReadFile(exif_file);

if (!short_output && !quiet && !print_crc && !print_md5) {
fprintf(stdout, "Input [%s]: %s (%u bytes, %.2f bpp, %d x %d)\n",
Expand All @@ -318,19 +335,19 @@ int main(int argc, char * argv[]) {
PrintMatrix("UV-chroma", quant_matrices[1], true);
}
}
PrintMetadataInfo(param);
PrintMetadataInfo(params);
}
}
if (info && !print_crc && !print_md5) return 0; // done

// finish setting up the quantization matrices
if (limit_quantization == false) param.SetLimitQuantization(false);
if (limit_quantization == false) params.SetLimitQuantization(false);

if (no_metadata) param.ResetMetadata();
if (no_metadata) params.ResetMetadata();

const double start = GetStopwatchTime();
std::string out;
const bool ok = sjpeg::Encode(&in_bytes[0], W, H, 3 * W, param, &out);
const bool ok = sjpeg::Encode(&in_bytes[0], W, H, 3 * W, params, &out);
const double encode_time = GetStopwatchTime() - start;

if (!ok) {
Expand All @@ -351,24 +368,26 @@ int main(int argc, char * argv[]) {
const bool show_reduction = use_reduction && !use_search;
yuv_mode_rec = SjpegRiskiness(&in_bytes[0], W, H, 3 * W, &riskiness);
fprintf(stdout, "new size: %u bytes (%.2f bpp, %.2lf%% of original)\n"
"%s%.1f (adaptive: %s, Huffman: %s)\n"
"%s%.1f (adaptive: %s, Huffman: %s%s)\n"
"yuv mode: %s (riskiness: %.1lf%%)\n"
"elapsed: %d ms\n",
static_cast<uint32_t>(out.size()),
8.f * out.size() / (W * H),
100. * out.size() / input.size(),
show_reduction ? "reduction: r=" : "quality: q=",
show_reduction ? reduction : quality,
kNoYes[param.adaptive_quantization],
kNoYes[param.Huffman_compress],
kNoYes[params.adaptive_quantization],
kNoYes[params.Huffman_compress],
(params.progressive_mode != EncoderParam::PROGRESSIVE_OFF) ?
", Progressive" : "",
kYUVModeNames[yuv_mode_rec], riskiness,
static_cast<int>(1000. * encode_time));
if (use_search) { // print final values
fprintf(stdout, "passes: %d\n", hook.pass + 1);
fprintf(stdout, "final value: %.1f\n", hook.value);
fprintf(stdout, "final q: %.2f\n", hook.q);
}
PrintMetadataInfo(param);
PrintMetadataInfo(params);
} else if (!quiet) {
fprintf(stdout, "%u %u %.2lf %%\n",
static_cast<uint32_t>(input.size()),
Expand Down
10 changes: 9 additions & 1 deletion examples/vjpeg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ static void PrintInfo() {
msg.push_back(" 'b' ............... toggle adaptive bias");
msg.push_back(" 'l' ............... toggle quantization limitation");
msg.push_back(" 't' ............... toggle trellis-based limitation");
msg.push_back(" 'p' ............... cycle progressive mode coding");
msg.push_back(" 'e' ............... show error map");
msg.push_back(" 'r' ............... show riskiness map");
msg.push_back(" '+'/'-' ........... go to next/previous file");
Expand Down Expand Up @@ -198,7 +199,8 @@ static void PrintInfo() {
msg.back() += tmp;
}

snprintf(tmp, sizeof(tmp), "Quality: %.1f", kParams.quality);
snprintf(tmp, sizeof(tmp), "Quality: %.1f (progressive mode: %d)",
kParams.quality, (int)kParams.param.progressive_mode);
msg.push_back(tmp);

if (!kParams.param.Huffman_compress) {
Expand Down Expand Up @@ -516,6 +518,11 @@ static void HandleKey(unsigned char key, int pos_x, int pos_y) {
} else if (key == 't') {
kParams.param.use_trellis = !kParams.param.use_trellis;
FullRedraw();
} else if (key == 'p') {
int mode = (int)kParams.param.progressive_mode + 1;
if (mode > (int)sjpeg::EncoderParam::PROGRESSIVE_OPTIMAL) mode = 0;
kParams.param.progressive_mode = (sjpeg::EncoderParam::ProgressiveMode)mode;
FullRedraw();
} else if (key == 'm') {
PrintMatrices();
}
Expand Down Expand Up @@ -650,6 +657,7 @@ static void Help(void) {
" 'a' ............... toggle adaptive quantization\n"
" 'l' ............... toggle quantization limitation\n"
" 't' ............... toggle trellis-based quantization\n"
" 'p' ............... cycle progressive mode coding\n"
" 'e' ............... show error map\n"
" 'r' ............... show riskiness map\n"
" '+'/'-' ........... go to next/previous file\n"
Expand Down
7 changes: 7 additions & 0 deletions man/sjpeg.1
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ Disable Huffman code optimization (faster processing, larger file)
.B \-adapt_bias
Disable the adaptive bias optimization.
.TP
.B \-progressive
Enable progressive coding (2_SCANS).
.TP
.B \-p string
Enable progressive coding with supplied mode, one of: 2_SCANS, 3_SCANS, OPTIMAL.
.TP

.B \-no_metadata
Disable transfer of metadata from source to JPEG (saves file size, but discards
information).
Expand Down
2 changes: 2 additions & 0 deletions man/vjpeg.1
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ The available key shortcuts are:
'l' ............... toggle quantization limitation
.IP
't' ............... toggle trellis-based quantization
.IP
'p' ............... cycle progressive mode coding
.IP
'e' ............... show error map
.IP
Expand Down
1 change: 1 addition & 0 deletions src/enc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,7 @@ void EncoderParam::Init(float quality_factor) {
Huffman_compress = true;
adaptive_quantization = true;
use_trellis = false;
progressive_mode = PROGRESSIVE_OFF;
yuv_mode = SJPEG_YUV_AUTO;
quantization_bias = kDefaultBias;
qdelta_max_luma = kDefaultDeltaMaxLuma;
Expand Down
11 changes: 10 additions & 1 deletion src/sjpeg.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <string>
#include <vector>

#define SJPEG_VERSION 0x000100 // 0.1.0
#define SJPEG_VERSION 0x000101 // 0.1.1

#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
Expand Down Expand Up @@ -216,6 +216,15 @@ struct EncoderParam {
bool adaptive_bias; // if true, use perceptual bias adaptation
bool use_trellis; // if true, use trellis-based optimization

typedef enum {
PROGRESSIVE_OFF = 0, // Disable progressive coding (default)
PROGRESSIVE_2_SCANS = 1, // luma: 1-3, 6-63 split, chroma: 1-63
PROGRESSIVE_3_SCANS = 2, // luma/chroma: 1-3, 6-63 split
PROGRESSIVE_CUSTOM = 3, // reserved for later
PROGRESSIVE_OPTIMAL = 4, // try several combinations (slowest)
} ProgressiveMode;
ProgressiveMode progressive_mode;

// target size or distortion
typedef enum {
TARGET_NONE = 0,
Expand Down
8 changes: 8 additions & 0 deletions tests/test_cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ ${SJPEG} ${SRC_FILE1} -crc
${SJPEG} ${SRC_FILE1} -estimate
${SJPEG} ${SRC_FILE1} -i

for pmode in 2_SCANS 3_SCANS OPTIMAL; do
for yuv_mode in 444 420; do
${SJPEG} ${SRC_FILE4} -o ${TMP_FILE1} -size 24000 -p ${pmode} -${yuv_mode}
done
${SJPEG} ${SRC_FILE4} -o ${TMP_FILE1} -size 24000 -progressive -${yuv_mode}
done

# test CRC is matching
if [ -x "$(command -v md5)" ]; then
for file in ${SRC_FILE1} ${SRC_FILE2} ${SRC_FILE4}; do
Expand Down Expand Up @@ -77,6 +84,7 @@ ${SJPEG} -no_adapt -no_optim -quiet
${SJPEG} ${SRC_FILE1} -o ${TMP_FILE1} -yuv_mode -1 -quiet
${SJPEG} ${SRC_FILE1} -o ${TMP_FILE1} -yuv_mode 4 -quiet
${SJPEG} ${SRC_FILE1} -o ${TMP_FILE1} -yuv_mode 99 -quiet -no_metadata
${SJPEG} ${SRC_FILE1} -o ${TMP_FILE1} -p BAD -quiet
${SJPEG} -q 80 -quiet
${SJPEG} ${SRC_FILE1} -risk -quiet
${SJPEG} ${SRC_FILE1} -o
Expand Down

0 comments on commit 93080b4

Please sign in to comment.