allow search with token buffer loop and fix PARTITION0 problem
The convergence algo is noticeably faster and more accurate. Try it with: 'cwebp -size xxxxx -pass 8 ...' or 'cwebp -psnr 39 -pass 8 ...' for instance Allow full-looping with TokenBuffer case, and make the non-TokenBuffer case match too. In case Partition0 is likely to overflow, retry encoding with harder limits on max_i4_header_bits_. This CL should make -partition_limit option somewhat useless, since the fix made automatically (albeit in a non-optimal way yet). Change-Id: I46fde3564188b13b89d4cb69f847a5f24b8c735b
This commit is contained in:
		
							
								
								
									
										239
									
								
								src/enc/frame.c
									
									
									
									
									
								
							
							
						
						
									
										239
									
								
								src/enc/frame.c
									
									
									
									
									
								
							@@ -18,6 +18,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "./vp8enci.h"
 | 
					#include "./vp8enci.h"
 | 
				
			||||||
#include "./cost.h"
 | 
					#include "./cost.h"
 | 
				
			||||||
 | 
					#include "../webp/format_constants.h"  // RIFF constants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__cplusplus) || defined(c_plusplus)
 | 
					#if defined(__cplusplus) || defined(c_plusplus)
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
@@ -39,6 +40,63 @@ typedef struct {
 | 
				
			|||||||
  CostArray*  cost;
 | 
					  CostArray*  cost;
 | 
				
			||||||
} VP8Residual;
 | 
					} VP8Residual;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// multi-pass convergence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE +  \
 | 
				
			||||||
 | 
					                              VP8_FRAME_HEADER_SIZE)
 | 
				
			||||||
 | 
					#define DQ_LIMIT 0.4  // convergence is considered reached if dq < DQ_LIMIT
 | 
				
			||||||
 | 
					// we allow 2k of extra head-room in PARTITION0 limit.
 | 
				
			||||||
 | 
					#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {  // struct for organizing convergence in either size or PSNR
 | 
				
			||||||
 | 
					  int is_first;
 | 
				
			||||||
 | 
					  float dq;
 | 
				
			||||||
 | 
					  float q, last_q;
 | 
				
			||||||
 | 
					  double value, last_value;   // PSNR or size
 | 
				
			||||||
 | 
					  double target;
 | 
				
			||||||
 | 
					  int do_size_search;
 | 
				
			||||||
 | 
					} PassStats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
 | 
				
			||||||
 | 
					  const uint64_t target_size = (uint64_t)enc->config_->target_size;
 | 
				
			||||||
 | 
					  const int do_size_search = (target_size != 0);
 | 
				
			||||||
 | 
					  const float target_PSNR = enc->config_->target_PSNR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  s->is_first = 1;
 | 
				
			||||||
 | 
					  s->dq = 10.f;
 | 
				
			||||||
 | 
					  s->q = s->last_q = enc->config_->quality;
 | 
				
			||||||
 | 
					  s->target = do_size_search ? (double)target_size
 | 
				
			||||||
 | 
					            : (target_PSNR > 0.) ? target_PSNR
 | 
				
			||||||
 | 
					            : 40.;   // default, just in case
 | 
				
			||||||
 | 
					  s->value = s->last_value = 0.;
 | 
				
			||||||
 | 
					  s->do_size_search = do_size_search;
 | 
				
			||||||
 | 
					  return do_size_search;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static float Clamp(float v, float min, float max) {
 | 
				
			||||||
 | 
					  return (v < min) ? min : (v > max) ? max : v;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static float ComputeNextQ(PassStats* const s) {
 | 
				
			||||||
 | 
					  float dq;
 | 
				
			||||||
 | 
					  if (s->is_first) {
 | 
				
			||||||
 | 
					    dq = (s->value > s->target) ? -s->dq : s->dq;
 | 
				
			||||||
 | 
					    s->is_first = 0;
 | 
				
			||||||
 | 
					  } else if (s->value != s->last_value) {
 | 
				
			||||||
 | 
					    const double slope = (s->target - s->value) / (s->last_value - s->value);
 | 
				
			||||||
 | 
					    dq = slope * (s->last_q - s->q);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    dq = 0.;  // we're done?!
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // Limit variable to avoid large swings.
 | 
				
			||||||
 | 
					  s->dq = Clamp(dq, -30.f, 30.f);
 | 
				
			||||||
 | 
					  s->last_q = s->q;
 | 
				
			||||||
 | 
					  s->last_value = s->value;
 | 
				
			||||||
 | 
					  s->q = Clamp(s->q + s->dq, 0.f, 100.f);
 | 
				
			||||||
 | 
					  return s->q;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
// Tables for level coding
 | 
					// Tables for level coding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -674,40 +732,37 @@ static void StoreSideInfo(const VP8EncIterator* const it) {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static double GetPSNR(uint64_t mse, uint64_t size) {
 | 
				
			||||||
 | 
					  return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
//  StatLoop(): only collect statistics (number of skips, token usage, ...).
 | 
					//  StatLoop(): only collect statistics (number of skips, token usage, ...).
 | 
				
			||||||
//  This is used for deciding optimal probabilities. It also modifies the
 | 
					//  This is used for deciding optimal probabilities. It also modifies the
 | 
				
			||||||
//  quantizer value if some target (size, PNSR) was specified.
 | 
					//  quantizer value if some target (size, PNSR) was specified.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define kHeaderSizeEstimate (15 + 20 + 10)      // TODO: fix better
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void SetLoopParams(VP8Encoder* const enc, float q) {
 | 
					static void SetLoopParams(VP8Encoder* const enc, float q) {
 | 
				
			||||||
  // Make sure the quality parameter is inside valid bounds
 | 
					  // Make sure the quality parameter is inside valid bounds
 | 
				
			||||||
  if (q < 0.) {
 | 
					  q = Clamp(q, 0.f, 100.f);
 | 
				
			||||||
    q = 0;
 | 
					 | 
				
			||||||
  } else if (q > 100.) {
 | 
					 | 
				
			||||||
    q = 100;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  VP8SetSegmentParams(enc, q);      // setup segment quantizations and filters
 | 
					  VP8SetSegmentParams(enc, q);      // setup segment quantizations and filters
 | 
				
			||||||
  SetSegmentProbas(enc);            // compute segment probabilities
 | 
					  SetSegmentProbas(enc);            // compute segment probabilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ResetStats(enc);
 | 
					  ResetStats(enc);
 | 
				
			||||||
  ResetTokenStats(enc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ResetSSE(enc);
 | 
					  ResetSSE(enc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int OneStatPass(VP8Encoder* const enc, float q, VP8RDLevel rd_opt,
 | 
					static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,
 | 
				
			||||||
                       int nb_mbs, float* const PSNR, int percent_delta) {
 | 
					                            int nb_mbs, int percent_delta,
 | 
				
			||||||
 | 
					                            PassStats* const s) {
 | 
				
			||||||
  VP8EncIterator it;
 | 
					  VP8EncIterator it;
 | 
				
			||||||
  uint64_t size = 0;
 | 
					  uint64_t size = 0;
 | 
				
			||||||
 | 
					  uint64_t size_p0 = 0;
 | 
				
			||||||
  uint64_t distortion = 0;
 | 
					  uint64_t distortion = 0;
 | 
				
			||||||
  const uint64_t pixel_count = nb_mbs * 384;
 | 
					  const uint64_t pixel_count = nb_mbs * 384;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SetLoopParams(enc, q);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VP8IteratorInit(enc, &it);
 | 
					  VP8IteratorInit(enc, &it);
 | 
				
			||||||
 | 
					  SetLoopParams(enc, s->q);
 | 
				
			||||||
  do {
 | 
					  do {
 | 
				
			||||||
    VP8ModeScore info;
 | 
					    VP8ModeScore info;
 | 
				
			||||||
    VP8IteratorImport(&it, NULL);
 | 
					    VP8IteratorImport(&it, NULL);
 | 
				
			||||||
@@ -717,39 +772,43 @@ static int OneStatPass(VP8Encoder* const enc, float q, VP8RDLevel rd_opt,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    RecordResiduals(&it, &info);
 | 
					    RecordResiduals(&it, &info);
 | 
				
			||||||
    size += info.R + info.H;
 | 
					    size += info.R + info.H;
 | 
				
			||||||
 | 
					    size_p0 += info.H;
 | 
				
			||||||
    distortion += info.D;
 | 
					    distortion += info.D;
 | 
				
			||||||
    if (percent_delta && !VP8IteratorProgress(&it, percent_delta))
 | 
					    if (percent_delta && !VP8IteratorProgress(&it, percent_delta))
 | 
				
			||||||
      return 0;
 | 
					      return 0;
 | 
				
			||||||
    VP8IteratorSaveBoundary(&it);
 | 
					    VP8IteratorSaveBoundary(&it);
 | 
				
			||||||
  } while (VP8IteratorNext(&it) && --nb_mbs > 0);
 | 
					  } while (VP8IteratorNext(&it) && --nb_mbs > 0);
 | 
				
			||||||
  size += FinalizeSkipProba(enc);
 | 
					 | 
				
			||||||
  size += FinalizeTokenProbas(&enc->proba_);
 | 
					 | 
				
			||||||
  size += enc->segment_hdr_.size_;
 | 
					 | 
				
			||||||
  size = ((size + 1024) >> 11) + kHeaderSizeEstimate;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (PSNR) {
 | 
					  size_p0 += enc->segment_hdr_.size_;
 | 
				
			||||||
    *PSNR = (float)(10.* log10(255. * 255. * pixel_count / distortion));
 | 
					  if (s->do_size_search) {
 | 
				
			||||||
 | 
					    size += FinalizeSkipProba(enc);
 | 
				
			||||||
 | 
					    size += FinalizeTokenProbas(&enc->proba_);
 | 
				
			||||||
 | 
					    size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE;
 | 
				
			||||||
 | 
					    s->value = (double)size;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    s->value = GetPSNR(distortion, pixel_count);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return (int)size;
 | 
					  return size_p0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// successive refinement increments.
 | 
					 | 
				
			||||||
static const int dqs[] = { 20, 15, 10, 8, 6, 4, 2, 1, 0 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int StatLoop(VP8Encoder* const enc) {
 | 
					static int StatLoop(VP8Encoder* const enc) {
 | 
				
			||||||
  const int method = enc->method_;
 | 
					  const int method = enc->method_;
 | 
				
			||||||
  const int do_search = enc->do_search_;
 | 
					  const int do_search = enc->do_search_;
 | 
				
			||||||
  const int fast_probe = ((method == 0 || method == 3) && !do_search);
 | 
					  const int fast_probe = ((method == 0 || method == 3) && !do_search);
 | 
				
			||||||
  float q = enc->config_->quality;
 | 
					  int num_pass_left = enc->config_->pass;
 | 
				
			||||||
  const int max_passes = enc->config_->pass;
 | 
					 | 
				
			||||||
  const int task_percent = 20;
 | 
					  const int task_percent = 20;
 | 
				
			||||||
  const int percent_per_pass = (task_percent + max_passes / 2) / max_passes;
 | 
					  const int percent_per_pass =
 | 
				
			||||||
 | 
					      (task_percent + num_pass_left / 2) / num_pass_left;
 | 
				
			||||||
  const int final_percent = enc->percent_ + task_percent;
 | 
					  const int final_percent = enc->percent_ + task_percent;
 | 
				
			||||||
  int pass;
 | 
					  const VP8RDLevel rd_opt =
 | 
				
			||||||
  int nb_mbs;
 | 
					      (method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE;
 | 
				
			||||||
 | 
					  int nb_mbs = enc->mb_w_ * enc->mb_h_;
 | 
				
			||||||
 | 
					  PassStats stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  InitPassStats(enc, &stats);
 | 
				
			||||||
 | 
					  ResetTokenStats(enc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Fast mode: quick analysis pass over few mbs. Better than nothing.
 | 
					  // Fast mode: quick analysis pass over few mbs. Better than nothing.
 | 
				
			||||||
  nb_mbs = enc->mb_w_ * enc->mb_h_;
 | 
					 | 
				
			||||||
  if (fast_probe) {
 | 
					  if (fast_probe) {
 | 
				
			||||||
    if (method == 3) {  // we need more stats for method 3 to be reliable.
 | 
					    if (method == 3) {  // we need more stats for method 3 to be reliable.
 | 
				
			||||||
      nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;
 | 
					      nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;
 | 
				
			||||||
@@ -758,37 +817,35 @@ static int StatLoop(VP8Encoder* const enc) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // No target size: just do several pass without changing 'q'
 | 
					  while (num_pass_left-- > 0) {
 | 
				
			||||||
  if (!do_search) {
 | 
					    const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
 | 
				
			||||||
    for (pass = 0; pass < max_passes; ++pass) {
 | 
					                             (num_pass_left == 0) ||
 | 
				
			||||||
      const VP8RDLevel rd_opt = (method >= 3) ? RD_OPT_BASIC : RD_OPT_NONE;
 | 
					                             (enc->max_i4_header_bits_ == 0);
 | 
				
			||||||
      if (!OneStatPass(enc, q, rd_opt, nb_mbs, NULL, percent_per_pass)) {
 | 
					    const uint64_t size_p0 =
 | 
				
			||||||
        return 0;
 | 
					        OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats);
 | 
				
			||||||
      }
 | 
					    if (size_p0 == 0) return 0;
 | 
				
			||||||
    }
 | 
					#if (DEBUG_SEARCH > 0)
 | 
				
			||||||
  } else {
 | 
					    printf("#%d value:%.1lf -> %.1lf   q:%.2f -> %.2f\n",
 | 
				
			||||||
    // binary search for a size close to target
 | 
					           num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q);
 | 
				
			||||||
    for (pass = 0; pass < max_passes && (dqs[pass] > 0); ++pass) {
 | 
					 | 
				
			||||||
      float PSNR;
 | 
					 | 
				
			||||||
      int criterion;
 | 
					 | 
				
			||||||
      const int size = OneStatPass(enc, q, RD_OPT_BASIC, nb_mbs, &PSNR,
 | 
					 | 
				
			||||||
                                   percent_per_pass);
 | 
					 | 
				
			||||||
#if DEBUG_SEARCH
 | 
					 | 
				
			||||||
      printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q);
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
      if (size == 0) return 0;
 | 
					    if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
 | 
				
			||||||
      if (enc->config_->target_PSNR > 0) {
 | 
					      ++num_pass_left;
 | 
				
			||||||
        criterion = (PSNR < enc->config_->target_PSNR);
 | 
					      enc->max_i4_header_bits_ >>= 1;  // strengthen header bit limitation...
 | 
				
			||||||
      } else {
 | 
					      continue;                        // ...and start over
 | 
				
			||||||
        criterion = (size < enc->config_->target_size);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // dichotomize
 | 
					 | 
				
			||||||
      if (criterion) {
 | 
					 | 
				
			||||||
        q += dqs[pass];
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        q -= dqs[pass];
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (is_last_pass) {
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // If no target size: just do several pass without changing 'q'
 | 
				
			||||||
 | 
					    if (do_search) {
 | 
				
			||||||
 | 
					      ComputeNextQ(&stats);
 | 
				
			||||||
 | 
					      if (fabs(stats.dq) <= DQ_LIMIT) break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!do_search || !stats.do_size_search) {
 | 
				
			||||||
 | 
					    // Need to finalize probas now, since it wasn't done during the search.
 | 
				
			||||||
 | 
					    FinalizeSkipProba(enc);
 | 
				
			||||||
 | 
					    FinalizeTokenProbas(&enc->proba_);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  VP8CalculateLevelCosts(&enc->proba_);  // finalize costs
 | 
					  VP8CalculateLevelCosts(&enc->proba_);  // finalize costs
 | 
				
			||||||
  return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
 | 
					  return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
 | 
				
			||||||
@@ -895,16 +952,23 @@ int VP8EncLoop(VP8Encoder* const enc) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#if !defined(DISABLE_TOKEN_BUFFER)
 | 
					#if !defined(DISABLE_TOKEN_BUFFER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MIN_COUNT 96   // minimum number of macroblocks before updating stats
 | 
					#define MIN_COUNT 96  // minimum number of macroblocks before updating stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int VP8EncTokenLoop(VP8Encoder* const enc) {
 | 
					int VP8EncTokenLoop(VP8Encoder* const enc) {
 | 
				
			||||||
  int ok;
 | 
					 | 
				
			||||||
  // Roughly refresh the proba eight times per pass
 | 
					  // Roughly refresh the proba eight times per pass
 | 
				
			||||||
  int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;
 | 
					  int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;
 | 
				
			||||||
  int num_pass_left = enc->config_->pass;
 | 
					  int num_pass_left = enc->config_->pass;
 | 
				
			||||||
 | 
					  const int do_search = enc->do_search_;
 | 
				
			||||||
  VP8EncIterator it;
 | 
					  VP8EncIterator it;
 | 
				
			||||||
  VP8Proba* const proba = &enc->proba_;
 | 
					  VP8Proba* const proba = &enc->proba_;
 | 
				
			||||||
  const VP8RDLevel rd_opt = enc->rd_opt_level_;
 | 
					  const VP8RDLevel rd_opt = enc->rd_opt_level_;
 | 
				
			||||||
 | 
					  const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384;
 | 
				
			||||||
 | 
					  PassStats stats;
 | 
				
			||||||
 | 
					  int ok;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  InitPassStats(enc, &stats);
 | 
				
			||||||
 | 
					  ok = PreLoopInitialize(enc);
 | 
				
			||||||
 | 
					  if (!ok) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (max_count < MIN_COUNT) max_count = MIN_COUNT;
 | 
					  if (max_count < MIN_COUNT) max_count = MIN_COUNT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -912,17 +976,18 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
 | 
				
			|||||||
  assert(enc->use_tokens_);
 | 
					  assert(enc->use_tokens_);
 | 
				
			||||||
  assert(proba->use_skip_proba_ == 0);
 | 
					  assert(proba->use_skip_proba_ == 0);
 | 
				
			||||||
  assert(rd_opt >= RD_OPT_BASIC);   // otherwise, token-buffer won't be useful
 | 
					  assert(rd_opt >= RD_OPT_BASIC);   // otherwise, token-buffer won't be useful
 | 
				
			||||||
  assert(!enc->do_search_);         // TODO(skal): handle pass and dichotomy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  SetLoopParams(enc, enc->config_->quality);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ok = PreLoopInitialize(enc);
 | 
					 | 
				
			||||||
  if (!ok) return 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  while (ok && num_pass_left-- > 0) {
 | 
					  while (ok && num_pass_left-- > 0) {
 | 
				
			||||||
 | 
					    const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
 | 
				
			||||||
 | 
					                             (num_pass_left == 0) ||
 | 
				
			||||||
 | 
					                             (enc->max_i4_header_bits_ == 0);
 | 
				
			||||||
 | 
					    uint64_t size_p0 = 0;
 | 
				
			||||||
 | 
					    uint64_t distortion = 0;
 | 
				
			||||||
    int cnt = max_count;
 | 
					    int cnt = max_count;
 | 
				
			||||||
    VP8IteratorInit(enc, &it);
 | 
					    VP8IteratorInit(enc, &it);
 | 
				
			||||||
    if (num_pass_left == 0) {
 | 
					    SetLoopParams(enc, stats.q);
 | 
				
			||||||
 | 
					    if (is_last_pass) {
 | 
				
			||||||
 | 
					      ResetTokenStats(enc);
 | 
				
			||||||
      VP8InitFilter(&it);  // don't collect stats until last pass (too costly)
 | 
					      VP8InitFilter(&it);  // don't collect stats until last pass (too costly)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    VP8TBufferClear(&enc->tokens_);
 | 
					    VP8TBufferClear(&enc->tokens_);
 | 
				
			||||||
@@ -936,12 +1001,14 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      VP8Decimate(&it, &info, rd_opt);
 | 
					      VP8Decimate(&it, &info, rd_opt);
 | 
				
			||||||
      RecordTokens(&it, &info, &enc->tokens_);
 | 
					      RecordTokens(&it, &info, &enc->tokens_);
 | 
				
			||||||
 | 
					      size_p0 += info.H;
 | 
				
			||||||
 | 
					      distortion += info.D;
 | 
				
			||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
 | 
					#ifdef WEBP_EXPERIMENTAL_FEATURES
 | 
				
			||||||
      if (enc->use_layer_) {
 | 
					      if (enc->use_layer_) {
 | 
				
			||||||
        VP8EncCodeLayerBlock(&it);
 | 
					        VP8EncCodeLayerBlock(&it);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
      if (num_pass_left == 0) {
 | 
					      if (is_last_pass) {
 | 
				
			||||||
        StoreSideInfo(&it);
 | 
					        StoreSideInfo(&it);
 | 
				
			||||||
        VP8StoreFilterStats(&it);
 | 
					        VP8StoreFilterStats(&it);
 | 
				
			||||||
        VP8IteratorExport(&it);
 | 
					        VP8IteratorExport(&it);
 | 
				
			||||||
@@ -949,13 +1016,45 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      VP8IteratorSaveBoundary(&it);
 | 
					      VP8IteratorSaveBoundary(&it);
 | 
				
			||||||
    } while (ok && VP8IteratorNext(&it));
 | 
					    } while (ok && VP8IteratorNext(&it));
 | 
				
			||||||
 | 
					    if (!ok) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_p0 += enc->segment_hdr_.size_;
 | 
				
			||||||
 | 
					    if (stats.do_size_search) {
 | 
				
			||||||
 | 
					      uint64_t size = FinalizeTokenProbas(&enc->proba_);
 | 
				
			||||||
 | 
					      size += VP8EstimateTokenSize(&enc->tokens_,
 | 
				
			||||||
 | 
					                                   (const uint8_t*)proba->coeffs_);
 | 
				
			||||||
 | 
					      size = (size + size_p0 + 1024) >> 11;  // -> size in bytes
 | 
				
			||||||
 | 
					      size += HEADER_SIZE_ESTIMATE;
 | 
				
			||||||
 | 
					      stats.value = (double)size;
 | 
				
			||||||
 | 
					    } else {  // compute and store PSNR
 | 
				
			||||||
 | 
					      stats.value = GetPSNR(distortion, pixel_count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (DEBUG_SEARCH > 0)
 | 
				
			||||||
 | 
					    printf("#%2d metric:%.1lf -> %.1lf   last_q=%.2lf q=%.2lf dq=%.2lf\n",
 | 
				
			||||||
 | 
					           num_pass_left, stats.last_value, stats.value,
 | 
				
			||||||
 | 
					           stats.last_q, stats.q, stats.dq);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    if (size_p0 > PARTITION0_SIZE_LIMIT) {
 | 
				
			||||||
 | 
					      ++num_pass_left;
 | 
				
			||||||
 | 
					      enc->max_i4_header_bits_ >>= 1;  // strengthen header bit limitation...
 | 
				
			||||||
 | 
					      continue;                        // ...and start over
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (is_last_pass) {
 | 
				
			||||||
 | 
					      break;   // done
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (do_search) {
 | 
				
			||||||
 | 
					      ComputeNextQ(&stats);  // Adjust q
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
 | 
					 | 
				
			||||||
  if (ok) {
 | 
					  if (ok) {
 | 
				
			||||||
    FinalizeTokenProbas(proba);
 | 
					    if (!stats.do_size_search) {
 | 
				
			||||||
 | 
					      FinalizeTokenProbas(&enc->proba_);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,
 | 
					    ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,
 | 
				
			||||||
                       (const uint8_t*)proba->coeffs_, 1);
 | 
					                       (const uint8_t*)proba->coeffs_, 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
 | 
				
			||||||
  return PostLoopFinalize(&it, ok);
 | 
					  return PostLoopFinalize(&it, ok);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,7 +136,7 @@ static void MapConfigToTools(VP8Encoder* const enc) {
 | 
				
			|||||||
  enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
 | 
					  enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
 | 
				
			||||||
  if (!config->low_memory) {
 | 
					  if (!config->low_memory) {
 | 
				
			||||||
#if !defined(DISABLE_TOKEN_BUFFER)
 | 
					#if !defined(DISABLE_TOKEN_BUFFER)
 | 
				
			||||||
    enc->use_tokens_ = (method >= 3) && !enc->do_search_;
 | 
					    enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC);  // need rd stats
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    if (enc->use_tokens_) {
 | 
					    if (enc->use_tokens_) {
 | 
				
			||||||
      enc->num_parts_ = 1;   // doesn't work with multi-partition
 | 
					      enc->num_parts_ = 1;   // doesn't work with multi-partition
 | 
				
			||||||
@@ -281,7 +281,7 @@ static int DeleteVP8Encoder(VP8Encoder* enc) {
 | 
				
			|||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static double GetPSNR(uint64_t err, uint64_t size) {
 | 
					static double GetPSNR(uint64_t err, uint64_t size) {
 | 
				
			||||||
  return err ? 10. * log10(255. * 255. * size / err) : 99.;
 | 
					  return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void FinalizePSNR(const VP8Encoder* const enc) {
 | 
					static void FinalizePSNR(const VP8Encoder* const enc) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user