/* * Copyright (c) 2016, Alliance for Open Media. All rights reserved. * * This source code is subject to the terms of the BSD 2 Clause License and * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License * was not distributed with this source code in the LICENSE file, you can * obtain it at www.aomedia.org/license/software. If the Alliance for Open * Media Patent License 1.0 was not distributed with this source code in the * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
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; constint 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; constint 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);
}
}
}
staticint get_down2_length(int length, int steps) { for (int s = 0; s < steps; ++s) length = (length + 1) >> 1; return length;
}
staticint 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;
}
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) { returnfalse;
}
// 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. constint 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) returnfalse; 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); returnfalse;
} 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. constint border_cols = UPSCALE_NORMATIVE_TAPS / 2 + 1; constint 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) returnfalse; 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); returnfalse;
} 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. constint downscaled_x0 = tile_col.mi_col_start << (MI_SIZE_LOG2 - ss_x); constint downscaled_x1 = tile_col.mi_col_end << (MI_SIZE_LOG2 - ss_x); constint src_width = downscaled_x1 - downscaled_x0;
constint 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;
}
// 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. constint 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) { constint num_planes = av1_num_planes(cm); if (!av1_superres_scaled(cm)) return; const SequenceHeader *const seq_params = cm->seq_params; constint 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.