--- branches/dev-api-4/xvidcore/src/utils/mbtransquant.c 2003/05/06 23:41:29 1010 +++ branches/dev-api-4/xvidcore/src/utils/mbtransquant.c 2003/05/09 22:03:13 1011 @@ -21,7 +21,7 @@ * along with this program ; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: mbtransquant.c,v 1.21.2.9 2003-04-27 19:47:48 chl Exp $ + * $Id: mbtransquant.c,v 1.21.2.10 2003-05-09 22:03:13 chl Exp $ * ****************************************************************************/ @@ -35,6 +35,7 @@ #include "mem_transfer.h" #include "timer.h" #include "../bitstream/mbcoding.h" +#include "../bitstream/zigzag.h" #include "../dct/fdct.h" #include "../dct/idct.h" #include "../quant/quant_mpeg4.h" @@ -158,6 +159,14 @@ } } + +static int +dct_quantize_trellis_h263_c(int16_t *const Out, const int16_t *const In, int Q, const uint16_t * const Zigzag, int Non_Zero); + +static int +dct_quantize_trellis_mpeg_c(int16_t *const Out, const int16_t *const In, int Q, const uint16_t * const Zigzag, int Non_Zero); + + /* Quantize all blocks -- Inter mode */ static __inline uint8_t MBQuantInter(const MBParam * pParam, @@ -181,13 +190,13 @@ if (!(pParam->vol_flags & XVID_VOL_MPEGQUANT)) { sum = quant_inter(&qcoeff[i*64], &data[i*64], pMB->quant); if ( (sum) && (frame->vop_flags & XVID_VOP_TRELLISQUANT) ) { - sum = dct_quantize_trellis_inter_h263_c (&qcoeff[i*64], &data[i*64], pMB->quant)+1; + sum = dct_quantize_trellis_h263_c(&qcoeff[i*64], &data[i*64], pMB->quant, &scan_tables[0][0], 63)+1; limit = 1; } } else { sum = quant4_inter(&qcoeff[i * 64], &data[i * 64], pMB->quant); // if ( (sum) && (frame->vop_flags & XVID_VOP_TRELLISQUANT) ) -// sum = dct_quantize_trellis_inter_mpeg_c (&qcoeff[i*64], &data[i*64], pMB->quant)+1; +// sum = dct_quantize_trellis_mpeg_c (&qcoeff[i*64], &data[i*64], pMB->quant)+1; } stop_quant_timer(); @@ -590,3 +599,441 @@ MOVLINE(LINE(3, 5), LINE(3, 3)); MOVLINE(LINE(3, 3), tmp); } + + + + + +/************************************************************************ + * Trellis based R-D optimal quantization * + * * + * Trellis Quant code (C) 2003 Pascal Massimino skal(at)planet-d.net * + * * + ************************************************************************/ + + +static int +dct_quantize_trellis_inter_mpeg_c (int16_t *qcoeff, const int16_t *data, int quant) +{ return 63; } + + +////////////////////////////////////////////////////////// +// +// Trellis-Based quantization +// +// So far I understand this paper: +// +// "Trellis-Based R-D Optimal Quantization in H.263+" +// J.Wen, M.Luttrell, J.Villasenor +// IEEE Transactions on Image Processing, Vol.9, No.8, Aug. 2000. +// +// we are at stake with a simplified Bellmand-Ford / Dijkstra Single +// Source Shorted Path algo. But due to the underlying graph structure +// ("Trellis"), it can be turned into a dynamic programming algo, +// partially saving the explicit graph's nodes representation. And +// without using a heap, since the open frontier of the DAG is always +// known, and of fixed sized. +// +////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////// +// Codes lengths for relevant levels. + + // let's factorize: +static const uint8_t Code_Len0[64] = { + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len1[64] = { + 20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len2[64] = { + 19,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len3[64] = { + 18,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len4[64] = { + 17,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len5[64] = { + 16,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len6[64] = { + 15,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len7[64] = { + 13,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len8[64] = { + 11,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len9[64] = { + 12,21,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len10[64] = { + 12,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len11[64] = { + 12,19,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len12[64] = { + 11,17,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len13[64] = { + 11,15,21,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len14[64] = { + 10,12,19,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len15[64] = { + 10,13,17,19,21,21,21,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len16[64] = { + 9,12,13,18,18,19,19,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30}; +static const uint8_t Code_Len17[64] = { + 8,11,13,14,14,14,15,19,19,19,21,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len18[64] = { + 7, 9,11,11,13,13,13,15,15,15,16,22,22,22,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len19[64] = { + 5, 7, 9,10,10,11,11,11,11,11,13,14,16,17,17,18,18,18,18,18,18,18,18,20,20,21,21,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 }; +static const uint8_t Code_Len20[64] = { + 3, 4, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9,10,10,10,10,10,10,10,10,12,12,13,13,12,13,14,15,15, + 15,16,16,16,16,17,17,17,18,18,19,19,19,19,19,19,19,19,21,21,22,22,30,30,30,30,30,30,30,30,30,30 }; + + // a few more table for LAST table: +static const uint8_t Code_Len21[64] = { + 13,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30}; +static const uint8_t Code_Len22[64] = { + 12,15,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, + 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30}; +static const uint8_t Code_Len23[64] = { + 10,12,15,15,15,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,20,20,20, + 20,21,21,21,21,21,21,21,21,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30}; +static const uint8_t Code_Len24[64] = { + 5, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,11,11,11,11,12,12,12, + 12,13,13,13,13,13,13,13,13,14,16,16,16,16,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,19,19}; + + +static const uint8_t * const B16_17_Code_Len[24] = { // levels [1..24] + Code_Len20,Code_Len19,Code_Len18,Code_Len17, + Code_Len16,Code_Len15,Code_Len14,Code_Len13, + Code_Len12,Code_Len11,Code_Len10,Code_Len9, + Code_Len8, Code_Len7 ,Code_Len6 ,Code_Len5, + Code_Len4, Code_Len3, Code_Len3 ,Code_Len2, + Code_Len2, Code_Len1, Code_Len1, Code_Len1, +}; + +static const uint8_t * const B16_17_Code_Len_Last[6] = { // levels [1..6] + Code_Len24,Code_Len23,Code_Len22,Code_Len21, Code_Len3, Code_Len1, +}; + +#define TL(q) 0xfe00/(q*q) + +static const int Trellis_Lambda_Tabs[31] = { + TL( 1),TL( 2),TL( 3),TL( 4),TL( 5),TL( 6), TL( 7), + TL( 8),TL( 9),TL(10),TL(11),TL(12),TL(13),TL(14), TL(15), + TL(16),TL(17),TL(18),TL(19),TL(20),TL(21),TL(22), TL(23), + TL(24),TL(25),TL(26),TL(27),TL(28),TL(29),TL(30), TL(31) +}; +#undef TL + +static inline int Find_Last(const int16_t *C, const uint16_t *Zigzag, int i) +{ + while(i>=0) + if (C[Zigzag[i]]) + return i; + else i--; + return -1; +} + +////////////////////////////////////////////////////////// + +#define DBG 0 + +static uint32_t Evaluate_Cost(const int16_t *C, int Mult, int Bias, + const uint16_t * Zigzag, int Max, int Lambda) +{ +#if (DBG>0) + const int16_t * const Ref = C + 6*64; + int Last = Max; + while(Last>=0 && C[Zigzag[Last]]==0) Last--; + int Bits = 0; + if (Last>=0) { + Bits = 2; // CBP + int j=0, j0=0; + int Run, Level; + while(j=-24 && Level<=24) Bits += B16_17_Code_Len[(Level<0) ? -Level-1 : Level-1][Run]; + else Bits += 30; + } + Level = C[Zigzag[Last]]; + Run = j - j0; + if (Level>=-6 && Level<=6) Bits += B16_17_Code_Len_Last[(Level<0) ? -Level-1 : Level-1][Run]; + else Bits += 30; + } + + int Dist = 0; + int i; + for(i=0; i<=Last; ++i) { + int V = C[Zigzag[i]]*Mult; + if (V>0) V += Bias; + else if (V<0) V -= Bias; + V -= Ref[Zigzag[i]]; + Dist += V*V; + } + uint32_t Cost = Lambda*Dist + (Bits<<16); + if (DBG==1) + printf( " Last:%2d/%2d Cost = [(Bits=%5.0d) + Lambda*(Dist=%6.0d) = %d ] >>12= %d ", Last,Max, Bits, Dist, Cost, Cost>>12 ); + return Cost; + +#else + return 0; +#endif +} + + +static int +dct_quantize_trellis_h263_c(int16_t *const Out, const int16_t *const In, int Q, const uint16_t * const Zigzag, int Non_Zero) +{ + + // Note: We should search last non-zero coeffs on *real* DCT input coeffs (In[]), + // not quantized one (Out[]). However, it only improves the result *very* + // slightly (~0.01dB), whereas speed drops to crawling level :) + // Well, actually, taking 1 more coeff past Non_Zero into account sometimes helps, + + typedef struct { int16_t Run, Level; } NODE; + + NODE Nodes[65], Last; + uint32_t Run_Costs0[64+1], * const Run_Costs = Run_Costs0 + 1; + + const int Mult = 2*Q; + const int Bias = (Q-1) | 1; + const int Lev0 = Mult + Bias; + const int Lambda = Trellis_Lambda_Tabs[Q-1]; // it's 1/lambda, actually + + int Run_Start = -1; + Run_Costs[-1] = 2<<16; // source (w/ CBP penalty) + uint32_t Min_Cost = 2<<16; + + int Last_Node = -1; + uint32_t Last_Cost = 0; + +#if (DBG>0) + Last.Level = 0; Last.Run = -1; // just initialize to smthg +#endif + + int i, j; + + Non_Zero = Find_Last(Out, Zigzag, Non_Zero); + if (Non_Zero<0) + return -1; + + for(i=0; i<=Non_Zero; i++) + { + const int AC = In[Zigzag[i]]; + const int Level1 = Out[Zigzag[i]]; + const int Dist0 = Lambda* AC*AC; + uint32_t Best_Cost = 0xf0000000; + Last_Cost += Dist0; + + if ((uint32_t)(Level1+1)<3) // very specialized loop for -1,0,+1 + { + int dQ; + int Run; + + if (AC<0) { + Nodes[i].Level = -1; + dQ = Lev0 + AC; + } else { + Nodes[i].Level = 1; + dQ = Lev0 - AC; + } + const uint32_t Cost0 = Lambda*dQ*dQ; + + Nodes[i].Run = 1; + Best_Cost = (Code_Len20[0]<<16) + Run_Costs[i-1]+Cost0; + for(Run=i-Run_Start; Run>0; --Run) + { + const uint32_t Cost_Base = Cost0 + Run_Costs[i-Run]; + const uint32_t Cost = Cost_Base + (Code_Len20[Run-1]<<16); + // TODO: what about tie-breaks? Should we favor short runs or + // long runs? Although the error is the same, it would not be + // spread the same way along high and low frequencies... + if (Cost>12 ); + else if (j>Run_Start && j>12 ); + else if (j==i) printf( "(%3.0d)", Run_Costs[j]>>12 ); + else printf( " - |" ); + } + printf( "<%3.0d %2d %d>", Min_Cost>>12, Nodes[i].Level, Nodes[i].Run ); + printf( " Last:#%2d {%3.0d %2d %d}", Last_Node, Last_Cost>>12, Last.Level, Last.Run ); + printf( " AC:%3.0d Dist0:%3d Dist(%d)=%d", AC, Dist0>>12, Nodes[i].Level, Cost0>>12 ); + printf( "\n" ); + } + } + else // "big" levels + { + const uint8_t *Tbl_L1, *Tbl_L2, *Tbl_L1_Last, *Tbl_L2_Last; + int Level2; + int dQ1, dQ2; + int Run; + + if (Level1>1) { + dQ1 = Level1*Mult-AC + Bias; + dQ2 = dQ1 - Mult; + Level2 = Level1-1; + Tbl_L1 = (Level1<=24) ? B16_17_Code_Len[Level1-1] : Code_Len0; + Tbl_L2 = (Level2<=24) ? B16_17_Code_Len[Level2-1] : Code_Len0; + Tbl_L1_Last = (Level1<=6) ? B16_17_Code_Len_Last[Level1-1] : Code_Len0; + Tbl_L2_Last = (Level2<=6) ? B16_17_Code_Len_Last[Level2-1] : Code_Len0; + } + else { // Level1<-1 + dQ1 = Level1*Mult-AC - Bias; + dQ2 = dQ1 + Mult; + Level2 = Level1 + 1; + Tbl_L1 = (Level1>=-24) ? B16_17_Code_Len[Level1^-1] : Code_Len0; + Tbl_L2 = (Level2>=-24) ? B16_17_Code_Len[Level2^-1] : Code_Len0; + Tbl_L1_Last = (Level1>=- 6) ? B16_17_Code_Len_Last[Level1^-1] : Code_Len0; + Tbl_L2_Last = (Level2>=- 6) ? B16_17_Code_Len_Last[Level2^-1] : Code_Len0; + } + const uint32_t Dist1 = Lambda*dQ1*dQ1; + const uint32_t Dist2 = Lambda*dQ2*dQ2; + const int dDist21 = Dist2-Dist1; + + for(Run=i-Run_Start; Run>0; --Run) + { + const uint32_t Cost_Base = Dist1 + Run_Costs[i-Run]; + +// for sub-optimal (but slightly worth it, speed-wise) search, uncomment the following: +// if (Cost_Base>=Best_Cost) continue; + + uint32_t Cost1, Cost2; + int bLevel; + + Cost1 = Cost_Base + (Tbl_L1[Run-1]<<16); + Cost2 = Cost_Base + (Tbl_L2[Run-1]<<16) + dDist21; + + if (Cost2>12 ); + else if (j>Run_Start && j>12 ); + else if (j==i) printf( "(%3.0d)", Run_Costs[j]>>12 ); + else printf( " - |" ); + } + printf( "<%3.0d %2d %d>", Min_Cost>>12, Nodes[i].Level, Nodes[i].Run ); + printf( " Last:#%2d {%3.0d %2d %d}", Last_Node, Last_Cost>>12, Last.Level, Last.Run ); + printf( " AC:%3.0d Dist0:%3d Dist(%2d):%3d Dist(%2d):%3d", AC, Dist0>>12, Level1, Dist1>>12, Level2, Dist2>>12 ); + printf( "\n" ); + } + } + + Run_Costs[i] = Best_Cost; + + if (Best_Cost < Min_Cost + Dist0) { + Min_Cost = Best_Cost; + Run_Start = i; + } + else + { + // as noticed by Michael Niedermayer (michaelni at gmx.at), there's + // a code shorter by 1 bit for a larger run (!), same level. We give + // it a chance by not moving the left barrier too much. + while( Run_Costs[Run_Start]>Min_Cost+(1<<16) ) + Run_Start++; + + // spread on preceding coeffs the cost incurred by skipping this one + for(j=Run_Start; j " ); + for(i=0; i<=Non_Zero; ++i) printf( "[%3.0d] ", Out[Zigzag[i]] ); + printf( "\n" ); + } + } + + if (Last_Node<0) + return -1; + + // reconstruct optimal sequence backward with surviving paths + bzero(Out, 64*sizeof(*Out)); + Out[Zigzag[Last_Node]] = Last.Level; + i = Last_Node - Last.Run; + while(i>=0) { + Out[Zigzag[i]] = Nodes[i].Level; + i -= Nodes[i].Run; + } + + if (DBG) { + uint32_t Cost = Evaluate_Cost(Out,Mult,Bias, Zigzag,Non_Zero, Lambda); + if (DBG==1) { + printf( "<= " ); + for(i=0; i<=Last_Node; ++i) printf( "[%3.0d] ", Out[Zigzag[i]] ); + printf( "\n--------------------------------\n" ); + } + if (Cost>Last_Cost) printf( "!!! %u > %u\n", Cost, Last_Cost ); + } + return Last_Node; +} + +#undef DBG