| /* |
| * Copyright (c) 2021, 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. |
| */ |
| |
| #include <stdlib.h> |
| #include <math.h> |
| |
| #include "av1/common/common.h" |
| #include "av1/common/filter.h" |
| #include "av1/common/spherical_pred.h" |
| #include "aom_dsp/aom_dsp_common.h" |
| |
| #if CONFIG_SPHERICAL_PRED |
| |
| void av1_sphere_to_plane_erp(double phi, double theta, int width, int height, |
| double *x, double *y) { |
| double phi_mod = fmod(phi, 2 * PI); |
| int is_on_other_hemisphere = ((phi_mod > 0.5 * PI && phi_mod < 1.5 * PI) || |
| (phi_mod < -0.5 * PI && phi_mod > -1.5 * PI)); |
| |
| // Flip phi to the destination quadrant |
| phi_mod = is_on_other_hemisphere ? PI - phi_mod : phi_mod; |
| // Regulate phi back to [-pi/2, pi/2) |
| if (phi_mod > 1.5 * PI) { |
| phi_mod = phi_mod - 2 * PI; |
| } else if (phi_mod < -1.5 * PI) { |
| phi_mod = phi_mod + 2 * PI; |
| } |
| |
| double theta_mod = fmod(is_on_other_hemisphere ? theta + PI : theta, 2 * PI); |
| if (theta_mod < -PI) { |
| theta_mod = theta_mod + 2 * PI; |
| } else if (theta_mod >= PI) { |
| theta_mod = theta_mod - 2 * PI; |
| } |
| |
| *x = theta_mod / PI * width * 0.5 + width * 0.5; |
| |
| // No minus sign for y since we only use an imaginary upside-down globe |
| *y = phi_mod / (PI * 0.5) * height * 0.5 + height * 0.5; |
| *y = AOMMIN(*y, height - 0.00001); |
| } |
| |
| void av1_plane_to_sphere_erp(double x, double y, int width, int height, |
| double *phi, double *theta) { |
| // Without the 0.0001, the X information of the top row will be discarded. Any |
| // points on the top row of the frame will be mapped to the north pole, hence |
| // will be mapped back to the center of the top row in the frame. |
| y = AOMMAX(y, 0.00001); |
| y = AOMMIN(y, height - 1); |
| |
| double x_mod = fmod(x, width); |
| x_mod = x_mod >= 0 ? x_mod : x_mod + width; |
| |
| // Since x_mod is in [0, width), theta is in [-PI, PI) |
| *theta = (x_mod - width * 0.5) / width * 2 * PI; |
| *phi = (y - height * 0.5) / height * PI; |
| } |
| |
| void av1_normalize_carte_vector(CartesianVector *carte) { |
| double temp = |
| sqrt(carte->x * carte->x + carte->y * carte->y + carte->z * carte->z); |
| |
| // k would be (0, 0, 0) if v and v_prime are the same |
| if (temp > 0.0001) { |
| carte->x = carte->x / temp; |
| carte->y = carte->y / temp; |
| carte->z = carte->z / temp; |
| } |
| } |
| |
| void av1_sphere_polar_to_carte(const PolarVector *polar, |
| CartesianVector *carte) { |
| assert(polar != NULL && carte != NULL); |
| |
| carte->x = polar->r * cos(polar->theta) * sin(polar->phi); |
| carte->y = polar->r * sin(polar->theta) * sin(polar->phi); |
| carte->z = polar->r * cos(polar->phi); |
| } |
| |
| void av1_sphere_carte_to_polar(const CartesianVector *carte, |
| PolarVector *polar) { |
| assert(polar != NULL && carte != NULL); |
| |
| polar->r = |
| sqrt(carte->x * carte->x + carte->y * carte->y + carte->z * carte->z); |
| polar->phi = acos(carte->z / polar->r); |
| |
| if (carte->x == 0) { |
| if (carte->y > 0) { |
| polar->theta = 0.5 * PI; |
| } else if (carte->y < 0) { |
| polar->theta = 1.5 * PI; |
| } else { |
| polar->theta = 0; |
| } |
| } else { |
| polar->theta = atan(carte->y / carte->x); |
| // arctan only gives (-PI/2, PI/2), so we need to adjust the result |
| if (carte->x < 0) { |
| polar->theta += PI; |
| } |
| } |
| } |
| |
| double av1_carte_vectors_dot_product(const CartesianVector *left, |
| const CartesianVector *right) { |
| assert(left != NULL && right != NULL); |
| |
| return left->x * right->x + left->y * right->y + left->z * right->z; |
| } |
| |
| void av1_carte_vectors_cross_product(const CartesianVector *left, |
| const CartesianVector *right, |
| CartesianVector *result) { |
| assert(left != NULL && right != NULL && result != NULL); |
| |
| result->x = left->y * right->z - left->z * right->y; |
| result->y = left->z * right->x - left->x * right->z; |
| result->z = left->x * right->y - left->y * right->x; |
| } |
| |
| /*!\brief Calculate the filter kernel index based on the subpixel coordination |
| * \param[in] subpel_pos The subpixel coordination |
| * \param[out] pos_ref The integer coordination to which the filter kernel |
| * center should align to. If the subpixel coordination |
| * is with in the range of (0, -0.0625) to the pixel on |
| * its right, we choose that pixel. Otherwise, we |
| * choose the pixel on the left of the subpixel. |
| * \return The kernel index. It is doubled since we only use |
| * #(0, 2, 4, ... 14) kernels |
| */ |
| static int cal_kernel_idx(double subpel_pos, int *pos_ref) { |
| double diff = subpel_pos - floor(subpel_pos); |
| int idx = (int)round(diff / 0.125); |
| |
| if (idx >= SUBPEL_SHIFTS / 2) { |
| idx = idx % (SUBPEL_SHIFTS / 2); |
| *pos_ref = (int)ceil(subpel_pos); |
| } else { |
| *pos_ref = (int)floor(subpel_pos); |
| } |
| |
| return idx * 2; |
| } |
| |
| // Get the filtered sums of the rows int the 8x8 block centered at the |
| // input location |
| static void get_interp_x_arr(int x, int y, const uint8_t *ref_frame, |
| int ref_frame_stride, int frame_width, |
| int frame_height, const InterpKernel *kernel, |
| int filter_tap, int x_kernel_idx, |
| double *x_sum_arr) { |
| int fo = filter_tap / 2 - 1; |
| int cur_x; |
| int cur_y; |
| double coeff = 0; |
| double sum = 0; |
| |
| for (int i = 0; i < 8; i++) { |
| cur_y = y - fo + i; |
| cur_y = AOMMAX(cur_y, 0); |
| cur_y = AOMMIN(cur_y, frame_height - 1); |
| |
| sum = 0; |
| |
| for (int k = 0; k < filter_tap; k++) { |
| coeff = kernel[x_kernel_idx][k] / 128.0; |
| |
| cur_x = x - fo + k; |
| cur_x = cur_x % frame_width; |
| cur_x = cur_x >= 0 ? cur_x : frame_width + cur_x; |
| |
| sum += ref_frame[cur_x + cur_y * ref_frame_stride] * coeff; |
| } |
| |
| x_sum_arr[i] = sum; |
| } // for i |
| } |
| |
| static uint8_t interp_pixel_erp(const uint8_t *ref_frame, int ref_frame_stride, |
| int frame_width, int frame_height, |
| double subpel_x, double subpel_y, |
| const InterpKernel *kernel, int filter_tap) { |
| double x_sum_arr[8]; |
| double sum = 0; |
| double coeff = 0; |
| int x_kernel_idx; |
| int y_kernel_idx; |
| int x_ref; |
| int y_ref; |
| int pixel; |
| |
| x_kernel_idx = cal_kernel_idx(subpel_x, &x_ref); |
| y_kernel_idx = cal_kernel_idx(subpel_y, &y_ref); |
| |
| get_interp_x_arr(x_ref, y_ref, ref_frame, ref_frame_stride, frame_width, |
| frame_height, kernel, filter_tap, x_kernel_idx, x_sum_arr); |
| |
| // Perform filtering on the column |
| sum = 0; |
| for (int k = 0; k < filter_tap; k++) { |
| coeff = kernel[y_kernel_idx][k] / 128.0; |
| sum += x_sum_arr[k] * coeff; |
| } |
| |
| pixel = (int)round(sum); |
| pixel = clamp(pixel, 0, 255); |
| return (uint8_t)pixel; |
| } |
| |
| void av1_get_pred_erp(int block_x, int block_y, int block_width, |
| int block_height, double delta_phi, double delta_theta, |
| const uint8_t *ref_frame, int ref_frame_stride, |
| int frame_width, int frame_height, int pred_block_stride, |
| uint8_t *pred_block) { |
| assert(ref_frame != NULL && frame_width > 0 && frame_height > 0 && |
| ref_frame_stride >= frame_width); |
| assert(pred_block != NULL && block_width > 0 && block_height > 0 && |
| pred_block_stride >= block_width); |
| |
| int pos_pred; // Offset in pred_block buffer |
| double x_current; // X coordiante in current frame |
| double y_current; // Y coordiante in currrent frame |
| double subpel_x_ref; // X coordinate in reference frame |
| double subpel_y_ref; // Y coordiante in reference frame |
| |
| PolarVector block_polar; |
| CartesianVector block_v; |
| CartesianVector block_v_prime; |
| CartesianVector k; // The axis that the block will rotate along |
| block_polar.r = 1.0; |
| av1_plane_to_sphere_erp(block_x, block_y, frame_width, frame_height, |
| &block_polar.phi, &block_polar.theta); |
| block_polar.phi += 0.5 * PI; |
| av1_sphere_polar_to_carte(&block_polar, &block_v); |
| |
| block_polar.phi += delta_phi; |
| block_polar.theta += delta_theta; |
| av1_sphere_polar_to_carte(&block_polar, &block_v_prime); |
| |
| av1_carte_vectors_cross_product(&block_v, &block_v_prime, &k); |
| av1_normalize_carte_vector(&k); |
| |
| double product = av1_carte_vectors_dot_product(&block_v, &block_v_prime); |
| // Avoid floating point precision issues |
| product = AOMMAX(product, -1.0); |
| product = AOMMIN(product, 1.0); |
| double alpha = acos(product); |
| |
| CartesianVector v; |
| CartesianVector v_prime; |
| PolarVector v_polar; |
| PolarVector v_prime_polar; |
| v_polar.r = 1.0; |
| v_prime_polar.r = 1.0; |
| double k_dot_v; |
| |
| const int filter_tap = 8; |
| |
| for (int idx_y = 0; idx_y < block_height; idx_y++) { |
| for (int idx_x = 0; idx_x < block_width; idx_x++) { |
| x_current = idx_x + block_x; |
| y_current = idx_y + block_y; |
| |
| av1_plane_to_sphere_erp(x_current, y_current, frame_width, frame_height, |
| &v_polar.phi, &v_polar.theta); |
| v_polar.phi += 0.5 * PI; |
| av1_sphere_polar_to_carte(&v_polar, &v); |
| k_dot_v = av1_carte_vectors_dot_product(&k, &v); |
| |
| v_prime.x = k.x * k_dot_v * (1 - cos(alpha)) + v.x * cos(alpha) + |
| (k.y * v.z - k.z * v.y) * sin(alpha); |
| v_prime.y = k.y * k_dot_v * (1 - cos(alpha)) + v.y * cos(alpha) + |
| (k.z * v.x - k.x * v.z) * sin(alpha); |
| v_prime.z = k.z * k_dot_v * (1 - cos(alpha)) + v.z * cos(alpha) + |
| (k.x * v.y - k.y * v.x) * sin(alpha); |
| |
| av1_sphere_carte_to_polar(&v_prime, &v_prime_polar); |
| v_prime_polar.phi -= 0.5 * PI; |
| av1_sphere_to_plane_erp(v_prime_polar.phi, v_prime_polar.theta, |
| frame_width, frame_height, &subpel_x_ref, |
| &subpel_y_ref); |
| |
| pos_pred = idx_x + idx_y * pred_block_stride; |
| pred_block[pos_pred] = interp_pixel_erp( |
| ref_frame, ref_frame_stride, frame_width, frame_height, subpel_x_ref, |
| subpel_y_ref, av1_sub_pel_filters_8, filter_tap); |
| } |
| } // for idx_y |
| } |
| |
| static int get_sad_of_blocks(const uint8_t *cur_block, |
| const uint8_t *pred_block, int block_width, |
| int block_height, int cur_block_stride, |
| int pred_block_stride) { |
| assert(block_width > 0 && block_height > 0 && cur_block_stride >= 0 && |
| pred_block_stride >= 0); |
| int pos_curr; |
| int pos_pred; |
| int ret_sad = 0; |
| |
| for (int idx_y = 0; idx_y < block_height; idx_y++) { |
| for (int idx_x = 0; idx_x < block_width; idx_x++) { |
| pos_curr = idx_x + idx_y * cur_block_stride; |
| pos_pred = idx_x + idx_y * pred_block_stride; |
| |
| ret_sad += abs((int)cur_block[pos_curr] - (int)pred_block[pos_pred]); |
| } |
| } |
| |
| return ret_sad; |
| } |
| |
| /* Calculate the scale of theta based on the current phi. |
| * Makes the search/filter pattern on the latitude sparse as the position moves |
| * toward polars. Will be used for adjusting searching step, searching range, |
| * and filtering. |
| */ |
| static double cal_theta_scale(double phi) { |
| const double max_scale = 10.0; |
| |
| if (fabs(cos(phi)) > 0.00001) { |
| return AOMMIN(max_scale, 1 / fabs(cos(phi))); |
| } else { |
| return max_scale; |
| } |
| } |
| |
| static double cal_range_theta(int search_range, int frame_width, double phi) { |
| return search_range * cal_theta_scale(phi) * 2 * PI / frame_width; |
| } |
| |
| int av1_motion_search_brute_force_erp( |
| int block_x, int block_y, int block_width, int block_height, |
| const uint8_t *cur_frame, const uint8_t *ref_frame, int frame_stride, |
| int frame_width, int frame_height, int search_range, SphereMV *best_mv) { |
| assert(cur_frame != NULL && ref_frame != NULL && best_mv != NULL); |
| assert(block_width > 0 && block_height > 0 && block_width <= 128 && |
| block_height <= 128 && block_x >= 0 && block_y >= 0 && |
| frame_width > 0 && frame_height > 0); |
| assert(search_range > 0); |
| |
| const double search_step_phi = PI / frame_height; |
| const double search_step_theta = 2 * PI / frame_width; |
| |
| double delta_phi; |
| double delta_theta; |
| int temp_sad; |
| int best_sad; |
| |
| const uint8_t *cur_block = &cur_frame[block_x + block_y * frame_stride]; |
| uint8_t pred_block[128 * 128]; |
| const int pred_block_stride = 128; |
| |
| av1_get_pred_erp(block_x, block_y, block_width, block_height, 0, 0, ref_frame, |
| frame_stride, frame_width, frame_height, pred_block_stride, |
| pred_block); |
| best_sad = get_sad_of_blocks(cur_block, pred_block, block_width, block_height, |
| frame_stride, pred_block_stride); |
| best_mv->phi = 0; |
| best_mv->theta = 0; |
| |
| for (int i = -search_range; i <= search_range; i++) { |
| delta_phi = i * search_step_phi; |
| |
| for (int j = -search_range; j <= search_range; j++) { |
| delta_theta = j * search_step_theta; |
| |
| av1_get_pred_erp(block_x, block_y, block_width, block_height, delta_phi, |
| delta_theta, ref_frame, frame_stride, frame_width, |
| frame_height, pred_block_stride, pred_block); |
| |
| temp_sad = |
| get_sad_of_blocks(cur_block, pred_block, block_width, block_height, |
| frame_stride, pred_block_stride); |
| |
| if (temp_sad < best_sad) { |
| best_sad = temp_sad; |
| best_mv->phi = delta_phi; |
| best_mv->theta = delta_theta; |
| } |
| } // for j |
| } // for i |
| |
| return best_sad; |
| } |
| |
| /* Update Large Diamond Search Pattern shperical motion vectors based on the |
| * center |
| * ldsp_mv[1] |
| * / \ |
| * ldsp_mv[8] ldsp_mv[2] |
| * / \ |
| * ldsp_mv[7] ldsp_mv[0] ldsp_mv[3] |
| * \ / |
| * ldsp_mv[6] ldsp_mv[4] |
| * \ / |
| * ldsp_mv[5] |
| */ |
| static void update_sphere_mv_ldsp(SphereMV ldsp_mv[9], double search_step_phi, |
| double search_step_theta, double block_phi) { |
| double magnified_step_theta; |
| |
| ldsp_mv[1].phi = ldsp_mv[0].phi - 2 * search_step_phi; |
| ldsp_mv[1].theta = ldsp_mv[0].theta; |
| |
| ldsp_mv[2].phi = ldsp_mv[0].phi - search_step_phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + ldsp_mv[2].phi); |
| ldsp_mv[2].theta = ldsp_mv[0].theta + magnified_step_theta; |
| |
| ldsp_mv[3].phi = ldsp_mv[0].phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + ldsp_mv[3].phi); |
| ldsp_mv[3].theta = ldsp_mv[0].theta + 2 * magnified_step_theta; |
| |
| ldsp_mv[4].phi = ldsp_mv[0].phi + search_step_phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + ldsp_mv[4].phi); |
| ldsp_mv[4].theta = ldsp_mv[0].theta + magnified_step_theta; |
| |
| ldsp_mv[5].phi = ldsp_mv[0].phi + 2 * search_step_phi; |
| ldsp_mv[5].theta = ldsp_mv[0].theta; |
| |
| ldsp_mv[6].phi = ldsp_mv[0].phi + search_step_phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + ldsp_mv[6].phi); |
| ldsp_mv[6].theta = ldsp_mv[0].theta - magnified_step_theta; |
| |
| ldsp_mv[7].phi = ldsp_mv[0].phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + ldsp_mv[7].phi); |
| ldsp_mv[7].theta = ldsp_mv[0].theta - 2 * magnified_step_theta; |
| |
| ldsp_mv[8].phi = ldsp_mv[0].phi - search_step_phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + ldsp_mv[8].phi); |
| ldsp_mv[8].theta = ldsp_mv[0].theta - magnified_step_theta; |
| } |
| |
| /* Small Diamond Search Pattern on shpere |
| * sdsp_mv[1] |
| * / \ |
| * sdsp_mv[4] sdsp_mv[0] sdsp_mv[2] |
| * \ / |
| * sdsp_mv[3] |
| */ |
| static void update_sphere_mv_sdsp(SphereMV sdsp_mv[5], double search_step_phi, |
| double search_step_theta, double block_phi) { |
| double magnified_step_theta; |
| |
| sdsp_mv[1].phi = sdsp_mv[0].phi - search_step_phi; |
| sdsp_mv[1].theta = sdsp_mv[0].theta; |
| |
| sdsp_mv[2].phi = sdsp_mv[0].phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + sdsp_mv[2].phi); |
| sdsp_mv[2].theta = sdsp_mv[0].theta + magnified_step_theta; |
| |
| sdsp_mv[3].phi = sdsp_mv[0].phi + search_step_phi; |
| sdsp_mv[3].theta = sdsp_mv[0].theta; |
| |
| sdsp_mv[4].phi = sdsp_mv[0].phi; |
| magnified_step_theta = |
| search_step_theta * cal_theta_scale(block_phi + sdsp_mv[4].phi); |
| sdsp_mv[4].theta = sdsp_mv[0].theta - magnified_step_theta; |
| } |
| |
| int av1_motion_search_diamond_erp(int block_x, int block_y, int block_width, |
| int block_height, const uint8_t *cur_frame, |
| const uint8_t *ref_frame, int frame_stride, |
| int frame_width, int frame_height, |
| int search_range, const SphereMV *start_mv, |
| SphereMV *best_mv) { |
| assert(cur_frame != NULL && ref_frame != NULL && best_mv != NULL); |
| assert(block_width > 0 && block_height > 0 && block_width <= 128 && |
| block_height <= 128 && block_x >= 0 && block_y >= 0 && |
| frame_width > 0 && frame_height > 0); |
| assert(search_range > 0); |
| |
| // Large Diamond Search Pattern on shpere |
| SphereMV ldsp_mv[9]; |
| // Small Diamond Search Pattern on shpere |
| SphereMV sdsp_mv[5]; |
| |
| double search_step_phi = |
| AOMMIN(0.25 * block_height, 0.25 * search_range) * PI / frame_height; |
| double search_step_theta = |
| AOMMIN(0.25 * block_width, 0.25 * search_range) * 2 * PI / frame_width; |
| |
| const uint8_t *cur_block = &cur_frame[block_x + block_y * frame_stride]; |
| uint8_t pred_block[128 * 128]; |
| const int pred_block_stride = 128; |
| |
| double start_phi; |
| double start_theta; |
| av1_plane_to_sphere_erp(block_x, block_y, frame_width, frame_height, |
| &start_phi, &start_theta); |
| |
| double max_range_phi = |
| start_phi + start_mv->phi + search_range * PI / frame_height; |
| double min_range_phi = |
| start_phi + start_mv->phi - search_range * PI / frame_height; |
| |
| int best_mv_idx = 0; |
| ldsp_mv[0].phi = start_mv->phi; |
| ldsp_mv[0].theta = start_mv->theta; |
| |
| int temp_sad; |
| int best_sad; |
| av1_get_pred_erp(block_x, block_y, block_width, block_height, ldsp_mv[0].phi, |
| ldsp_mv[0].theta, ref_frame, frame_stride, frame_width, |
| frame_height, pred_block_stride, pred_block); |
| best_sad = get_sad_of_blocks(cur_block, pred_block, block_width, block_height, |
| frame_stride, pred_block_stride); |
| update_sphere_mv_ldsp(ldsp_mv, search_step_phi, search_step_theta, start_phi); |
| |
| const double min_scale = 0.125; |
| |
| while (start_phi + ldsp_mv[5].phi <= max_range_phi && |
| start_phi + ldsp_mv[1].phi >= min_range_phi && |
| start_theta + ldsp_mv[3].theta <= |
| start_theta + start_mv->theta + |
| cal_range_theta(search_range, frame_width, |
| start_phi + ldsp_mv[3].phi) && |
| start_theta + ldsp_mv[7].theta >= |
| start_theta + start_mv->theta - |
| cal_range_theta(search_range, frame_width, |
| start_phi + ldsp_mv[7].phi)) { |
| for (int i = 0; i < 9; i++) { |
| av1_get_pred_erp(block_x, block_y, block_width, block_height, |
| ldsp_mv[i].phi, ldsp_mv[i].theta, ref_frame, |
| frame_stride, frame_width, frame_height, |
| pred_block_stride, pred_block); |
| |
| temp_sad = |
| get_sad_of_blocks(cur_block, pred_block, block_width, block_height, |
| frame_stride, pred_block_stride); |
| |
| if (temp_sad < best_sad) { |
| best_sad = temp_sad; |
| best_mv_idx = i; |
| } |
| } // for |
| |
| if (best_mv_idx == 0) { |
| if (search_step_phi > min_scale * PI / frame_height && |
| search_step_theta > min_scale * 2 * PI / frame_height) { |
| search_step_phi *= 0.5; |
| search_step_theta *= 0.5; |
| } else { |
| break; |
| } |
| } else { |
| ldsp_mv[0].phi = ldsp_mv[best_mv_idx].phi; |
| ldsp_mv[0].theta = ldsp_mv[best_mv_idx].theta; |
| best_mv_idx = 0; |
| } |
| update_sphere_mv_ldsp(ldsp_mv, search_step_phi, search_step_theta, |
| start_phi); |
| } |
| |
| sdsp_mv[0].phi = ldsp_mv[0].phi; |
| sdsp_mv[0].theta = ldsp_mv[0].theta; |
| best_mv_idx = 0; |
| search_step_phi = min_scale * PI / frame_height; |
| search_step_theta = min_scale * 2 * PI / frame_width; |
| update_sphere_mv_sdsp(sdsp_mv, search_step_phi, search_step_theta, start_phi); |
| if (start_phi + sdsp_mv[3].phi <= max_range_phi && |
| start_phi + sdsp_mv[1].phi >= min_range_phi && |
| start_theta + sdsp_mv[2].theta <= |
| start_theta + start_mv->theta + |
| cal_range_theta(search_range, frame_width, |
| start_phi + sdsp_mv[2].phi) && |
| start_theta + sdsp_mv[4].theta >= |
| start_theta + start_mv->theta + |
| cal_range_theta(search_range, frame_width, |
| start_phi + sdsp_mv[4].phi)) { |
| for (int i = 0; i < 5; i++) { |
| av1_get_pred_erp(block_x, block_y, block_width, block_height, |
| sdsp_mv[i].phi, sdsp_mv[i].theta, ref_frame, |
| frame_stride, frame_width, frame_height, |
| pred_block_stride, pred_block); |
| |
| temp_sad = |
| get_sad_of_blocks(cur_block, pred_block, block_width, block_height, |
| frame_stride, pred_block_stride); |
| |
| if (temp_sad < best_sad) { |
| best_sad = temp_sad; |
| best_mv_idx = i; |
| } |
| } // for |
| } |
| |
| best_mv->phi = sdsp_mv[best_mv_idx].phi; |
| best_mv->theta = sdsp_mv[best_mv_idx].theta; |
| |
| return best_sad; |
| } |
| |
| #endif |