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