277 const Result *squared_table,
285 float4 mean_of_squared_color_of_quadrants[4] = {
287 float4 mean_of_color_of_quadrants[4] = {
291 for (
int q = 0; q < 4; q++) {
295 int2 lower_bound = texel -
int2(
sign.x > 0 ? 0 : radius,
sign.y > 0 ? 0 : radius);
296 int2 upper_bound = texel +
int2(
sign.x < 0 ? 0 : radius,
sign.y < 0 ? 0 : radius);
302 int2 region_size = corrected_upper_bound - corrected_lower_bound +
int2(1);
303 int quadrant_pixel_count = region_size.x * region_size.y;
305 if constexpr (UseSummedAreaTable) {
308 *squared_table, lower_bound, upper_bound);
311 for (
int j = 0; j <= radius; j++) {
312 for (
int i = 0;
i <= radius;
i++) {
314 mean_of_color_of_quadrants[q] +=
color;
315 mean_of_squared_color_of_quadrants[q] +=
color *
color;
320 mean_of_color_of_quadrants[q] /= quadrant_pixel_count;
321 mean_of_squared_color_of_quadrants[q] /= quadrant_pixel_count;
325 float minimum_variance = std::numeric_limits<float>::max();
326 float4 mean_color_of_chosen_quadrant = mean_of_color_of_quadrants[0];
327 for (
int q = 0; q < 4; q++) {
328 float4 color_mean = mean_of_color_of_quadrants[q];
329 float4 squared_color_mean = mean_of_squared_color_of_quadrants[q];
330 float4 color_variance = squared_color_mean - color_mean * color_mean;
333 if (variance < minimum_variance) {
334 minimum_variance = variance;
335 mean_color_of_chosen_quadrant = color_mean;
339 output.store_pixel(texel, mean_color_of_chosen_quadrant);
439 float4 encoded_structure_tensor = structure_tensor.load_pixel<float4>(texel);
440 float dxdx = encoded_structure_tensor.x;
441 float dxdy = encoded_structure_tensor.y;
442 float dydy = encoded_structure_tensor.w;
446 float eigenvalue_first_term = (dxdx + dydy) / 2.0f;
447 float eigenvalue_square_root_term = math::sqrt(math::square(dxdx - dydy) +
448 4.0f * math::square(dxdy)) /
450 float first_eigenvalue = eigenvalue_first_term + eigenvalue_square_root_term;
451 float second_eigenvalue = eigenvalue_first_term - eigenvalue_square_root_term;
456 float2 eigenvector = float2(first_eigenvalue - dxdx, -dxdy);
457 float eigenvector_length = math::length(eigenvector);
458 float2 unit_eigenvector = eigenvector_length != 0.0f ? eigenvector / eigenvector_length :
464 float eigenvalue_sum = first_eigenvalue + second_eigenvalue;
465 float eigenvalue_difference = first_eigenvalue - second_eigenvalue;
466 float anisotropy = eigenvalue_sum > 0.0f ? eigenvalue_difference / eigenvalue_sum : 0.0f;
468 float radius = math::max(0.0f, size.load_pixel<float, true>(texel));
469 if (radius == 0.0f) {
470 output.store_pixel(texel, input.load_pixel<float4>(texel));
480 float ellipse_width_factor = (eccentricity + anisotropy) / eccentricity;
481 float ellipse_width = ellipse_width_factor * radius;
482 float ellipse_height = radius / ellipse_width_factor;
487 float cosine = unit_eigenvector.x;
488 float sine = unit_eigenvector.y;
494 float2(cosine / ellipse_width, -sine / ellipse_height),
495 float2(sine / ellipse_width, cosine / ellipse_height));
506 float2 ellipse_major_axis = ellipse_width * unit_eigenvector;
507 float2 ellipse_minor_axis = ellipse_height *
float2(unit_eigenvector.y, unit_eigenvector.x) *
515 const int number_of_sectors = 8;
516 float sector_center_overlap_parameter = 2.0f / radius;
519 float cross_sector_overlap_parameter = (sector_center_overlap_parameter +
526 float4 weighted_mean_of_squared_color_of_sectors[8];
527 float4 weighted_mean_of_color_of_sectors[8];
528 float sum_of_weights_of_sectors[8];
536 float4 center_color_squared = center_color * center_color;
537 float center_weight = 1.0f / number_of_sectors;
538 float4 weighted_center_color = center_color * center_weight;
539 float4 weighted_center_color_squared = center_color_squared * center_weight;
540 for (
int i = 0;
i < number_of_sectors;
i++) {
541 weighted_mean_of_squared_color_of_sectors[
i] = weighted_center_color_squared;
542 weighted_mean_of_color_of_sectors[
i] = weighted_center_color;
543 sum_of_weights_of_sectors[
i] = center_weight;
550 for (
int j = 0; j <= ellipse_bounds.y; j++) {
551 for (
int i = -ellipse_bounds.x;
i <= ellipse_bounds.x;
i++) {
560 if (j == 0 &&
i <= 0) {
567 float disk_point_length_squared =
math::dot(disk_point, disk_point);
568 if (disk_point_length_squared > 1.0f) {
576 float sector_weights[8];
586 float2 polynomial = sector_center_overlap_parameter -
587 cross_sector_overlap_parameter *
math::square(disk_point);
596 float2(disk_point.x - disk_point.y,
597 disk_point.x + disk_point.y);
601 float2 rotated_polynomial = sector_center_overlap_parameter -
602 cross_sector_overlap_parameter *
605 math::max(0.0f, rotated_disk_point.y + rotated_polynomial.x));
607 math::max(0.0f, -rotated_disk_point.x + rotated_polynomial.y));
609 math::max(0.0f, -rotated_disk_point.y + rotated_polynomial.x));
611 math::max(0.0f, rotated_disk_point.x + rotated_polynomial.y));
617 float sector_weights_sum = sector_weights[0] + sector_weights[1] + sector_weights[2] +
618 sector_weights[3] + sector_weights[4] + sector_weights[5] +
619 sector_weights[6] + sector_weights[7];
621 disk_point_length_squared) /
627 float4 upper_color_squared = upper_color * upper_color;
628 float4 lower_color_squared = lower_color * lower_color;
630 for (
int k = 0; k < number_of_sectors; k++) {
631 float weight = sector_weights[k] * radial_gaussian_weight;
635 sum_of_weights_of_sectors[upper_index] += weight;
636 weighted_mean_of_color_of_sectors[upper_index] += upper_color * weight;
637 weighted_mean_of_squared_color_of_sectors[upper_index] += upper_color_squared * weight;
641 int lower_index = (k + number_of_sectors / 2) % number_of_sectors;
642 sum_of_weights_of_sectors[lower_index] += weight;
643 weighted_mean_of_color_of_sectors[lower_index] += lower_color * weight;
644 weighted_mean_of_squared_color_of_sectors[lower_index] += lower_color_squared * weight;
651 float sum_of_weights = 0.0f;
653 for (
int i = 0;
i < number_of_sectors;
i++) {
654 weighted_mean_of_color_of_sectors[
i] /= sum_of_weights_of_sectors[
i];
655 weighted_mean_of_squared_color_of_sectors[
i] /= sum_of_weights_of_sectors[
i];
657 float4 color_mean = weighted_mean_of_color_of_sectors[
i];
658 float4 squared_color_mean = weighted_mean_of_squared_color_of_sectors[
i];
659 float4 color_variance =
math::abs(squared_color_mean - color_mean * color_mean);
668 sum_of_weights += weight;
669 weighted_sum += color_mean * weight;
674 if (sum_of_weights == 0.0f) {
675 weighted_sum = center_color;
678 weighted_sum /= sum_of_weights;
681 output.store_pixel(texel, weighted_sum);
735 const float corner_weight = 0.182f;
736 const float center_weight = 1.0f - 2.0f * corner_weight;
738 float3 x_partial_derivative =
739 input.load_pixel_extended<float4>(texel + int2(-1, 1)).xyz() * -corner_weight +
740 input.load_pixel_extended<float4>(texel + int2(-1, 0)).xyz() * -center_weight +
741 input.load_pixel_extended<float4>(texel + int2(-1, -1)).xyz() * -corner_weight +
742 input.load_pixel_extended<float4>(texel + int2(1, 1)).xyz() * corner_weight +
743 input.load_pixel_extended<float4>(texel + int2(1, 0)).xyz() * center_weight +
744 input.load_pixel_extended<float4>(texel + int2(1, -1)).xyz() * corner_weight;
746 float3 y_partial_derivative =
747 input.load_pixel_extended<float4>(texel + int2(-1, 1)).xyz() * corner_weight +
748 input.load_pixel_extended<float4>(texel + int2(0, 1)).xyz() * center_weight +
749 input.load_pixel_extended<float4>(texel + int2(1, 1)).xyz() * corner_weight +
750 input.load_pixel_extended<float4>(texel + int2(-1, -1)).xyz() * -corner_weight +
751 input.load_pixel_extended<float4>(texel + int2(0, -1)).xyz() * -center_weight +
752 input.load_pixel_extended<float4>(texel + int2(1, -1)).xyz() * -corner_weight;
754 float dxdx = math::dot(x_partial_derivative, x_partial_derivative);
755 float dxdy = math::dot(x_partial_derivative, y_partial_derivative);
756 float dydy = math::dot(y_partial_derivative, y_partial_derivative);
759 float4 structure_tensor = float4(dxdx, dxdy, dxdy, dydy);
761 structure_tensor_image.store_pixel(texel, structure_tensor);
764 return structure_tensor_image;