blob: 3e979caf38d6e55263c8bebf45bfb8ce94174ae5 [file] [log] [blame]
#### 18.3 Sub-Pixel Interpolation {#h-18-03}
The sub-pixel interpolation is effected via two one-dimensional
convolutions. These convolutions may be thought of as operating on a
two-dimensional array of pixels whose origin is the subblock origin,
that is the origin of the prediction macroblock described above plus
the offset to the subblock. Because motion vectors are arbitrary, so
are these "prediction subblock origins".
The integer part of the motion vector is subsumed in the origin of
the prediction subblock; the 16 (synthetic) pixels we need to
construct are given by 16 offsets from the origin. The integer part
of each of these offsets is the offset of the corresponding pixel
from the subblock origin (using the vertical stride). To these
integer parts is added a constant fractional part, which is simply
the difference between the actual motion vector and its integer
truncation used to calculate the origins of the prediction macroblock
and subblock. Each component of this fractional part is an integer
between 0 and 7, representing a forward displacement in eighths of a
pixel.
It is these fractional displacements that determine the filtering
process. If they both happen to be zero (that is, we had a "whole
pixel" motion vector), the prediction subblock is simply copied into
the corresponding piece of the current macroblock's prediction
buffer. As discussed in Section 14, the layout of the macroblock's
prediction buffer can depend on the specifics of the reconstruction
implementation chosen. Of course, the vertical displacement between
lines of the prediction subblock is given by the stride, as are all
vertical displacements used here.
Otherwise, at least one of the fractional displacements is non-zero.
We then synthesize the missing pixels via a horizontal, followed by a
vertical, one-dimensional interpolation.
The two interpolations are essentially identical. Each uses a (at
most) six-tap filter (the choice of which of course depends on the
one-dimensional offset). Thus, every calculated pixel references at
most three pixels before (above or to the left of) it and at most
three pixels after (below or to the right of) it. The horizontal
interpolation must calculate two extra rows above and three extra
rows below the 4x4 block, to provide enough samples for the vertical
interpolation to proceed.
Depending on the reconstruction filter type given in the `version
number` field in the frame tag, either a bicubic or a bilinear tap set
is used.
The exact implementation of subsampling is as follows.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* Filter taps taken to 7-bit precision.
Because DC is always passed, taps always sum to 128. */
const int BilinearFilters[8][6] =
{
{ 0, 0, 128, 0, 0, 0 },
{ 0, 0, 112, 16, 0, 0 },
{ 0, 0, 96, 32, 0, 0 },
{ 0, 0, 80, 48, 0, 0 },
{ 0, 0, 64, 64, 0, 0 },
{ 0, 0, 48, 80, 0, 0 },
{ 0, 0, 32, 96, 0, 0 },
{ 0, 0, 16, 112, 0, 0 }
};
const int filters [8] [6] = { /* indexed by displacement */
{ 0, 0, 128, 0, 0, 0 }, /* degenerate whole-pixel */
{ 0, -6, 123, 12, -1, 0 }, /* 1/8 */
{ 2, -11, 108, 36, -8, 1 }, /* 1/4 */
{ 0, -9, 93, 50, -6, 0 }, /* 3/8 */
{ 3, -16, 77, 77, -16, 3 }, /* 1/2 is symmetric */
{ 0, -6, 50, 93, -9, 0 }, /* 5/8 = reverse of 3/8 */
{ 1, -8, 36, 108, -11, 2 }, /* 3/4 = reverse of 1/4 */
{ 0, -1, 12, 123, -6, 0 } /* 7/8 = reverse of 1/8 */
};
/* One-dimensional synthesis of a single sample.
Filter is determined by fractional displacement */
Pixel interp(
const int fil[6], /* filter to apply */
const Pixel *p, /* origin (rounded "before") in
prediction area */
const int s /* size of one forward step "" */
) {
int32 a = 0;
int i = 0;
p -= s + s; /* move back two positions */
do {
a += *p * fil[i];
p += s;
} while( ++i < 6);
return clamp255( (a + 64) >> 7); /* round to nearest
8-bit value */
}
/* First do horizontal interpolation, producing intermediate
buffer. */
void Hinterp(
Pixel temp[9][4], /* 9 rows of 4 (intermediate)
destination values */
const Pixel *p, /* subblock origin in prediction
frame */
int s, /* vertical stride to be used in
prediction frame */
uint hfrac, /* 0 <= horizontal displacement <= 7 */
uint bicubic /* 1=bicubic filter, 0=bilinear */
) {
const int * const fil = bicubic ? filters [hfrac] :
BilinearFilters[hfrac];
int r = 0; do /* for each row */
{
int c = 0; do /* for each destination sample */
{
/* Pixel separation = one horizontal step = 1 */
temp[r][c] = interp( fil, p + c, 1);
}
while( ++c < 4);
}
while( p += s, ++r < 9); /* advance p to next row */
}
/* Finish with vertical interpolation, producing final results.
Input array "temp" is of course that computed above. */
void Vinterp(
Pixel final[4][4], /* 4 rows of 4 (final) destination values */
const Pixel temp[9][4],
uint vfrac, /* 0 <= vertical displacement <= 7 */
uint bicubic /* 1=bicubic filter, 0=bilinear */
) {
const int * const fil = bicubic ? filters [vfrac] :
BilinearFilters[vfrac];
int r = 0; do /* for each row */
{
int c = 0; do /* for each destination sample */
{
/* Pixel separation = one vertical step = width
of array = 4 */
final[r][c] = interp( fil, temp[r] + c, 4);
}
while( ++c < 4);
}
while( ++r < 4);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{:lang="c"}