[FFmpeg-soc] [soc]: r334 - jpeg2000/j2kenc.c

k.nowosad subversion at mplayerhq.hu
Sat Jul 7 01:54:31 CEST 2007


Author: k.nowosad
Date: Sat Jul  7 01:54:31 2007
New Revision: 334

Log:
adds lossy compression with possibility to limit the number of bytes contributed by a single tile by setting maxtilelen field in the encoder context [i'll connect it with api in the nearest future];


Modified:
   jpeg2000/j2kenc.c

Modified: jpeg2000/j2kenc.c
==============================================================================
--- jpeg2000/j2kenc.c	(original)
+++ jpeg2000/j2kenc.c	Sat Jul  7 01:54:31 2007
@@ -30,14 +30,28 @@
 #include "j2k.h"
 #include "common.h"
 
+#define NMSEDEC_BITS 7
+#define NMSEDEC_FRACBITS (NMSEDEC_BITS-1)
+
+static int lut_nmsedec_ref[1<<NMSEDEC_BITS],
+    lut_nmsedec_ref0[1<<NMSEDEC_BITS],
+    lut_nmsedec_sig[1<<NMSEDEC_BITS],
+    lut_nmsedec_sig0[1<<NMSEDEC_BITS];
+
 // TODO: doxygen-compatible comments
 
 typedef struct {
-    int length;
+    uint16_t rate;
+    double disto;
+} J2kPass;
+
+typedef struct {
     int npassess;
+    int ninclpassess; // number of passess included in codestream
     int nonzerobits;
     int zero;
     uint8_t data[8192];
+    J2kPass passess[30];
 } J2kCblk; // code block
 
 typedef struct J2kTgtNode {
@@ -73,6 +87,9 @@ typedef struct {
 
 typedef struct { // flatten with context
    J2kComponent *comp;
+   double distortion;
+   double mindr; // minimum dist/rate value
+   double maxdr;
 } J2kTile;
 
 typedef struct {
@@ -88,6 +105,7 @@ typedef struct {
     int xcb, ycb; // exponent of the code block size
     int XTsiz, YTsiz; // tile size
     int numXtiles, numYtiles;
+    int maxtilelen;
 
     int nguardbits;
 
@@ -428,6 +446,10 @@ static int init_tiles(J2kEncoderContext 
         int p = tno % s->numXtiles;
         int q = tno / s->numXtiles;
 
+        tile->distortion = 0.0;
+        tile->mindr = 1e10;
+        tile->maxdr = 0.0;
+
         tile->comp = av_malloc(s->ncomponents * sizeof(J2kComponent));
         if (tile->comp == NULL)
             return -1;
@@ -578,6 +600,26 @@ static int init_tiles(J2kEncoderContext 
     return 0;
 }
 
+void init_luts()
+{
+    int i;
+    double u, v, t, pfr;
+
+    pfr = pow(2, NMSEDEC_FRACBITS);
+    for (i = 0; i < (1 << NMSEDEC_BITS); i++){
+        t = i / pfr;
+        u = t;
+        v = t - 1.5;
+        lut_nmsedec_sig[i]  = FFMAX((int) (floor((u*u - v*v) * pfr + 0.5) / pfr * 8192.0), 0);
+        lut_nmsedec_sig0[i] = FFMAX((int) (floor((u*u) * pfr + 0.5) / pfr * 8192.0), 0);
+
+        u = t - 1.0;
+        v = t - ((i & (1<<(NMSEDEC_BITS-1))) ? 1.5 : 0.5);
+        lut_nmsedec_ref[i]  = FFMAX((int) (floor((u*u - v*v) * pfr + 0.5) / pfr * 8192.0), 0);
+        lut_nmsedec_ref0[i] = FFMAX((int) (floor((u*u) * pfr + 0.5) / pfr * 8192.0), 0);
+    }
+}
+
 /* discrete wavelet transform routines */
 static void sd_1d(int *p, int i0, int i1, int ileft, int iright)
 {
@@ -759,10 +801,35 @@ static void set_significant(J2kT1Context
     t1->flags[y-1][x-1] |= J2K_T1_SIG_SE;
 }
 
+static int getnmsedec_sig(int x, int bpno)
+{
+    if (bpno > NMSEDEC_FRACBITS)
+        return lut_nmsedec_sig[(x >> (bpno - NMSEDEC_FRACBITS)) & ((1 << NMSEDEC_BITS) - 1)];
+    return lut_nmsedec_sig0[x & ((1 << NMSEDEC_BITS) - 1)];
+}
 
-static void encode_sigpass(J2kT1Context *t1, int width, int height, int mask, int bandno)
+static int getnmsedec_ref(int x, int bpno)
 {
-    int i, j, k;
+    if (bpno > NMSEDEC_FRACBITS)
+        return lut_nmsedec_ref[(x >> (bpno - NMSEDEC_FRACBITS)) & ((1 << NMSEDEC_BITS) - 1)];
+    return lut_nmsedec_ref0[x & ((1 << NMSEDEC_BITS) - 1)];
+}
+
+static double getwmsedec(int nmsedec, int bandpos, int lev, int bpno)
+{
+   static const double dwt_norms[4][10] = {
+    {1.000, 1.500, 2.750, 5.375, 10.68, 21.34, 42.67, 85.33, 170.7, 341.3},
+    {1.038, 1.592, 2.919, 5.703, 11.33, 22.64, 45.25, 90.48, 180.9},
+    {1.038, 1.592, 2.919, 5.703, 11.33, 22.64, 45.25, 90.48, 180.9},
+    {.7186, .9218, 1.586, 3.043, 6.019, 12.01, 24.00, 47.97, 95.93}};
+
+    double t = dwt_norms[bandpos][lev] * (1 << bpno);
+    return t * t * nmsedec / 8192.0;
+}
+
+static void encode_sigpass(J2kT1Context *t1, int width, int height, int bandno, int *nmsedec, int bpno)
+{
+    int i, j, k, mask = 1 << (bpno + NMSEDEC_FRACBITS);
     for (i = 0; i < height; i += 4)
         for (j = 0; j < width; j++)
             for (k = i; k < height && k < i+4; k++){
@@ -774,6 +841,7 @@ static void encode_sigpass(J2kT1Context 
                         int xorbit;
                         int ctxno = getsgnctxno(t1->flags[k+1][j+1], &xorbit);
                         ff_aec_encode(&t1->aec, ctxno, (t1->data[k][j] < 0 ? 1:0) ^ xorbit);
+                        *nmsedec += getnmsedec_sig(abs(t1->data[k][j]), bpno + NMSEDEC_FRACBITS);
                         set_significant(t1, j, k);
                     }
                     t1->flags[k+1][j+1] |= J2K_T1_VIS;
@@ -781,22 +849,23 @@ static void encode_sigpass(J2kT1Context 
             }
 }
 
-static void encode_refpass(J2kT1Context *t1, int width, int height, int mask)
+static void encode_refpass(J2kT1Context *t1, int width, int height, int *nmsedec, int bpno)
 {
-    int i, j, k;
+    int i, j, k, mask = 1 << (bpno + NMSEDEC_FRACBITS);
     for (i = 0; i < height; i += 4)
         for (j = 0; j < width; j++)
             for (k = i; k < height && k < i+4; k++)
                 if ((t1->flags[k+1][j+1] & (J2K_T1_SIG | J2K_T1_VIS)) == J2K_T1_SIG){
                     int ctxno = getrefctxno(t1->flags[k+1][j+1]);
+                    *nmsedec += getnmsedec_ref(abs(t1->data[k][j]), bpno + NMSEDEC_FRACBITS);
                     ff_aec_encode(&t1->aec, ctxno, abs(t1->data[k][j]) & mask ? 1:0);
                     t1->flags[k+1][j+1] |= J2K_T1_REF;
                 }
 }
 
-static void encode_clnpass(J2kT1Context *t1, int width, int height, int mask, int bandno)
+static void encode_clnpass(J2kT1Context *t1, int width, int height, int bandno, int *nmsedec, int bpno)
 {
-    int i, j, k;
+    int i, j, k, mask = 1 << (bpno + NMSEDEC_FRACBITS);
     for (i = 0; i < height; i += 4)
         for (j = 0; j < width; j++){
             if (i + 3 < height && !(
@@ -823,6 +892,7 @@ static void encode_clnpass(J2kT1Context 
                         if (abs(t1->data[k][j]) & mask){ // newly significant
                             int xorbit;
                             int ctxno = getsgnctxno(t1->flags[k+1][j+1], &xorbit);
+                            *nmsedec += getnmsedec_sig(abs(t1->data[k][j]), bpno + NMSEDEC_FRACBITS);
                             ff_aec_encode(&t1->aec, ctxno, (t1->data[k][j] < 0 ? 1:0) ^ xorbit);
                             set_significant(t1, j, k);
                         }
@@ -838,6 +908,7 @@ static void encode_clnpass(J2kT1Context 
                         if (abs(t1->data[k][j]) & mask){ // newly significant
                             int xorbit;
                             int ctxno = getsgnctxno(t1->flags[k+1][j+1], &xorbit);
+                            *nmsedec += getnmsedec_sig(abs(t1->data[k][j]), bpno + NMSEDEC_FRACBITS);
                             ff_aec_encode(&t1->aec, ctxno, (t1->data[k][j] < 0 ? 1:0) ^ xorbit);
                             set_significant(t1, j, k);
                         }
@@ -848,9 +919,11 @@ static void encode_clnpass(J2kT1Context 
         }
 }
 
-static void encode_cblk(J2kEncoderContext *s, J2kT1Context *t1, J2kCblk *cblk, int width, int height, int bandno)
+static void encode_cblk(J2kEncoderContext *s, J2kT1Context *t1, J2kCblk *cblk, J2kTile *tile,
+                        int width, int height, int bandpos, int lev)
 {
-    int pass_t = 2, passno, i, j, mask, max=0;
+    int pass_t = 2, passno, i, j, max=0, nmsedec, bpno;
+    double wmsedec = 0;
 
     for (i = 0; i < height+2; i++)
         bzero(t1->flags[i], (width+2)*sizeof(int));
@@ -862,33 +935,55 @@ static void encode_cblk(J2kEncoderContex
 
     if (max == 0){
         cblk->nonzerobits = 0;
-        mask = 0;
+        bpno = 0;
     }
     else{
-        cblk->nonzerobits = av_log2(max) + 1;
-        mask = 1 << (cblk->nonzerobits - 1);
+        cblk->nonzerobits = av_log2(max) + 1 - NMSEDEC_FRACBITS;
+        bpno = cblk->nonzerobits - 1;
     }
 
     ff_aec_initenc(&t1->aec, cblk->data);
 
-    for (passno = 0; mask != 0; passno++){
+    for (passno = 0; bpno >= 0; passno++){
+        double dr;
+        int drate;
+
+        nmsedec=0;
         switch(pass_t){
-            case 0: encode_sigpass(t1, width, height, mask, bandno);
+            case 0: encode_sigpass(t1, width, height, bandpos, &nmsedec, bpno);
                     break;
-            case 1: encode_refpass(t1, width, height, mask);
+            case 1: encode_refpass(t1, width, height, &nmsedec, bpno);
                     break;
-            case 2: encode_clnpass(t1, width, height, mask, bandno);
+            case 2: encode_clnpass(t1, width, height, bandpos, &nmsedec, bpno);
                     break;
         }
+
+        wmsedec += getwmsedec(nmsedec, bandpos, lev, bpno);
+        cblk->passess[passno].rate = 3 + ff_aec_length(&t1->aec);
+        cblk->passess[passno].disto = wmsedec;
+
+        drate = cblk->passess[passno].rate - (passno > 0 ? cblk->passess[passno-1].rate : 0);
+        if (drate > 0){
+            dr = (cblk->passess[passno].disto
+               - (passno > 0 ? cblk->passess[passno-1].disto : 0.0)) / drate;
+
+            if (dr < tile->mindr)
+                tile->mindr = dr;
+            if (dr > tile->maxdr)
+                tile->maxdr = dr;
+        }
+
         if (++pass_t == 3){
             pass_t = 0;
-            mask = mask >> 1;
+            bpno--;
         }
     }
     cblk->npassess = passno;
+    cblk->ninclpassess = passno;
 
     // TODO: optional flush on each pass
-    cblk->length = ff_aec_flush(&t1->aec);
+    cblk->passess[passno-1].rate = ff_aec_flush(&t1->aec);
+    tile->distortion += wmsedec;
 }
 
 /* tier-2 routines: */
@@ -946,7 +1041,7 @@ static void encode_packet(J2kEncoderCont
 
         for (pos=0, yi = band->prec[precno].yi0; yi < band->prec[precno].yi1; yi++){
             for (xi = band->prec[precno].xi0; xi < band->prec[precno].xi1; xi++, pos++){
-                cblkincl[pos].val = band->cblk[yi * cblknw + xi].npassess == 0 ? 1:0;
+                cblkincl[pos].val = band->cblk[yi * cblknw + xi].ninclpassess == 0 ? 1:0;
                 tag_tree_update(cblkincl + pos);
                 zerobits[pos].val = s->bbps[compno][rlevelno][bandno] - band->cblk[yi * cblknw + xi].nonzerobits;
                 tag_tree_update(zerobits + pos);
@@ -955,19 +1050,20 @@ static void encode_packet(J2kEncoderCont
 
         for (pos=0, yi = band->prec[precno].yi0; yi < band->prec[precno].yi1; yi++){
             for (xi = band->prec[precno].xi0; xi < band->prec[precno].xi1; xi++, pos++){
-                int pad = 0, llen;
+                int pad = 0, llen, length;
                 J2kCblk *cblk = band->cblk + yi * cblknw + xi;
 
                 // inclusion information
                 tag_tree_code(s, cblkincl + pos, 1);
-                if (!cblk->npassess)
+                if (!cblk->ninclpassess)
                     continue;
                 // zerobits information
                 tag_tree_code(s, zerobits + pos, 100);
                 // number of passess
-                putnumpassess(s, cblk->npassess);
+                putnumpassess(s, cblk->ninclpassess);
 
-                llen = av_log2(cblk->length) - av_log2(cblk->npassess) - 2;
+                length = cblk->passess[cblk->ninclpassess-1].rate;
+                llen = av_log2(length) - av_log2(cblk->ninclpassess) - 2;
                 if (llen < 0){
                     pad = -llen;
                     llen = 0;
@@ -975,7 +1071,7 @@ static void encode_packet(J2kEncoderCont
                 // length of code block
                 put_bits(s, 1, llen);
                 put_bits(s, 0, 1);
-                put_num(s, cblk->length, av_log2(cblk->length)+1+pad);
+                put_num(s, length, av_log2(length)+1+pad);
             }
         }
 
@@ -991,14 +1087,99 @@ static void encode_packet(J2kEncoderCont
             int xi;
             for (xi = band->prec[precno].xi0; xi < band->prec[precno].xi1; xi++){
                 J2kCblk *cblk = band->cblk + yi * cblknw + xi;
-                if (cblk->npassess)
-                    bytestream_put_buffer(&s->buf, cblk->data, cblk->length);
+                if (cblk->ninclpassess)
+                    bytestream_put_buffer(&s->buf, cblk->data, cblk->passess[cblk->ninclpassess-1].rate);
             }
         }
     }
 }
 
-static void encode_tile(J2kEncoderContext *s, int tileno)
+static void encode_packets(J2kEncoderContext *s, J2kTile *tile, int tileno)
+{
+    int compno, reslevelno;
+
+    av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
+    // lay-rlevel-comp-pos progression
+    for (reslevelno = 0; reslevelno < s->nreslevels; reslevelno++){
+        for (compno = 0; compno < s->ncomponents; compno++){
+            int precno;
+            J2kResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
+            for (precno = 0; precno < reslevel->nprecw * reslevel->nprech; precno++){
+                encode_packet(s, reslevel, precno, compno, reslevelno);
+            }
+        }
+    }
+    av_log(s->avctx, AV_LOG_DEBUG, "after tier2\n");
+}
+
+static int getcut(J2kCblk *cblk, double threshold)
+{
+    int passno, res = 0;
+    for (passno = 0; passno < cblk->npassess; passno++){
+        int dr;
+        double dd;
+
+        dr = cblk->passess[passno].rate
+           - (res ? cblk->passess[res-1].rate:0);
+        dd = cblk->passess[passno].disto
+           - (res ? cblk->passess[res-1].disto:0.0);
+
+        if (dd >= dr * threshold)
+            res = passno+1;
+    }
+    return res;
+}
+
+static void truncpassess(J2kEncoderContext *s, J2kTile *tile, double threshold)
+{
+    int compno, reslevelno, bandno, cblkno;
+    for (compno = 0; compno < s->ncomponents; compno++){
+        J2kComponent *comp = tile->comp + compno;
+
+        for (reslevelno = 0; reslevelno < s->nreslevels; reslevelno++){
+            J2kResLevel *reslevel = comp->reslevel + reslevelno;
+
+            for (bandno = 0; bandno < reslevel->nbands ; bandno++){
+                J2kBand *band = reslevel->band + bandno;
+
+                for (cblkno = 0; cblkno < band->cblknx * band->cblkny; cblkno++){
+                    J2kCblk *cblk = band->cblk + cblkno;
+
+                    cblk->ninclpassess = getcut(cblk, threshold);
+                }
+            }
+        }
+    }
+}
+
+static void rate_control(J2kEncoderContext *s, J2kTile *tile, int tileno)
+{
+    int i, ok;
+    uint8_t *oldbuf = s->buf;
+    double lo = tile->mindr, hi = tile->maxdr, resthres = lo;
+    for (i = 0; (i < 32); i++){
+        double thres = (lo+hi)/2;
+
+        ok = 0;
+        s->buf = oldbuf;
+        truncpassess(s, tile, thres);
+        encode_packets(s, tile, tileno);
+
+        if (s->buf - oldbuf > s->maxtilelen)
+            lo = thres;
+        else{
+            resthres = hi = thres;
+            ok=1;
+        }
+    }
+    if (!ok){
+        s->buf = oldbuf;
+        truncpassess(s, tile, resthres);
+        encode_packets(s, tile, tileno);
+    }
+}
+
+static void encode_tile(J2kEncoderContext *s, J2kTile *tile, int tileno)
 {
     int compno, reslevelno, bandno;
     J2kT1Context t1;
@@ -1014,7 +1195,7 @@ static void encode_tile(J2kEncoderContex
 
             for (bandno = 0; bandno < reslevel->nbands ; bandno++){
                 J2kBand *band = reslevel->band + bandno;
-                int cblkx, cblky, cblkno=0, xx0, x0, xx1, y0, yy0, yy1;
+                int cblkx, cblky, cblkno=0, xx0, x0, xx1, y0, yy0, yy1, bandpos;
                 yy0 = bandno == 0 ? 0 : comp->reslevel[reslevelno-1].y1 - comp->reslevel[reslevelno-1].y0;
                 y0 = yy0;
                 yy1 = FFMIN(ceildiv(band->y0 + 1, band->cblkh) * band->cblkh, band->y1) - band->y0 + yy0;
@@ -1022,6 +1203,8 @@ static void encode_tile(J2kEncoderContex
                 if (band->x0 == band->x1 || band->y0 == band->y1)
                     continue;
 
+                bandpos = bandno + (reslevelno > 0 ? 1:0);
+
                 for (cblky = 0; cblky < band->cblkny; cblky++){
                     if (reslevelno == 0 || bandno == 1)
                         xx0 = 0;
@@ -1035,9 +1218,10 @@ static void encode_tile(J2kEncoderContex
                         for (y = yy0; y < yy1; y++){
                             int *ptr = t1.data[y-yy0];
                             for (x = xx0; x < xx1; x++)
-                                *ptr++ = comp->data[(comp->x1 - comp->x0) * y + x];
+                                *ptr++ = comp->data[(comp->x1 - comp->x0) * y + x] << NMSEDEC_FRACBITS;
                         }
-                        encode_cblk(s, &t1, band->cblk + cblkno, xx1 - xx0, yy1 - yy0, bandno + (reslevelno > 0?1:0));
+                        encode_cblk(s, &t1, band->cblk + cblkno, tile, xx1 - xx0, yy1 - yy0,
+                                    bandpos, s->nreslevels - reslevelno - 1);
                         xx0 = xx1;
                         xx1 = FFMIN(xx1 + band->cblkw, band->x1 - band->x0 + x0);
                     }
@@ -1049,18 +1233,10 @@ static void encode_tile(J2kEncoderContex
         av_free(comp->data);
         av_log(s->avctx, AV_LOG_DEBUG, "after tier1\n");
     }
-    av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
-    // lay-rlevel-comp-pos progression
-    for (reslevelno = 0; reslevelno < s->nreslevels; reslevelno++){
-        for (compno = 0; compno < s->ncomponents; compno++){
-            int precno;
-            J2kResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
-            for (precno = 0; precno < reslevel->nprecw * reslevel->nprech; precno++){
-                encode_packet(s, reslevel, precno, compno, reslevelno);
-            }
-        }
-    }
-    av_log(s->avctx, AV_LOG_DEBUG, "after tier2\n");
+
+    av_log(s->avctx, AV_LOG_DEBUG, "rate control\n");
+    rate_control(s, tile, tileno);
+    av_log(s->avctx, AV_LOG_DEBUG, "after rate control\n");
 }
 
 void cleanup(J2kEncoderContext *s)
@@ -1113,6 +1289,7 @@ static int encode_frame(AVCodecContext *
     s->Ysiz = avctx->height;
 
     s->nguardbits = 1;
+    s->maxtilelen = 5000;
 
     // TODO: other pixel formats
     if (avctx->pix_fmt == PIX_FMT_RGB24){
@@ -1126,6 +1303,7 @@ static int encode_frame(AVCodecContext *
 
     av_log(s->avctx, AV_LOG_DEBUG, "init\n");
     init_tiles(s);
+    init_luts();
     av_log(s->avctx, AV_LOG_DEBUG, "after init\n");
 
     put_soc(s);
@@ -1137,7 +1315,7 @@ static int encode_frame(AVCodecContext *
         uint8_t *psotptr;
         psotptr = put_sot(s, tileno);
         put_marker(s, J2K_SOD);
-        encode_tile(s, tileno);
+        encode_tile(s, s->tile + tileno, tileno);
         bytestream_put_be32(&psotptr, s->buf - psotptr + 6);
     }
     put_marker(s, J2K_EOC);



More information about the FFmpeg-soc mailing list