From c06632c6f6d0a3e106374fe462869b131366c2d0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 11 Aug 2022 18:06:21 +0200 Subject: [PATCH] Cleanup code related to quality layer allocation, and add a few safety checks --- src/bin/jp2/opj_compress.c | 6 ++-- src/lib/openjp2/j2k.c | 48 ++++++++++++++++++++++++-------- src/lib/openjp2/j2k.h | 21 +++++++++----- src/lib/openjp2/openjpeg.h | 6 ++-- src/lib/openjp2/t1.c | 5 +--- src/lib/openjp2/tcd.c | 57 +++++++++++++++++++++----------------- src/lib/openjp2/tcd.h | 10 +++---- 7 files changed, 94 insertions(+), 59 deletions(-) diff --git a/src/bin/jp2/opj_compress.c b/src/bin/jp2/opj_compress.c index fc9b7c12..8ba3b6d7 100644 --- a/src/bin/jp2/opj_compress.c +++ b/src/bin/jp2/opj_compress.c @@ -846,7 +846,7 @@ static int parse_cmdline_encoder(int argc, char **argv, /* ----------------------------------------------------- */ - case 'q': { /* add fixed_quality */ + case 'q': { /* layer allocation by distortion ratio (PSNR) */ char *s = opj_optarg; while (sscanf(s, "%f", ¶meters->tcp_distoratio[parameters->tcp_numlayers]) == 1) { @@ -866,7 +866,7 @@ static int parse_cmdline_encoder(int argc, char **argv, /* dda */ /* ----------------------------------------------------- */ - case 'f': { /* mod fixed_quality (before : -q) */ + case 'f': { /* layer allocation by fixed layer */ int *row = NULL, *col = NULL; OPJ_UINT32 numlayers = 0, numresolution = 0, matrix_width = 0; @@ -1812,7 +1812,7 @@ static int parse_cmdline_encoder(int argc, char **argv, parameters->cp_fixed_quality))) { fprintf(stderr, "[ERROR] options -r -q and -f cannot be used together !!\n"); return 1; - } /* mod fixed_quality */ + } /* if no rate entered, lossless by default */ diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c index bcce3165..923bd891 100644 --- a/src/lib/openjp2/j2k.c +++ b/src/lib/openjp2/j2k.c @@ -7666,6 +7666,27 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k, return OPJ_FALSE; } + if (parameters->cp_fixed_alloc) { + if (parameters->cp_matrice == NULL) { + opj_event_msg(p_manager, EVT_ERROR, + "cp_fixed_alloc set, but cp_matrice missing\n"); + return OPJ_FALSE; + } + + if (parameters->tcp_numlayers > J2K_TCD_MATRIX_MAX_LAYER_COUNT) { + opj_event_msg(p_manager, EVT_ERROR, + "tcp_numlayers when cp_fixed_alloc set should not exceed %d\n", + J2K_TCD_MATRIX_MAX_LAYER_COUNT); + return OPJ_FALSE; + } + if (parameters->numresolution > J2K_TCD_MATRIX_MAX_RESOLUTION_COUNT) { + opj_event_msg(p_manager, EVT_ERROR, + "numresolution when cp_fixed_alloc set should not exceed %d\n", + J2K_TCD_MATRIX_MAX_RESOLUTION_COUNT); + return OPJ_FALSE; + } + } + p_j2k->m_specific_param.m_encoder.m_nb_comps = image->numcomps; /* keep a link to cp so that we can destroy it later in j2k_destroy_compress */ @@ -7885,15 +7906,17 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k, cp->m_specific_param.m_enc.m_max_comp_size = (OPJ_UINT32) parameters->max_comp_size; cp->rsiz = parameters->rsiz; - cp->m_specific_param.m_enc.m_disto_alloc = (OPJ_UINT32) - parameters->cp_disto_alloc & 1u; - cp->m_specific_param.m_enc.m_fixed_alloc = (OPJ_UINT32) - parameters->cp_fixed_alloc & 1u; - cp->m_specific_param.m_enc.m_fixed_quality = (OPJ_UINT32) - parameters->cp_fixed_quality & 1u; + if (parameters->cp_fixed_alloc) { + cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy = FIXED_LAYER; + } else if (parameters->cp_fixed_quality) { + cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy = + FIXED_DISTORTION_RATIO; + } else { + cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy = + RATE_DISTORTION_RATIO; + } - /* mod fixed_quality */ - if (parameters->cp_fixed_alloc && parameters->cp_matrice) { + if (parameters->cp_fixed_alloc) { size_t array_size = (size_t)parameters->tcp_numlayers * (size_t)parameters->numresolution * 3 * sizeof(OPJ_INT32); cp->m_specific_param.m_enc.m_matrice = (OPJ_INT32 *) opj_malloc(array_size); @@ -8051,22 +8074,25 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k, for (tileno = 0; tileno < cp->tw * cp->th; tileno++) { opj_tcp_t *tcp = &cp->tcps[tileno]; + const OPJ_BOOL fixed_distoratio = + cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy == + FIXED_DISTORTION_RATIO; tcp->numlayers = (OPJ_UINT32)parameters->tcp_numlayers; for (j = 0; j < tcp->numlayers; j++) { if (OPJ_IS_CINEMA(cp->rsiz) || OPJ_IS_IMF(cp->rsiz)) { - if (cp->m_specific_param.m_enc.m_fixed_quality) { + if (fixed_distoratio) { tcp->distoratio[j] = parameters->tcp_distoratio[j]; } tcp->rates[j] = parameters->tcp_rates[j]; } else { - if (cp->m_specific_param.m_enc.m_fixed_quality) { /* add fixed_quality */ + if (fixed_distoratio) { tcp->distoratio[j] = parameters->tcp_distoratio[j]; } else { tcp->rates[j] = parameters->tcp_rates[j]; } } - if (!cp->m_specific_param.m_enc.m_fixed_quality && + if (!fixed_distoratio && tcp->rates[j] <= 1.0) { tcp->rates[j] = 0.0; /* force lossless */ } diff --git a/src/lib/openjp2/j2k.h b/src/lib/openjp2/j2k.h index 04fba645..e0b9688a 100644 --- a/src/lib/openjp2/j2k.h +++ b/src/lib/openjp2/j2k.h @@ -113,6 +113,9 @@ The functions in J2K.C have for goal to read/write the several parts of the code #define J2K_MAX_POCS 32 /**< Maximum number of POCs */ +#define J2K_TCD_MATRIX_MAX_LAYER_COUNT 10 +#define J2K_TCD_MATRIX_MAX_RESOLUTION_COUNT 10 + /* ----------------------------------------------------------------------- */ /** @@ -272,7 +275,7 @@ typedef struct opj_tcp { OPJ_UINT32 ppt_data_size; /** size of ppt_data*/ OPJ_UINT32 ppt_len; - /** add fixed_quality */ + /** PSNR values */ OPJ_FLOAT32 distoratio[100]; /** tile-component coding parameters */ opj_tccp_t *tccps; @@ -314,6 +317,14 @@ typedef struct opj_tcp { } opj_tcp_t; +/** +Rate allocation strategy +*/ +typedef enum { + RATE_DISTORTION_RATIO = 0, /** allocation by rate/distortion */ + FIXED_DISTORTION_RATIO = 1, /** allocation by fixed distortion ratio (PSNR) (fixed quality) */ + FIXED_LAYER = 2, /** allocation by fixed layer (number of passes per layer / resolution / subband) */ +} J2K_QUALITY_LAYER_ALLOCATION_STRATEGY; typedef struct opj_encoding_param { @@ -325,12 +336,8 @@ typedef struct opj_encoding_param { OPJ_INT32 *m_matrice; /** Flag determining tile part generation*/ OPJ_BYTE m_tp_flag; - /** allocation by rate/distortion */ - OPJ_BITFIELD m_disto_alloc : 1; - /** allocation by fixed layer */ - OPJ_BITFIELD m_fixed_alloc : 1; - /** add fixed_quality */ - OPJ_BITFIELD m_fixed_quality : 1; + /** Quality layer allocation strategy */ + J2K_QUALITY_LAYER_ALLOCATION_STRATEGY m_quality_layer_alloc_strategy; /** Enabling Tile part generation*/ OPJ_BITFIELD m_tp_on : 1; } diff --git a/src/lib/openjp2/openjpeg.h b/src/lib/openjp2/openjpeg.h index ebce53db..ebe78444 100644 --- a/src/lib/openjp2/openjpeg.h +++ b/src/lib/openjp2/openjpeg.h @@ -405,7 +405,7 @@ typedef struct opj_cparameters { int cp_disto_alloc; /** allocation by fixed layer */ int cp_fixed_alloc; - /** add fixed_quality */ + /** allocation by fixed quality (PSNR) */ int cp_fixed_quality; /** fixed layer */ int *cp_matrice; @@ -829,9 +829,9 @@ typedef struct opj_tile_info { int pdy[33]; /** information concerning packets inside tile */ opj_packet_info_t *packet; - /** add fixed_quality */ + /** number of pixels of the tile */ int numpix; - /** add fixed_quality */ + /** distortion of the tile */ double distotile; /** number of markers */ int marknum; diff --git a/src/lib/openjp2/t1.c b/src/lib/openjp2/t1.c index c8c1c0e1..52e466eb 100644 --- a/src/lib/openjp2/t1.c +++ b/src/lib/openjp2/t1.c @@ -1410,7 +1410,6 @@ static void opj_t1_dec_clnpass( } -/** mod fixed_quality */ static OPJ_FLOAT64 opj_t1_getwmsedec( OPJ_INT32 nmsedec, OPJ_UINT32 compno, @@ -2313,7 +2312,7 @@ OPJ_BOOL opj_t1_encode_cblks(opj_tcd_t* tcd, OPJ_UINT32 compno, resno, bandno, precno, cblkno; opj_mutex_t* mutex = opj_mutex_create(); - tile->distotile = 0; /* fixed_quality */ + tile->distotile = 0; for (compno = 0; compno < tile->numcomps; ++compno) { opj_tcd_tilecomp_t* tilec = &tile->comps[compno]; @@ -2401,7 +2400,6 @@ static int opj_t1_enc_is_term_pass(opj_tcd_cblk_enc_t* cblk, } -/** mod fixed_quality */ static OPJ_FLOAT64 opj_t1_encode_cblk(opj_t1_t *t1, opj_tcd_cblk_enc_t* cblk, OPJ_UINT32 orient, @@ -2505,7 +2503,6 @@ static OPJ_FLOAT64 opj_t1_encode_cblk(opj_t1_t *t1, break; } - /* fixed_quality */ tempwmsedec = opj_t1_getwmsedec(nmsedec, compno, level, orient, bpno, qmfbid, stepsize, numcomps, mct_norms, mct_numcomps) ; cumwmsedec += tempwmsedec; diff --git a/src/lib/openjp2/tcd.c b/src/lib/openjp2/tcd.c index 998baf9a..438247b6 100644 --- a/src/lib/openjp2/tcd.c +++ b/src/lib/openjp2/tcd.c @@ -256,7 +256,7 @@ OPJ_BOOL opj_tcd_makelayer(opj_tcd_t *tcd, opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles; OPJ_BOOL layer_allocation_is_same = OPJ_TRUE; - tcd_tile->distolayer[layno] = 0; /* fixed_quality */ + tcd_tile->distolayer[layno] = 0; for (compno = 0; compno < tcd_tile->numcomps; compno++) { opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno]; @@ -338,7 +338,7 @@ OPJ_BOOL opj_tcd_makelayer(opj_tcd_t *tcd, cblk->passes[cblk->numpassesinlayers - 1].distortiondec; } - tcd_tile->distolayer[layno] += layer->disto; /* fixed_quality */ + tcd_tile->distolayer[layno] += layer->disto; if (final) { cblk->numpassesinlayers = n; @@ -351,13 +351,14 @@ OPJ_BOOL opj_tcd_makelayer(opj_tcd_t *tcd, return layer_allocation_is_same; } +/** For m_quality_layer_alloc_strategy == FIXED_LAYER */ static void opj_tcd_makelayer_fixed(opj_tcd_t *tcd, OPJ_UINT32 layno, OPJ_UINT32 final) { OPJ_UINT32 compno, resno, bandno, precno, cblkno; OPJ_INT32 value; /*, matrice[tcd_tcp->numlayers][tcd_tile->comps[0].numresolutions][3]; */ - OPJ_INT32 matrice[10][10][3]; + OPJ_INT32 matrice[J2K_TCD_MATRIX_MAX_LAYER_COUNT][J2K_TCD_MATRIX_MAX_RESOLUTION_COUNT][3]; OPJ_UINT32 i, j, k; opj_cp_t *cp = tcd->cp; @@ -458,8 +459,8 @@ void opj_tcd_makelayer_fixed(opj_tcd_t *tcd, OPJ_UINT32 layno, } /** Rate allocation for the following methods: - * - allocation by rate/distortio (m_disto_alloc == 1) - * - allocation by fixed quality (m_fixed_quality == 1) + * - allocation by rate/distortio (m_quality_layer_alloc_strategy == RATE_DISTORTION_RATIO) + * - allocation by fixed quality (m_quality_layer_alloc_strategy == FIXED_DISTORTION_RATIO) */ static OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, @@ -472,8 +473,8 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, OPJ_UINT32 compno, resno, bandno, precno, cblkno, layno; OPJ_UINT32 passno; OPJ_FLOAT64 min, max; - OPJ_FLOAT64 cumdisto[100]; /* fixed_quality */ - const OPJ_FLOAT64 K = 1; /* 1.1; fixed_quality */ + OPJ_FLOAT64 cumdisto[100]; + const OPJ_FLOAT64 K = 1; OPJ_FLOAT64 maxSE = 0; opj_cp_t *cp = tcd->cp; @@ -483,7 +484,7 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, min = DBL_MAX; max = 0; - tcd_tile->numpix = 0; /* fixed_quality */ + tcd_tile->numpix = 0; for (compno = 0; compno < tcd_tile->numcomps; compno++) { opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno]; @@ -533,9 +534,12 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, } } /* passno */ - /* fixed_quality */ - tcd_tile->numpix += ((cblk->x1 - cblk->x0) * (cblk->y1 - cblk->y0)); - tilec->numpix += ((cblk->x1 - cblk->x0) * (cblk->y1 - cblk->y0)); + { + const OPJ_SIZE_T cblk_pix_count = (OPJ_SIZE_T)((cblk->x1 - cblk->x0) * + (cblk->y1 - cblk->y0)); + tcd_tile->numpix += cblk_pix_count; + tilec->numpix += cblk_pix_count; + } } /* cbklno */ } /* precno */ } /* bandno */ @@ -549,8 +553,8 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, /* index file */ if (cstr_info) { opj_tile_info_t *tile_info = &cstr_info->tile[tcd->tcd_tileno]; - tile_info->numpix = tcd_tile->numpix; - tile_info->distotile = tcd_tile->distotile; + tile_info->numpix = (int)tcd_tile->numpix; + tile_info->distotile = (int)tcd_tile->distotile; tile_info->thresh = (OPJ_FLOAT64 *) opj_malloc(tcd_tcp->numlayers * sizeof( OPJ_FLOAT64)); if (!tile_info->thresh) { @@ -567,19 +571,20 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, OPJ_FLOAT64 goodthresh = 0; OPJ_FLOAT64 stable_thresh = 0; OPJ_UINT32 i; - OPJ_FLOAT64 distotarget; /* fixed_quality */ + OPJ_FLOAT64 distotarget; - /* fixed_quality */ distotarget = tcd_tile->distotile - ((K * maxSE) / pow((OPJ_FLOAT32)10, tcd_tcp->distoratio[layno] / 10)); /* Don't try to find an optimal threshold but rather take everything not included yet, if - -r xx,yy,zz,0 (disto_alloc == 1 and rates == 0) - -q xx,yy,zz,0 (fixed_quality == 1 and distoratio == 0) + -r xx,yy,zz,0 (m_quality_layer_alloc_strategy == RATE_DISTORTION_RATIO and rates == NULL) + -q xx,yy,zz,0 (m_quality_layer_alloc_strategy == FIXED_DISTORTION_RATIO and distoratio == NULL) ==> possible to have some lossy layers and the last layer for sure lossless */ - if (((cp->m_specific_param.m_enc.m_disto_alloc == 1) && + if (((cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy == + RATE_DISTORTION_RATIO) && (tcd_tcp->rates[layno] > 0.0f)) || - ((cp->m_specific_param.m_enc.m_fixed_quality == 1) && + ((cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy == + FIXED_DISTORTION_RATIO) && (tcd_tcp->distoratio[layno] > 0.0))) { opj_t2_t*t2 = opj_t2_create(tcd->image, cp); OPJ_FLOAT64 thresh = 0; @@ -590,7 +595,7 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, } for (i = 0; i < 128; ++i) { - OPJ_FLOAT64 distoachieved = 0; /* fixed_quality */ + OPJ_FLOAT64 distoachieved = 0; OPJ_BOOL layer_allocation_is_same; OPJ_FLOAT64 new_thresh = (lo + hi) / 2; @@ -612,7 +617,8 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, opj_event_msg(p_manager, EVT_INFO, "--> layer_allocation_is_same = %d", layer_allocation_is_same); #endif - if (cp->m_specific_param.m_enc.m_fixed_quality) { /* fixed_quality */ + if (cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy == + FIXED_DISTORTION_RATIO) { if (OPJ_IS_CINEMA(cp->rsiz) || OPJ_IS_IMF(cp->rsiz)) { if (! opj_t2_encode_packets(t2, tcd->tcd_tileno, tcd_tile, layno + 1, dest, p_data_written, maxlen, cstr_info, NULL, tcd->cur_tp_num, tcd->tp_pos, @@ -698,7 +704,6 @@ OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd, opj_tcd_makelayer(tcd, layno, goodthresh, 1); - /* fixed_quality */ cumdisto[layno] = (layno == 0) ? tcd_tile->distolayer[0] : (cumdisto[layno - 1] + tcd_tile->distolayer[layno]); } @@ -2662,10 +2667,10 @@ static OPJ_BOOL opj_tcd_rate_allocate_encode(opj_tcd_t *p_tcd, p_cstr_info->index_write = 0; } - if (l_cp->m_specific_param.m_enc.m_disto_alloc || - l_cp->m_specific_param.m_enc.m_fixed_quality) { - /* fixed_quality */ - /* Normal Rate/distortion allocation */ + if (l_cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy == + RATE_DISTORTION_RATIO || + l_cp->m_specific_param.m_enc.m_quality_layer_alloc_strategy == + FIXED_DISTORTION_RATIO) { if (! opj_tcd_rateallocate(p_tcd, p_dest_data, &l_nb_written, p_max_dest_size, p_cstr_info, p_manager)) { return OPJ_FALSE; diff --git a/src/lib/openjp2/tcd.h b/src/lib/openjp2/tcd.h index 7a4f3dcb..f659869a 100644 --- a/src/lib/openjp2/tcd.h +++ b/src/lib/openjp2/tcd.h @@ -222,8 +222,8 @@ typedef struct opj_tcd_tilecomp { OPJ_UINT32 win_x1; OPJ_UINT32 win_y1; - /* add fixed_quality */ - OPJ_INT32 numpix; + /* number of pixels */ + OPJ_SIZE_T numpix; } opj_tcd_tilecomp_t; @@ -235,9 +235,9 @@ typedef struct opj_tcd_tile { OPJ_INT32 x0, y0, x1, y1; OPJ_UINT32 numcomps; /* number of components in tile */ opj_tcd_tilecomp_t *comps; /* Components information */ - OPJ_INT32 numpix; /* add fixed_quality */ - OPJ_FLOAT64 distotile; /* add fixed_quality */ - OPJ_FLOAT64 distolayer[100]; /* add fixed_quality */ + OPJ_SIZE_T numpix; /* number of pixels */ + OPJ_FLOAT64 distotile; /* distortion of the tile */ + OPJ_FLOAT64 distolayer[100]; /* distortion per layer */ OPJ_UINT32 packno; /* packet number */ } opj_tcd_tile_t;