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