void down2_symeven(const uint8_t *const input, int length, uint8_t *output,
int start_offset) { // Actual filter len = 2 * filter_len_half. const int16_t *filter = av1_down2_symeven_half_filter; const int filter_len_half = sizeof(av1_down2_symeven_half_filter) / 2;
int i, j;
uint8_t *optr = output;
int l1 = filter_len_half;
int l2 = (length - filter_len_half);
l1 += (l1 & 1);
l2 += (l2 & 1);
if (l1 > l2) { // Short input length.
for (i = start_offset; i < length; i += 2) {
int sum = (1 << (FILTER_BITS - 1));
for (j = 0; j < filter_len_half; ++j) {
sum +=
(input[AOMMAX(i - j, 0)] + input[AOMMIN(i + 1 + j, length - 1)]) *
filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
}
} else { // Initial part.
for (i = start_offset; i < l1; i += 2) {
int sum = (1 << (FILTER_BITS - 1));
for (j = 0; j < filter_len_half; ++j) {
sum += (input[AOMMAX(i - j, 0)] + input[i + 1 + j]) * filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
} // Middle part.
for (; i < l2; i += 2) {
int sum = (1 << (FILTER_BITS - 1));
for (j = 0; j < filter_len_half; ++j) {
sum += (input[i - j] + input[i + 1 + j]) * filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
} // End part.
for (; i < length; i += 2) {
int sum = (1 << (FILTER_BITS - 1));
for (j = 0; j < filter_len_half; ++j) {
sum +=
(input[i - j] + input[AOMMIN(i + 1 + j, length - 1)]) * filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
}
}
}
staticvoid down2_symodd(const uint8_t *const input, int length,
uint8_t *output) { // Actual filter len = 2 * filter_len_half - 1. const int16_t *filter = av1_down2_symodd_half_filter; const int filter_len_half = sizeof(av1_down2_symodd_half_filter) / 2;
int i, j;
uint8_t *optr = output;
int l1 = filter_len_half - 1;
int l2 = (length - filter_len_half + 1);
l1 += (l1 & 1);
l2 += (l2 & 1);
if (l1 > l2) { // Short input length.
for (i = 0; i < length; i += 2) {
int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
for (j = 1; j < filter_len_half; ++j) {
sum += (input[(i - j < 0 ? 0 : i - j)] +
input[(i + j >= length ? length - 1 : i + j)]) *
filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
}
} else { // Initial part.
for (i = 0; i < l1; i += 2) {
int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
for (j = 1; j < filter_len_half; ++j) {
sum += (input[(i - j < 0 ? 0 : i - j)] + input[i + j]) * filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
} // Middle part.
for (; i < l2; i += 2) {
int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
for (j = 1; j < filter_len_half; ++j) {
sum += (input[i - j] + input[i + j]) * filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
} // End part.
for (; i < length; i += 2) {
int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
for (j = 1; j < filter_len_half; ++j) {
sum += (input[i - j] + input[(i + j >= length ? length - 1 : i + j)]) *
filter[j];
}
sum >>= FILTER_BITS;
*optr++ = clip_pixel(sum);
}
}
}
static int get_down2_length(int length, int steps) {
for (int s = 0; s < steps; ++s) length = (length + 1) >> 1; return length;
}
static int get_down2_steps(int in_length, int out_length) {
int steps = 0;
int proj_in_length; while ((proj_in_length = get_down2_length(in_length, 1)) >= out_length) {
++steps;
in_length = proj_in_length;
if (in_length == 1) { // Special case: we break because any further calls to get_down2_length() // with be with length == 1, which return 1, resulting in an infinite // loop. break;
}
} return steps;
}
staticvoid resize_multistep(const uint8_t *const input, int length,
uint8_t *output, int olength, uint8_t *otmp) {
if (length == olength) {
memcpy(output, input, sizeof(output[0]) * length); return;
} const int steps = get_down2_steps(length, olength);
if (steps > 0) {
uint8_t *out = NULL;
int filteredlength = length;
assert(otmp != NULL);
uint8_t *otmp2 = otmp + get_down2_length(length, 1);
for (int s = 0; s < steps; ++s) { const int proj_filteredlength = get_down2_length(filteredlength, 1); const uint8_t *const in = (s == 0 ? input : out);
if (s == steps - 1 && proj_filteredlength == olength)
out = output; else
out = (s & 1 ? otmp2 : otmp);
if (filteredlength & 1)
down2_symodd(in, filteredlength, out); else
down2_symeven(in, filteredlength, out, 0);
filteredlength = proj_filteredlength;
}
if (filteredlength != olength) {
interpolate(out, filteredlength, output, olength);
}
} else {
interpolate(input, length, output, olength);
}
}
staticvoid fill_col_to_arr(uint8_t *img, int stride, int len, uint8_t *arr) {
int i;
uint8_t *iptr = img;
uint8_t *aptr = arr;
for (i = 0; i < len; ++i, iptr += stride) {
*aptr++ = *iptr;
}
}
staticvoid fill_arr_to_col(uint8_t *img, int stride, int len, uint8_t *arr) {
int i;
uint8_t *iptr = img;
uint8_t *aptr = arr;
for (i = 0; i < len; ++i, iptr += stride) {
*iptr = *aptr++;
}
}
bool av1_resize_vert_dir_c(uint8_t *intbuf, uint8_t *output, int out_stride,
int height, int height2, int width2, int start_col) { bool mem_status = true;
uint8_t *arrbuf = (uint8_t *)aom_malloc(sizeof(*arrbuf) * height);
uint8_t *arrbuf2 = (uint8_t *)aom_malloc(sizeof(*arrbuf2) * height2);
if (arrbuf == NULL || arrbuf2 == NULL) {
mem_status = false;
goto Error;
}
for (int i = start_col; i < width2; ++i) {
fill_col_to_arr(intbuf + i, width2, height, arrbuf);
down2_symeven(arrbuf, height, arrbuf2, 0);
fill_arr_to_col(output + i, out_stride, height2, arrbuf2);
}
void av1_resize_horz_dir_c(const uint8_t *const input, int in_stride,
uint8_t *intbuf, int height, int filtered_length,
int width2) {
for (int i = 0; i < height; ++i)
down2_symeven(input + in_stride * i, filtered_length, intbuf + width2 * i, 0);
}
bool av1_resize_plane_to_half(const uint8_t *const input, int height, int width,
int in_stride, uint8_t *output, int height2,
int width2, int out_stride) {
uint8_t *intbuf = (uint8_t *)aom_malloc(sizeof(*intbuf) * width2 * height);
if (intbuf == NULL) { return false;
}
// Resize in the horizontal direction
av1_resize_horz_dir(input, in_stride, intbuf, height, width, width2); // Resize in the vertical direction bool mem_status = av1_resize_vert_dir(intbuf, output, out_stride, height,
height2, width2, 0/*start_col*/);
aom_free(intbuf); return mem_status;
}
// Check if both the output width and height are half of input width and // height respectively. bool should_resize_by_half(int height, int width, int height2, int width2) { constbool is_width_by_2 = get_down2_length(width, 1) == width2; constbool is_height_by_2 = get_down2_length(height, 1) == height2; return (is_width_by_2 && is_height_by_2);
}
bool av1_resize_plane(const uint8_t *input, int height, int width,
int in_stride, uint8_t *output, int height2, int width2,
int out_stride) {
int i; bool mem_status = true;
uint8_t *intbuf = (uint8_t *)aom_malloc(sizeof(uint8_t) * width2 * height);
uint8_t *tmpbuf =
(uint8_t *)aom_malloc(sizeof(uint8_t) * AOMMAX(width, height));
uint8_t *arrbuf = (uint8_t *)aom_malloc(sizeof(uint8_t) * height);
uint8_t *arrbuf2 = (uint8_t *)aom_malloc(sizeof(uint8_t) * height2);
if (intbuf == NULL || tmpbuf == NULL || arrbuf == NULL || arrbuf2 == NULL) {
mem_status = false;
goto Error;
}
assert(width > 0);
assert(height > 0);
assert(width2 > 0);
assert(height2 > 0);
for (i = 0; i < height; ++i)
resize_multistep(input + in_stride * i, width, intbuf + width2 * i, width2,
tmpbuf);
for (i = 0; i < width2; ++i) {
fill_col_to_arr(intbuf + i, width2, height, arrbuf);
resize_multistep(arrbuf, height, arrbuf2, height2, tmpbuf);
fill_arr_to_col(output + i, out_stride, height2, arrbuf2);
}
staticbool upscale_normative_rect(const uint8_t *const input, int height,
int width, int in_stride, uint8_t *output,
int height2, int width2, int out_stride,
int x_step_qn, int x0_qn, int pad_left,
int pad_right) {
assert(width > 0);
assert(height > 0);
assert(width2 > 0);
assert(height2 > 0);
assert(height2 == height);
// Extend the left/right pixels of the tile column if needed // (either because we can't sample from other tiles, or because we're at // a frame edge). // Save the overwritten pixels into tmp_left and tmp_right. // Note: Because we pass input-1 to av1_convolve_horiz_rs, we need one extra // column of border pixels compared to what we'd naively think. const int border_cols = UPSCALE_NORMATIVE_TAPS / 2 + 1;
uint8_t *tmp_left =
NULL; // Silence spurious "may be used uninitialized" warnings
uint8_t *tmp_right = NULL;
uint8_t *const in_tl = (uint8_t *)(input - border_cols); // Cast off 'const'
uint8_t *const in_tr = (uint8_t *)(input + width);
if (pad_left) {
tmp_left = (uint8_t *)aom_malloc(sizeof(*tmp_left) * border_cols * height);
if (!tmp_left) return false;
for (int i = 0; i < height; i++) {
memcpy(tmp_left + i * border_cols, in_tl + i * in_stride, border_cols);
memset(in_tl + i * in_stride, input[i * in_stride], border_cols);
}
}
if (pad_right) {
tmp_right =
(uint8_t *)aom_malloc(sizeof(*tmp_right) * border_cols * height);
if (!tmp_right) {
aom_free(tmp_left); return false;
}
for (int i = 0; i < height; i++) {
memcpy(tmp_right + i * border_cols, in_tr + i * in_stride, border_cols);
memset(in_tr + i * in_stride, input[i * in_stride + width - 1],
border_cols);
}
}
// Restore the left/right border pixels
if (pad_left) {
for (int i = 0; i < height; i++) {
memcpy(in_tl + i * in_stride, tmp_left + i * border_cols, border_cols);
}
aom_free(tmp_left);
}
if (pad_right) {
for (int i = 0; i < height; i++) {
memcpy(in_tr + i * in_stride, tmp_right + i * border_cols, border_cols);
}
aom_free(tmp_right);
} returntrue;
}
staticbool highbd_upscale_normative_rect(const uint8_t *const input,
int height, int width, int in_stride,
uint8_t *output, int height2,
int width2, int out_stride,
int x_step_qn, int x0_qn,
int pad_left, int pad_right, int bd) {
assert(width > 0);
assert(height > 0);
assert(width2 > 0);
assert(height2 > 0);
assert(height2 == height);
// Extend the left/right pixels of the tile column if needed // (either because we can't sample from other tiles, or because we're at // a frame edge). // Save the overwritten pixels into tmp_left and tmp_right. // Note: Because we pass input-1 to av1_convolve_horiz_rs, we need one extra // column of border pixels compared to what we'd naively think. const int border_cols = UPSCALE_NORMATIVE_TAPS / 2 + 1; const int border_size = border_cols * sizeof(uint16_t);
uint16_t *tmp_left =
NULL; // Silence spurious "may be used uninitialized" warnings
uint16_t *tmp_right = NULL;
uint16_t *const input16 = CONVERT_TO_SHORTPTR(input);
uint16_t *const in_tl = input16 - border_cols;
uint16_t *const in_tr = input16 + width;
if (pad_left) {
tmp_left = (uint16_t *)aom_malloc(sizeof(*tmp_left) * border_cols * height);
if (!tmp_left) return false;
for (int i = 0; i < height; i++) {
memcpy(tmp_left + i * border_cols, in_tl + i * in_stride, border_size);
aom_memset16(in_tl + i * in_stride, input16[i * in_stride], border_cols);
}
}
if (pad_right) {
tmp_right =
(uint16_t *)aom_malloc(sizeof(*tmp_right) * border_cols * height);
if (!tmp_right) {
aom_free(tmp_left); return false;
}
for (int i = 0; i < height; i++) {
memcpy(tmp_right + i * border_cols, in_tr + i * in_stride, border_size);
aom_memset16(in_tr + i * in_stride, input16[i * in_stride + width - 1],
border_cols);
}
}
// Restore the left/right border pixels
if (pad_left) {
for (int i = 0; i < height; i++) {
memcpy(in_tl + i * in_stride, tmp_left + i * border_cols, border_size);
}
aom_free(tmp_left);
}
if (pad_right) {
for (int i = 0; i < height; i++) {
memcpy(in_tr + i * in_stride, tmp_right + i * border_cols, border_size);
}
aom_free(tmp_right);
} returntrue;
} #endif// CONFIG_AV1_HIGHBITDEPTH
for (int j = 0; j < cm->tiles.cols; j++) {
av1_tile_set_col(&tile_col, cm, j); // Determine the limits of this tile column in both the source // and destination images. // Note: The actual location which we start sampling from is // (downscaled_x0 - 1 + (x0_qn/2^14)), and this quantity increases // by exactly dst_width * (x_step_qn/2^14) pixels each iteration. const int downscaled_x0 = tile_col.mi_col_start << (MI_SIZE_LOG2 - ss_x); const int downscaled_x1 = tile_col.mi_col_end << (MI_SIZE_LOG2 - ss_x); const int src_width = downscaled_x1 - downscaled_x0;
const int upscaled_x0 = (downscaled_x0 * superres_denom) / SCALE_NUMERATOR;
int upscaled_x1;
if (j == cm->tiles.cols - 1) { // Note that we can't just use AOMMIN here - due to rounding, // (downscaled_x1 * superres_denom) / SCALE_NUMERATOR may be less than // upscaled_plane_width.
upscaled_x1 = upscaled_plane_width;
} else {
upscaled_x1 = (downscaled_x1 * superres_denom) / SCALE_NUMERATOR;
}
staticvoid upscale_normative_and_extend_frame(const AV1_COMMON *cm, const YV12_BUFFER_CONFIG *src,
YV12_BUFFER_CONFIG *dst) { const int num_planes = av1_num_planes(cm);
for (int i = 0; i < num_planes; ++i) { const int is_uv = (i > 0);
av1_upscale_normative_rows(cm, src->buffers[i], src->strides[is_uv],
dst->buffers[i], dst->strides[is_uv], i,
src->crop_heights[is_uv]);
}
aom_extend_frame_borders(dst, num_planes);
}
YV12_BUFFER_CONFIG *av1_realloc_and_scale_if_required(
AV1_COMMON *cm, YV12_BUFFER_CONFIG *unscaled, YV12_BUFFER_CONFIG *scaled, const InterpFilter filter, const int phase, constbool use_optimized_scaler, constbool for_psnr, const int border_in_pixels, constbool alloc_pyramid) { // If scaling is performed for the sole purpose of calculating PSNR, then our // target dimensions are superres upscaled width/height. Otherwise our target // dimensions are coded width/height. const int scaled_width = for_psnr ? cm->superres_upscaled_width : cm->width; const int scaled_height =
for_psnr ? cm->superres_upscaled_height : cm->height; constbool scaling_required = (scaled_width != unscaled->y_crop_width) ||
(scaled_height != unscaled->y_crop_height);
if (scaling_required) { const int num_planes = av1_num_planes(cm); const SequenceHeader *seq_params = cm->seq_params;
// Reallocate the frame buffer based on the target dimensions when scaling // is required.
if (aom_realloc_frame_buffer(
scaled, scaled_width, scaled_height, seq_params->subsampling_x,
seq_params->subsampling_y, seq_params->use_highbitdepth,
border_in_pixels, cm->features.byte_alignment, NULL, NULL, NULL,
alloc_pyramid, 0))
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, "Failed to allocate scaled buffer");
#if CONFIG_AV1_HIGHBITDEPTH
if (use_optimized_scaler && has_optimized_scaler &&
cm->seq_params->bit_depth == AOM_BITS_8) {
av1_resize_and_extend_frame(unscaled, scaled, filter, phase, num_planes);
} else {
if (!av1_resize_and_extend_frame_nonnormative(
unscaled, scaled, (int)cm->seq_params->bit_depth, num_planes))
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, "Failed to allocate buffers during resize");
} #else
if (use_optimized_scaler && has_optimized_scaler) {
av1_resize_and_extend_frame(unscaled, scaled, filter, phase, num_planes);
} else {
if (!av1_resize_and_extend_frame_nonnormative(
unscaled, scaled, (int)cm->seq_params->bit_depth, num_planes))
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, "Failed to allocate buffers during resize");
} #endif
if (unscaled->metadata &&
aom_copy_metadata_to_frame_buffer(scaled, unscaled->metadata)) {
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, "Failed to copy source metadata to scaled frame");
} return scaled;
} return unscaled;
}
// Calculates the scaled dimension given the original dimension and the scale // denominator. staticvoid calculate_scaled_size_helper(int *dim, int denom) {
if (denom != SCALE_NUMERATOR) { // We need to ensure the constraint in "Appendix A" of the spec: // * FrameWidth is greater than or equal to 16 // * FrameHeight is greater than or equal to 16 // For this, we clamp the downscaled dimension to at least 16. One // exception: if original dimension itself was < 16, then we keep the // downscaled dimension to be same as the original, to ensure that resizing // is valid. const int min_dim = AOMMIN(16, *dim); // Use this version if we need *dim to be even // *width = (*width * SCALE_NUMERATOR + denom) / (2 * denom); // *width <<= 1;
*dim = (*dim * SCALE_NUMERATOR + denom / 2) / (denom);
*dim = AOMMAX(*dim, min_dim);
}
}
void av1_calculate_scaled_size(int *width, int *height, int resize_denom) {
calculate_scaled_size_helper(width, resize_denom);
calculate_scaled_size_helper(height, resize_denom);
}
void av1_calculate_scaled_superres_size(int *width, int *height,
int superres_denom) {
(void)height;
calculate_scaled_size_helper(width, superres_denom);
}
// Copy only the config data from 'src' to 'dst'. staticvoid copy_buffer_config(const YV12_BUFFER_CONFIG *const src,
YV12_BUFFER_CONFIG *const dst) {
dst->bit_depth = src->bit_depth;
dst->color_primaries = src->color_primaries;
dst->transfer_characteristics = src->transfer_characteristics;
dst->matrix_coefficients = src->matrix_coefficients;
dst->monochrome = src->monochrome;
dst->chroma_sample_position = src->chroma_sample_position;
dst->color_range = src->color_range;
}
// TODO(afergs): Look for in-place upscaling // TODO(afergs): aom_ vs av1_ functions? Which can I use? // Upscale decoded image. void av1_superres_upscale(AV1_COMMON *cm, BufferPool *const pool, bool alloc_pyramid) { const int num_planes = av1_num_planes(cm);
if (!av1_superres_scaled(cm)) return; const SequenceHeader *const seq_params = cm->seq_params; const int byte_alignment = cm->features.byte_alignment;
// Realloc the current frame buffer at a higher resolution in place.
if (pool != NULL) { // Use callbacks if on the decoder.
aom_codec_frame_buffer_t *fb = &cm->cur_frame->raw_frame_buffer;
aom_release_frame_buffer_cb_fn_t release_fb_cb = pool->release_fb_cb;
aom_get_frame_buffer_cb_fn_t cb = pool->get_fb_cb; void *cb_priv = pool->cb_priv;
// Don't use callbacks on the encoder. // aom_alloc_frame_buffer() clears the config data for frame_to_show
if (aom_alloc_frame_buffer(
frame_to_show, cm->superres_upscaled_width,
cm->superres_upscaled_height, seq_params->subsampling_x,
seq_params->subsampling_y, seq_params->use_highbitdepth,
AOM_BORDER_IN_PIXELS, byte_alignment, alloc_pyramid, 0))
aom_internal_error(
cm->error, AOM_CODEC_MEM_ERROR, "Failed to reallocate current frame buffer for superres upscaling");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.