272 const Result *squared_table,
280 float4 mean_of_squared_color_of_quadrants[4] = {
282 float4 mean_of_color_of_quadrants[4] = {
286 for (
int q = 0; q < 4; q++) {
290 int2 lower_bound = texel -
int2(
sign.x > 0 ? 0 : radius,
sign.y > 0 ? 0 : radius);
291 int2 upper_bound = texel +
int2(
sign.x < 0 ? 0 : radius,
sign.y < 0 ? 0 : radius);
297 int2 region_size = corrected_upper_bound - corrected_lower_bound +
int2(1);
298 int quadrant_pixel_count = region_size.x * region_size.y;
300 if constexpr (UseSummedAreaTable) {
303 *squared_table, lower_bound, upper_bound);
306 for (
int j = 0; j <= radius; j++) {
307 for (
int i = 0;
i <= radius;
i++) {
309 mean_of_color_of_quadrants[q] +=
color;
310 mean_of_squared_color_of_quadrants[q] +=
color *
color;
315 mean_of_color_of_quadrants[q] /= quadrant_pixel_count;
316 mean_of_squared_color_of_quadrants[q] /= quadrant_pixel_count;
320 float minimum_variance = std::numeric_limits<float>::max();
321 float4 mean_color_of_chosen_quadrant = mean_of_color_of_quadrants[0];
322 for (
int q = 0; q < 4; q++) {
323 float4 color_mean = mean_of_color_of_quadrants[q];
324 float4 squared_color_mean = mean_of_squared_color_of_quadrants[q];
325 float4 color_variance = squared_color_mean - color_mean * color_mean;
328 if (variance < minimum_variance) {
329 minimum_variance = variance;
330 mean_color_of_chosen_quadrant = color_mean;
334 output.store_pixel(texel, mean_color_of_chosen_quadrant);
434 float4 encoded_structure_tensor = structure_tensor.load_pixel<float4>(texel);
435 float dxdx = encoded_structure_tensor.x;
436 float dxdy = encoded_structure_tensor.y;
437 float dydy = encoded_structure_tensor.w;
441 float eigenvalue_first_term = (dxdx + dydy) / 2.0f;
442 float eigenvalue_square_root_term = math::sqrt(math::square(dxdx - dydy) +
443 4.0f * math::square(dxdy)) /
445 float first_eigenvalue = eigenvalue_first_term + eigenvalue_square_root_term;
446 float second_eigenvalue = eigenvalue_first_term - eigenvalue_square_root_term;
451 float2 eigenvector = float2(first_eigenvalue - dxdx, -dxdy);
452 float eigenvector_length = math::length(eigenvector);
453 float2 unit_eigenvector = eigenvector_length != 0.0f ? eigenvector / eigenvector_length :
459 float eigenvalue_sum = first_eigenvalue + second_eigenvalue;
460 float eigenvalue_difference = first_eigenvalue - second_eigenvalue;
461 float anisotropy = eigenvalue_sum > 0.0f ? eigenvalue_difference / eigenvalue_sum : 0.0f;
463 float radius = math::max(0.0f, size.load_pixel<float, true>(texel));
464 if (radius == 0.0f) {
465 output.store_pixel(texel, input.load_pixel<float4>(texel));
475 float ellipse_width_factor = (eccentricity + anisotropy) / eccentricity;
476 float ellipse_width = ellipse_width_factor * radius;
477 float ellipse_height = radius / ellipse_width_factor;
482 float cosine = unit_eigenvector.x;
483 float sine = unit_eigenvector.y;
489 float2(cosine / ellipse_width, -sine / ellipse_height),
490 float2(sine / ellipse_width, cosine / ellipse_height));
501 float2 ellipse_major_axis = ellipse_width * unit_eigenvector;
502 float2 ellipse_minor_axis = ellipse_height *
float2(unit_eigenvector.y, unit_eigenvector.x) *
510 const int number_of_sectors = 8;
511 float sector_center_overlap_parameter = 2.0f / radius;
514 float cross_sector_overlap_parameter = (sector_center_overlap_parameter +
521 float4 weighted_mean_of_squared_color_of_sectors[8];
522 float4 weighted_mean_of_color_of_sectors[8];
523 float sum_of_weights_of_sectors[8];
531 float4 center_color_squared = center_color * center_color;
532 float center_weight = 1.0f / number_of_sectors;
533 float4 weighted_center_color = center_color * center_weight;
534 float4 weighted_center_color_squared = center_color_squared * center_weight;
535 for (
int i = 0;
i < number_of_sectors;
i++) {
536 weighted_mean_of_squared_color_of_sectors[
i] = weighted_center_color_squared;
537 weighted_mean_of_color_of_sectors[
i] = weighted_center_color;
538 sum_of_weights_of_sectors[
i] = center_weight;
545 for (
int j = 0; j <= ellipse_bounds.y; j++) {
546 for (
int i = -ellipse_bounds.x;
i <= ellipse_bounds.x;
i++) {
555 if (j == 0 &&
i <= 0) {
562 float disk_point_length_squared =
math::dot(disk_point, disk_point);
563 if (disk_point_length_squared > 1.0f) {
571 float sector_weights[8];
581 float2 polynomial = sector_center_overlap_parameter -
582 cross_sector_overlap_parameter *
math::square(disk_point);
591 float2(disk_point.x - disk_point.y,
592 disk_point.x + disk_point.y);
596 float2 rotated_polynomial = sector_center_overlap_parameter -
597 cross_sector_overlap_parameter *
600 math::max(0.0f, rotated_disk_point.y + rotated_polynomial.x));
602 math::max(0.0f, -rotated_disk_point.x + rotated_polynomial.y));
604 math::max(0.0f, -rotated_disk_point.y + rotated_polynomial.x));
606 math::max(0.0f, rotated_disk_point.x + rotated_polynomial.y));
612 float sector_weights_sum = sector_weights[0] + sector_weights[1] + sector_weights[2] +
613 sector_weights[3] + sector_weights[4] + sector_weights[5] +
614 sector_weights[6] + sector_weights[7];
616 disk_point_length_squared) /
622 float4 upper_color_squared = upper_color * upper_color;
623 float4 lower_color_squared = lower_color * lower_color;
625 for (
int k = 0; k < number_of_sectors; k++) {
626 float weight = sector_weights[k] * radial_gaussian_weight;
630 sum_of_weights_of_sectors[upper_index] += weight;
631 weighted_mean_of_color_of_sectors[upper_index] += upper_color * weight;
632 weighted_mean_of_squared_color_of_sectors[upper_index] += upper_color_squared * weight;
636 int lower_index = (k + number_of_sectors / 2) % number_of_sectors;
637 sum_of_weights_of_sectors[lower_index] += weight;
638 weighted_mean_of_color_of_sectors[lower_index] += lower_color * weight;
639 weighted_mean_of_squared_color_of_sectors[lower_index] += lower_color_squared * weight;
646 float sum_of_weights = 0.0f;
648 for (
int i = 0;
i < number_of_sectors;
i++) {
649 weighted_mean_of_color_of_sectors[
i] /= sum_of_weights_of_sectors[
i];
650 weighted_mean_of_squared_color_of_sectors[
i] /= sum_of_weights_of_sectors[
i];
652 float4 color_mean = weighted_mean_of_color_of_sectors[
i];
653 float4 squared_color_mean = weighted_mean_of_squared_color_of_sectors[
i];
654 float4 color_variance =
math::abs(squared_color_mean - color_mean * color_mean);
663 sum_of_weights += weight;
664 weighted_sum += color_mean * weight;
669 if (sum_of_weights == 0.0f) {
670 weighted_sum = center_color;
673 weighted_sum /= sum_of_weights;
676 output.store_pixel(texel, weighted_sum);
730 const float corner_weight = 0.182f;
731 const float center_weight = 1.0f - 2.0f * corner_weight;
733 float3 x_partial_derivative =
734 input.load_pixel_extended<float4>(texel + int2(-1, 1)).xyz() * -corner_weight +
735 input.load_pixel_extended<float4>(texel + int2(-1, 0)).xyz() * -center_weight +
736 input.load_pixel_extended<float4>(texel + int2(-1, -1)).xyz() * -corner_weight +
737 input.load_pixel_extended<float4>(texel + int2(1, 1)).xyz() * corner_weight +
738 input.load_pixel_extended<float4>(texel + int2(1, 0)).xyz() * center_weight +
739 input.load_pixel_extended<float4>(texel + int2(1, -1)).xyz() * corner_weight;
741 float3 y_partial_derivative =
742 input.load_pixel_extended<float4>(texel + int2(-1, 1)).xyz() * corner_weight +
743 input.load_pixel_extended<float4>(texel + int2(0, 1)).xyz() * center_weight +
744 input.load_pixel_extended<float4>(texel + int2(1, 1)).xyz() * corner_weight +
745 input.load_pixel_extended<float4>(texel + int2(-1, -1)).xyz() * -corner_weight +
746 input.load_pixel_extended<float4>(texel + int2(0, -1)).xyz() * -center_weight +
747 input.load_pixel_extended<float4>(texel + int2(1, -1)).xyz() * -corner_weight;
749 float dxdx = math::dot(x_partial_derivative, x_partial_derivative);
750 float dxdy = math::dot(x_partial_derivative, y_partial_derivative);
751 float dydy = math::dot(y_partial_derivative, y_partial_derivative);
754 float4 structure_tensor = float4(dxdx, dxdy, dxdy, dydy);
756 structure_tensor_image.store_pixel(texel, structure_tensor);
759 return structure_tensor_image;