1 /*
2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 /* MFQE: Multiframe Quality Enhancement
12 * In rate limited situations keyframes may cause significant visual artifacts
13 * commonly referred to as "popping." This file implements a postproccesing
14 * algorithm which blends data from the preceeding frame when there is no
15 * motion and the q from the previous frame is lower which indicates that it is
16 * higher quality.
17 */
18
19 #include "./vp8_rtcd.h"
20 #include "./vpx_dsp_rtcd.h"
21 #include "vp8/common/common.h"
22 #include "vp8/common/postproc.h"
23 #include "vpx_dsp/variance.h"
24 #include "vpx_mem/vpx_mem.h"
25 #include "vpx_scale/yv12config.h"
26
27 #include <limits.h>
28 #include <stdlib.h>
29
filter_by_weight(unsigned char * src,int src_stride,unsigned char * dst,int dst_stride,int block_size,int src_weight)30 static void filter_by_weight(unsigned char *src, int src_stride,
31 unsigned char *dst, int dst_stride, int block_size,
32 int src_weight) {
33 int dst_weight = (1 << MFQE_PRECISION) - src_weight;
34 int rounding_bit = 1 << (MFQE_PRECISION - 1);
35 int r, c;
36
37 for (r = 0; r < block_size; ++r) {
38 for (c = 0; c < block_size; ++c) {
39 dst[c] = (src[c] * src_weight + dst[c] * dst_weight + rounding_bit) >>
40 MFQE_PRECISION;
41 }
42 src += src_stride;
43 dst += dst_stride;
44 }
45 }
46
vp8_filter_by_weight16x16_c(unsigned char * src,int src_stride,unsigned char * dst,int dst_stride,int src_weight)47 void vp8_filter_by_weight16x16_c(unsigned char *src, int src_stride,
48 unsigned char *dst, int dst_stride,
49 int src_weight) {
50 filter_by_weight(src, src_stride, dst, dst_stride, 16, src_weight);
51 }
52
vp8_filter_by_weight8x8_c(unsigned char * src,int src_stride,unsigned char * dst,int dst_stride,int src_weight)53 void vp8_filter_by_weight8x8_c(unsigned char *src, int src_stride,
54 unsigned char *dst, int dst_stride,
55 int src_weight) {
56 filter_by_weight(src, src_stride, dst, dst_stride, 8, src_weight);
57 }
58
vp8_filter_by_weight4x4_c(unsigned char * src,int src_stride,unsigned char * dst,int dst_stride,int src_weight)59 void vp8_filter_by_weight4x4_c(unsigned char *src, int src_stride,
60 unsigned char *dst, int dst_stride,
61 int src_weight) {
62 filter_by_weight(src, src_stride, dst, dst_stride, 4, src_weight);
63 }
64
apply_ifactor(unsigned char * y_src,int y_src_stride,unsigned char * y_dst,int y_dst_stride,unsigned char * u_src,unsigned char * v_src,int uv_src_stride,unsigned char * u_dst,unsigned char * v_dst,int uv_dst_stride,int block_size,int src_weight)65 static void apply_ifactor(unsigned char *y_src, int y_src_stride,
66 unsigned char *y_dst, int y_dst_stride,
67 unsigned char *u_src, unsigned char *v_src,
68 int uv_src_stride, unsigned char *u_dst,
69 unsigned char *v_dst, int uv_dst_stride,
70 int block_size, int src_weight) {
71 if (block_size == 16) {
72 vp8_filter_by_weight16x16(y_src, y_src_stride, y_dst, y_dst_stride,
73 src_weight);
74 vp8_filter_by_weight8x8(u_src, uv_src_stride, u_dst, uv_dst_stride,
75 src_weight);
76 vp8_filter_by_weight8x8(v_src, uv_src_stride, v_dst, uv_dst_stride,
77 src_weight);
78 } else {
79 vp8_filter_by_weight8x8(y_src, y_src_stride, y_dst, y_dst_stride,
80 src_weight);
81 vp8_filter_by_weight4x4(u_src, uv_src_stride, u_dst, uv_dst_stride,
82 src_weight);
83 vp8_filter_by_weight4x4(v_src, uv_src_stride, v_dst, uv_dst_stride,
84 src_weight);
85 }
86 }
87
int_sqrt(unsigned int x)88 static unsigned int int_sqrt(unsigned int x) {
89 unsigned int y = x;
90 unsigned int guess;
91 int p = 1;
92 while (y >>= 1) p++;
93 p >>= 1;
94
95 guess = 0;
96 while (p >= 0) {
97 guess |= (1 << p);
98 if (x < guess * guess) guess -= (1 << p);
99 p--;
100 }
101 /* choose between guess or guess+1 */
102 return guess + (guess * guess + guess + 1 <= x);
103 }
104
105 #define USE_SSD
multiframe_quality_enhance_block(int blksize,int qcurr,int qprev,unsigned char * y,unsigned char * u,unsigned char * v,int y_stride,int uv_stride,unsigned char * yd,unsigned char * ud,unsigned char * vd,int yd_stride,int uvd_stride)106 static void multiframe_quality_enhance_block(
107 int blksize, /* Currently only values supported are 16, 8 */
108 int qcurr, int qprev, unsigned char *y, unsigned char *u, unsigned char *v,
109 int y_stride, int uv_stride, unsigned char *yd, unsigned char *ud,
110 unsigned char *vd, int yd_stride, int uvd_stride) {
111 static const unsigned char VP8_ZEROS[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 0, 0, 0, 0, 0, 0, 0 };
113 int uvblksize = blksize >> 1;
114 int qdiff = qcurr - qprev;
115
116 int i;
117 unsigned char *up;
118 unsigned char *udp;
119 unsigned char *vp;
120 unsigned char *vdp;
121
122 unsigned int act, actd, sad, usad, vsad, sse, thr, thrsq, actrisk;
123
124 if (blksize == 16) {
125 actd = (vpx_variance16x16(yd, yd_stride, VP8_ZEROS, 0, &sse) + 128) >> 8;
126 act = (vpx_variance16x16(y, y_stride, VP8_ZEROS, 0, &sse) + 128) >> 8;
127 #ifdef USE_SSD
128 vpx_variance16x16(y, y_stride, yd, yd_stride, &sse);
129 sad = (sse + 128) >> 8;
130 vpx_variance8x8(u, uv_stride, ud, uvd_stride, &sse);
131 usad = (sse + 32) >> 6;
132 vpx_variance8x8(v, uv_stride, vd, uvd_stride, &sse);
133 vsad = (sse + 32) >> 6;
134 #else
135 sad = (vpx_sad16x16(y, y_stride, yd, yd_stride) + 128) >> 8;
136 usad = (vpx_sad8x8(u, uv_stride, ud, uvd_stride) + 32) >> 6;
137 vsad = (vpx_sad8x8(v, uv_stride, vd, uvd_stride) + 32) >> 6;
138 #endif
139 } else {
140 actd = (vpx_variance8x8(yd, yd_stride, VP8_ZEROS, 0, &sse) + 32) >> 6;
141 act = (vpx_variance8x8(y, y_stride, VP8_ZEROS, 0, &sse) + 32) >> 6;
142 #ifdef USE_SSD
143 vpx_variance8x8(y, y_stride, yd, yd_stride, &sse);
144 sad = (sse + 32) >> 6;
145 vpx_variance4x4(u, uv_stride, ud, uvd_stride, &sse);
146 usad = (sse + 8) >> 4;
147 vpx_variance4x4(v, uv_stride, vd, uvd_stride, &sse);
148 vsad = (sse + 8) >> 4;
149 #else
150 sad = (vpx_sad8x8(y, y_stride, yd, yd_stride) + 32) >> 6;
151 usad = (vpx_sad4x4(u, uv_stride, ud, uvd_stride) + 8) >> 4;
152 vsad = (vpx_sad4x4(v, uv_stride, vd, uvd_stride) + 8) >> 4;
153 #endif
154 }
155
156 actrisk = (actd > act * 5);
157
158 /* thr = qdiff/16 + log2(act) + log4(qprev) */
159 thr = (qdiff >> 4);
160 while (actd >>= 1) thr++;
161 while (qprev >>= 2) thr++;
162
163 #ifdef USE_SSD
164 thrsq = thr * thr;
165 if (sad < thrsq &&
166 /* additional checks for color mismatch and excessive addition of
167 * high-frequencies */
168 4 * usad < thrsq && 4 * vsad < thrsq && !actrisk)
169 #else
170 if (sad < thr &&
171 /* additional checks for color mismatch and excessive addition of
172 * high-frequencies */
173 2 * usad < thr && 2 * vsad < thr && !actrisk)
174 #endif
175 {
176 int ifactor;
177 #ifdef USE_SSD
178 /* TODO: optimize this later to not need sqr root */
179 sad = int_sqrt(sad);
180 #endif
181 ifactor = (sad << MFQE_PRECISION) / thr;
182 ifactor >>= (qdiff >> 5);
183
184 if (ifactor) {
185 apply_ifactor(y, y_stride, yd, yd_stride, u, v, uv_stride, ud, vd,
186 uvd_stride, blksize, ifactor);
187 }
188 } else { /* else implicitly copy from previous frame */
189 if (blksize == 16) {
190 vp8_copy_mem16x16(y, y_stride, yd, yd_stride);
191 vp8_copy_mem8x8(u, uv_stride, ud, uvd_stride);
192 vp8_copy_mem8x8(v, uv_stride, vd, uvd_stride);
193 } else {
194 vp8_copy_mem8x8(y, y_stride, yd, yd_stride);
195 for (up = u, udp = ud, i = 0; i < uvblksize;
196 ++i, up += uv_stride, udp += uvd_stride) {
197 memcpy(udp, up, uvblksize);
198 }
199 for (vp = v, vdp = vd, i = 0; i < uvblksize;
200 ++i, vp += uv_stride, vdp += uvd_stride) {
201 memcpy(vdp, vp, uvblksize);
202 }
203 }
204 }
205 }
206
qualify_inter_mb(const MODE_INFO * mode_info_context,int * map)207 static int qualify_inter_mb(const MODE_INFO *mode_info_context, int *map) {
208 if (mode_info_context->mbmi.mb_skip_coeff) {
209 map[0] = map[1] = map[2] = map[3] = 1;
210 } else if (mode_info_context->mbmi.mode == SPLITMV) {
211 static int ndx[4][4] = {
212 { 0, 1, 4, 5 }, { 2, 3, 6, 7 }, { 8, 9, 12, 13 }, { 10, 11, 14, 15 }
213 };
214 int i, j;
215 vp8_zero(*map);
216 for (i = 0; i < 4; ++i) {
217 map[i] = 1;
218 for (j = 0; j < 4 && map[j]; ++j) {
219 map[i] &= (mode_info_context->bmi[ndx[i][j]].mv.as_mv.row <= 2 &&
220 mode_info_context->bmi[ndx[i][j]].mv.as_mv.col <= 2);
221 }
222 }
223 } else {
224 map[0] = map[1] = map[2] = map[3] =
225 (mode_info_context->mbmi.mode > B_PRED &&
226 abs(mode_info_context->mbmi.mv.as_mv.row) <= 2 &&
227 abs(mode_info_context->mbmi.mv.as_mv.col) <= 2);
228 }
229 return (map[0] + map[1] + map[2] + map[3]);
230 }
231
vp8_multiframe_quality_enhance(VP8_COMMON * cm)232 void vp8_multiframe_quality_enhance(VP8_COMMON *cm) {
233 YV12_BUFFER_CONFIG *show = cm->frame_to_show;
234 YV12_BUFFER_CONFIG *dest = &cm->post_proc_buffer;
235
236 FRAME_TYPE frame_type = cm->frame_type;
237 /* Point at base of Mb MODE_INFO list has motion vectors etc */
238 const MODE_INFO *mode_info_context = cm->mi;
239 int mb_row;
240 int mb_col;
241 int totmap, map[4];
242 int qcurr = cm->base_qindex;
243 int qprev = cm->postproc_state.last_base_qindex;
244
245 unsigned char *y_ptr, *u_ptr, *v_ptr;
246 unsigned char *yd_ptr, *ud_ptr, *vd_ptr;
247
248 /* Set up the buffer pointers */
249 y_ptr = show->y_buffer;
250 u_ptr = show->u_buffer;
251 v_ptr = show->v_buffer;
252 yd_ptr = dest->y_buffer;
253 ud_ptr = dest->u_buffer;
254 vd_ptr = dest->v_buffer;
255
256 /* postprocess each macro block */
257 for (mb_row = 0; mb_row < cm->mb_rows; ++mb_row) {
258 for (mb_col = 0; mb_col < cm->mb_cols; ++mb_col) {
259 /* if motion is high there will likely be no benefit */
260 if (frame_type == INTER_FRAME) {
261 totmap = qualify_inter_mb(mode_info_context, map);
262 } else {
263 totmap = (frame_type == KEY_FRAME ? 4 : 0);
264 }
265 if (totmap) {
266 if (totmap < 4) {
267 int i, j;
268 for (i = 0; i < 2; ++i) {
269 for (j = 0; j < 2; ++j) {
270 if (map[i * 2 + j]) {
271 multiframe_quality_enhance_block(
272 8, qcurr, qprev, y_ptr + 8 * (i * show->y_stride + j),
273 u_ptr + 4 * (i * show->uv_stride + j),
274 v_ptr + 4 * (i * show->uv_stride + j), show->y_stride,
275 show->uv_stride, yd_ptr + 8 * (i * dest->y_stride + j),
276 ud_ptr + 4 * (i * dest->uv_stride + j),
277 vd_ptr + 4 * (i * dest->uv_stride + j), dest->y_stride,
278 dest->uv_stride);
279 } else {
280 /* copy a 8x8 block */
281 int k;
282 unsigned char *up = u_ptr + 4 * (i * show->uv_stride + j);
283 unsigned char *udp = ud_ptr + 4 * (i * dest->uv_stride + j);
284 unsigned char *vp = v_ptr + 4 * (i * show->uv_stride + j);
285 unsigned char *vdp = vd_ptr + 4 * (i * dest->uv_stride + j);
286 vp8_copy_mem8x8(
287 y_ptr + 8 * (i * show->y_stride + j), show->y_stride,
288 yd_ptr + 8 * (i * dest->y_stride + j), dest->y_stride);
289 for (k = 0; k < 4; ++k, up += show->uv_stride,
290 udp += dest->uv_stride, vp += show->uv_stride,
291 vdp += dest->uv_stride) {
292 memcpy(udp, up, 4);
293 memcpy(vdp, vp, 4);
294 }
295 }
296 }
297 }
298 } else { /* totmap = 4 */
299 multiframe_quality_enhance_block(
300 16, qcurr, qprev, y_ptr, u_ptr, v_ptr, show->y_stride,
301 show->uv_stride, yd_ptr, ud_ptr, vd_ptr, dest->y_stride,
302 dest->uv_stride);
303 }
304 } else {
305 vp8_copy_mem16x16(y_ptr, show->y_stride, yd_ptr, dest->y_stride);
306 vp8_copy_mem8x8(u_ptr, show->uv_stride, ud_ptr, dest->uv_stride);
307 vp8_copy_mem8x8(v_ptr, show->uv_stride, vd_ptr, dest->uv_stride);
308 }
309 y_ptr += 16;
310 u_ptr += 8;
311 v_ptr += 8;
312 yd_ptr += 16;
313 ud_ptr += 8;
314 vd_ptr += 8;
315 mode_info_context++; /* step to next MB */
316 }
317
318 y_ptr += show->y_stride * 16 - 16 * cm->mb_cols;
319 u_ptr += show->uv_stride * 8 - 8 * cm->mb_cols;
320 v_ptr += show->uv_stride * 8 - 8 * cm->mb_cols;
321 yd_ptr += dest->y_stride * 16 - 16 * cm->mb_cols;
322 ud_ptr += dest->uv_stride * 8 - 8 * cm->mb_cols;
323 vd_ptr += dest->uv_stride * 8 - 8 * cm->mb_cols;
324
325 mode_info_context++; /* Skip border mb */
326 }
327 }
328