OLD | NEW |
1 // Copyright 2011 Google Inc. All Rights Reserved. | 1 // Copyright 2011 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Use of this source code is governed by a BSD-style license | 3 // Use of this source code is governed by a BSD-style license |
4 // that can be found in the COPYING file in the root of the source | 4 // that can be found in the COPYING file in the root of the source |
5 // tree. An additional intellectual property rights grant can be found | 5 // tree. An additional intellectual property rights grant can be found |
6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
9 // | 9 // |
10 // Macroblock analysis | 10 // Macroblock analysis |
11 // | 11 // |
12 // Author: Skal (pascal.massimino@gmail.com) | 12 // Author: Skal (pascal.massimino@gmail.com) |
13 | 13 |
14 #include <stdlib.h> | 14 #include <stdlib.h> |
15 #include <string.h> | 15 #include <string.h> |
16 #include <assert.h> | 16 #include <assert.h> |
17 | 17 |
18 #include "./vp8enci.h" | 18 #include "./vp8enci.h" |
19 #include "./cost.h" | 19 #include "./cost.h" |
20 #include "../utils/utils.h" | 20 #include "../utils/utils.h" |
21 | 21 |
22 #if defined(__cplusplus) || defined(c_plusplus) | |
23 extern "C" { | |
24 #endif | |
25 | |
26 #define MAX_ITERS_K_MEANS 6 | 22 #define MAX_ITERS_K_MEANS 6 |
27 | 23 |
28 //------------------------------------------------------------------------------ | 24 //------------------------------------------------------------------------------ |
29 // Smooth the segment map by replacing isolated block by the majority of its | 25 // Smooth the segment map by replacing isolated block by the majority of its |
30 // neighbours. | 26 // neighbours. |
31 | 27 |
32 static void SmoothSegmentMap(VP8Encoder* const enc) { | 28 static void SmoothSegmentMap(VP8Encoder* const enc) { |
33 int n, x, y; | 29 int n, x, y; |
34 const int w = enc->mb_w_; | 30 const int w = enc->mb_w_; |
35 const int h = enc->mb_h_; | 31 const int h = enc->mb_h_; |
(...skipping 12 matching lines...) Expand all Loading... |
48 cnt[mb[-w + 0].segment_]++; // top | 44 cnt[mb[-w + 0].segment_]++; // top |
49 cnt[mb[-w + 1].segment_]++; // top-right | 45 cnt[mb[-w + 1].segment_]++; // top-right |
50 cnt[mb[ - 1].segment_]++; // left | 46 cnt[mb[ - 1].segment_]++; // left |
51 cnt[mb[ + 1].segment_]++; // right | 47 cnt[mb[ + 1].segment_]++; // right |
52 cnt[mb[ w - 1].segment_]++; // bottom-left | 48 cnt[mb[ w - 1].segment_]++; // bottom-left |
53 cnt[mb[ w + 0].segment_]++; // bottom | 49 cnt[mb[ w + 0].segment_]++; // bottom |
54 cnt[mb[ w + 1].segment_]++; // bottom-right | 50 cnt[mb[ w + 1].segment_]++; // bottom-right |
55 for (n = 0; n < NUM_MB_SEGMENTS; ++n) { | 51 for (n = 0; n < NUM_MB_SEGMENTS; ++n) { |
56 if (cnt[n] >= majority_cnt_3_x_3_grid) { | 52 if (cnt[n] >= majority_cnt_3_x_3_grid) { |
57 majority_seg = n; | 53 majority_seg = n; |
| 54 break; |
58 } | 55 } |
59 } | 56 } |
60 tmp[x + y * w] = majority_seg; | 57 tmp[x + y * w] = majority_seg; |
61 } | 58 } |
62 } | 59 } |
63 for (y = 1; y < h - 1; ++y) { | 60 for (y = 1; y < h - 1; ++y) { |
64 for (x = 1; x < w - 1; ++x) { | 61 for (x = 1; x < w - 1; ++x) { |
65 VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; | 62 VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; |
66 mb->segment_ = tmp[x + y * w]; | 63 mb->segment_ = tmp[x + y * w]; |
67 } | 64 } |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 const int alphas[MAX_ALPHA + 1]) { | 143 const int alphas[MAX_ALPHA + 1]) { |
147 const int nb = enc->segment_hdr_.num_segments_; | 144 const int nb = enc->segment_hdr_.num_segments_; |
148 int centers[NUM_MB_SEGMENTS]; | 145 int centers[NUM_MB_SEGMENTS]; |
149 int weighted_average = 0; | 146 int weighted_average = 0; |
150 int map[MAX_ALPHA + 1]; | 147 int map[MAX_ALPHA + 1]; |
151 int a, n, k; | 148 int a, n, k; |
152 int min_a = 0, max_a = MAX_ALPHA, range_a; | 149 int min_a = 0, max_a = MAX_ALPHA, range_a; |
153 // 'int' type is ok for histo, and won't overflow | 150 // 'int' type is ok for histo, and won't overflow |
154 int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS]; | 151 int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS]; |
155 | 152 |
| 153 assert(nb >= 1); |
| 154 |
156 // bracket the input | 155 // bracket the input |
157 for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {} | 156 for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {} |
158 min_a = n; | 157 min_a = n; |
159 for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {} | 158 for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {} |
160 max_a = n; | 159 max_a = n; |
161 range_a = max_a - min_a; | 160 range_a = max_a - min_a; |
162 | 161 |
163 // Spread initial centers evenly | 162 // Spread initial centers evenly |
164 for (n = 1, k = 0; n < 2 * nb; n += 2) { | 163 for (k = 0, n = 1; k < nb; ++k, n += 2) { |
165 centers[k++] = min_a + (n * range_a) / (2 * nb); | 164 assert(n < 2 * nb); |
| 165 centers[k] = min_a + (n * range_a) / (2 * nb); |
166 } | 166 } |
167 | 167 |
168 for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough | 168 for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough |
169 int total_weight; | 169 int total_weight; |
170 int displaced; | 170 int displaced; |
171 // Reset stats | 171 // Reset stats |
172 for (n = 0; n < nb; ++n) { | 172 for (n = 0; n < nb; ++n) { |
173 accum[n] = 0; | 173 accum[n] = 0; |
174 dist_accum[n] = 0; | 174 dist_accum[n] = 0; |
175 } | 175 } |
176 // Assign nearest center for each 'a' | 176 // Assign nearest center for each 'a' |
177 n = 0; // track the nearest center for current 'a' | 177 n = 0; // track the nearest center for current 'a' |
178 for (a = min_a; a <= max_a; ++a) { | 178 for (a = min_a; a <= max_a; ++a) { |
179 if (alphas[a]) { | 179 if (alphas[a]) { |
180 while (n < nb - 1 && abs(a - centers[n + 1]) < abs(a - centers[n])) { | 180 while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) { |
181 n++; | 181 n++; |
182 } | 182 } |
183 map[a] = n; | 183 map[a] = n; |
184 // accumulate contribution into best centroid | 184 // accumulate contribution into best centroid |
185 dist_accum[n] += a * alphas[a]; | 185 dist_accum[n] += a * alphas[a]; |
186 accum[n] += alphas[a]; | 186 accum[n] += alphas[a]; |
187 } | 187 } |
188 } | 188 } |
189 // All point are classified. Move the centroids to the | 189 // All point are classified. Move the centroids to the |
190 // center of their respective cloud. | 190 // center of their respective cloud. |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 // this stage. | 377 // this stage. |
378 | 378 |
379 static void ResetAllMBInfo(VP8Encoder* const enc) { | 379 static void ResetAllMBInfo(VP8Encoder* const enc) { |
380 int n; | 380 int n; |
381 for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { | 381 for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { |
382 DefaultMBInfo(&enc->mb_info_[n]); | 382 DefaultMBInfo(&enc->mb_info_[n]); |
383 } | 383 } |
384 // Default susceptibilities. | 384 // Default susceptibilities. |
385 enc->dqm_[0].alpha_ = 0; | 385 enc->dqm_[0].alpha_ = 0; |
386 enc->dqm_[0].beta_ = 0; | 386 enc->dqm_[0].beta_ = 0; |
387 // Note: we can't compute this alpha_ / uv_alpha_. | 387 // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value. |
| 388 enc->alpha_ = 0; |
| 389 enc->uv_alpha_ = 0; |
388 WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); | 390 WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); |
389 } | 391 } |
390 | 392 |
| 393 // struct used to collect job result |
| 394 typedef struct { |
| 395 WebPWorker worker; |
| 396 int alphas[MAX_ALPHA + 1]; |
| 397 int alpha, uv_alpha; |
| 398 VP8EncIterator it; |
| 399 int delta_progress; |
| 400 } SegmentJob; |
| 401 |
| 402 // main work call |
| 403 static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) { |
| 404 int ok = 1; |
| 405 if (!VP8IteratorIsDone(it)) { |
| 406 uint8_t tmp[32 + ALIGN_CST]; |
| 407 uint8_t* const scratch = (uint8_t*)DO_ALIGN(tmp); |
| 408 do { |
| 409 // Let's pretend we have perfect lossless reconstruction. |
| 410 VP8IteratorImport(it, scratch); |
| 411 MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha); |
| 412 ok = VP8IteratorProgress(it, job->delta_progress); |
| 413 } while (ok && VP8IteratorNext(it)); |
| 414 } |
| 415 return ok; |
| 416 } |
| 417 |
| 418 static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) { |
| 419 int i; |
| 420 for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i]; |
| 421 dst->alpha += src->alpha; |
| 422 dst->uv_alpha += src->uv_alpha; |
| 423 } |
| 424 |
| 425 // initialize the job struct with some TODOs |
| 426 static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, |
| 427 int start_row, int end_row) { |
| 428 WebPWorkerInit(&job->worker); |
| 429 job->worker.data1 = job; |
| 430 job->worker.data2 = &job->it; |
| 431 job->worker.hook = (WebPWorkerHook)DoSegmentsJob; |
| 432 VP8IteratorInit(enc, &job->it); |
| 433 VP8IteratorSetRow(&job->it, start_row); |
| 434 VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_); |
| 435 memset(job->alphas, 0, sizeof(job->alphas)); |
| 436 job->alpha = 0; |
| 437 job->uv_alpha = 0; |
| 438 // only one of both jobs can record the progress, since we don't |
| 439 // expect the user's hook to be multi-thread safe |
| 440 job->delta_progress = (start_row == 0) ? 20 : 0; |
| 441 } |
| 442 |
| 443 // main entry point |
391 int VP8EncAnalyze(VP8Encoder* const enc) { | 444 int VP8EncAnalyze(VP8Encoder* const enc) { |
392 int ok = 1; | 445 int ok = 1; |
393 const int do_segments = | 446 const int do_segments = |
394 enc->config_->emulate_jpeg_size || // We need the complexity evaluation. | 447 enc->config_->emulate_jpeg_size || // We need the complexity evaluation. |
395 (enc->segment_hdr_.num_segments_ > 1) || | 448 (enc->segment_hdr_.num_segments_ > 1) || |
396 (enc->method_ == 0); // for method 0, we need preds_[] to be filled. | 449 (enc->method_ == 0); // for method 0, we need preds_[] to be filled. |
397 enc->alpha_ = 0; | |
398 enc->uv_alpha_ = 0; | |
399 if (do_segments) { | 450 if (do_segments) { |
400 int alphas[MAX_ALPHA + 1] = { 0 }; | 451 const int last_row = enc->mb_h_; |
401 VP8EncIterator it; | 452 // We give a little more than a half work to the main thread. |
402 | 453 const int split_row = (9 * last_row + 15) >> 4; |
403 VP8IteratorInit(enc, &it); | 454 const int total_mb = last_row * enc->mb_w_; |
404 do { | 455 #ifdef WEBP_USE_THREAD |
405 VP8IteratorImport(&it); | 456 const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it |
406 MBAnalyze(&it, alphas, &enc->alpha_, &enc->uv_alpha_); | 457 const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow); |
407 ok = VP8IteratorProgress(&it, 20); | 458 #else |
408 // Let's pretend we have perfect lossless reconstruction. | 459 const int do_mt = 0; |
409 } while (ok && VP8IteratorNext(&it, it.yuv_in_)); | 460 #endif |
410 enc->alpha_ /= enc->mb_w_ * enc->mb_h_; | 461 SegmentJob main_job; |
411 enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_; | 462 if (do_mt) { |
412 if (ok) AssignSegments(enc, alphas); | 463 SegmentJob side_job; |
| 464 // Note the use of '&' instead of '&&' because we must call the functions |
| 465 // no matter what. |
| 466 InitSegmentJob(enc, &main_job, 0, split_row); |
| 467 InitSegmentJob(enc, &side_job, split_row, last_row); |
| 468 // we don't need to call Reset() on main_job.worker, since we're calling |
| 469 // WebPWorkerExecute() on it |
| 470 ok &= WebPWorkerReset(&side_job.worker); |
| 471 // launch the two jobs in parallel |
| 472 if (ok) { |
| 473 WebPWorkerLaunch(&side_job.worker); |
| 474 WebPWorkerExecute(&main_job.worker); |
| 475 ok &= WebPWorkerSync(&side_job.worker); |
| 476 ok &= WebPWorkerSync(&main_job.worker); |
| 477 } |
| 478 WebPWorkerEnd(&side_job.worker); |
| 479 if (ok) MergeJobs(&side_job, &main_job); // merge results together |
| 480 } else { |
| 481 // Even for single-thread case, we use the generic Worker tools. |
| 482 InitSegmentJob(enc, &main_job, 0, last_row); |
| 483 WebPWorkerExecute(&main_job.worker); |
| 484 ok &= WebPWorkerSync(&main_job.worker); |
| 485 } |
| 486 WebPWorkerEnd(&main_job.worker); |
| 487 if (ok) { |
| 488 enc->alpha_ = main_job.alpha / total_mb; |
| 489 enc->uv_alpha_ = main_job.uv_alpha / total_mb; |
| 490 AssignSegments(enc, main_job.alphas); |
| 491 } |
413 } else { // Use only one default segment. | 492 } else { // Use only one default segment. |
414 ResetAllMBInfo(enc); | 493 ResetAllMBInfo(enc); |
415 } | 494 } |
416 return ok; | 495 return ok; |
417 } | 496 } |
418 | 497 |
419 #if defined(__cplusplus) || defined(c_plusplus) | |
420 } // extern "C" | |
421 #endif | |
OLD | NEW |