Skip to content

Commit cf3170c

Browse files
committed
Update estimate_gaussian_parameters_2d template function implementation
Update estimate_gaussian_parameters_2d template function implementation
1 parent 410f688 commit cf3170c

1 file changed

Lines changed: 49 additions & 22 deletions

File tree

image_operations.h

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3089,6 +3089,8 @@ namespace TinyDIP
30893089
}
30903090

30913091
// estimate_gaussian_parameters_2d template function implementation
3092+
// Test1: https://godbolt.org/z/Ee6xjPETE
3093+
// Test2: https://godbolt.org/z/7rcnqWff6
30923094
template <
30933095
class ExecutionPolicy,
30943096
typename ElementT,
@@ -3106,6 +3108,7 @@ namespace TinyDIP
31063108
throw std::invalid_argument("Input image must be 2-dimensional.");
31073109
}
31083110

3111+
constexpr std::size_t num_params = GaussianParameters2D<FloatingPointT>::num_params;
31093112
const std::size_t count = image.count();
31103113
const std::size_t width = image.getWidth();
31113114

@@ -3127,11 +3130,10 @@ namespace TinyDIP
31273130
FloatingPointT x0 = static_cast<FloatingPointT>(max_idx % width);
31283131
FloatingPointT y0 = static_cast<FloatingPointT>(max_idx / width);
31293132

3130-
// Estimate standard deviations & correlation via 2nd moments utilizing OpenMP reduction natively
3131-
FloatingPointT sum_z = static_cast<FloatingPointT>(0.0);
3132-
FloatingPointT sum_dx2_z = static_cast<FloatingPointT>(0.0);
3133-
FloatingPointT sum_dy2_z = static_cast<FloatingPointT>(0.0);
3134-
FloatingPointT sum_dxdy_z = static_cast<FloatingPointT>(0.0);
3133+
FloatingPointT sum_z{};
3134+
FloatingPointT sum_dx2_z{};
3135+
FloatingPointT sum_dy2_z{};
3136+
FloatingPointT sum_dxdy_z{};
31353137
const std::ptrdiff_t signed_count = static_cast<std::ptrdiff_t>(count);
31363138

31373139
#pragma omp parallel for reduction(+:sum_z, sum_dx2_z, sum_dy2_z, sum_dxdy_z)
@@ -3153,7 +3155,11 @@ namespace TinyDIP
31533155
}
31543156

31553157
FloatingPointT sigma_x = (sum_z > static_cast<FloatingPointT>(0.0)) ? std::sqrt(sum_dx2_z / sum_z) : static_cast<FloatingPointT>(10.0);
3158+
sigma_x = std::max(static_cast<FloatingPointT>(1e-6), sigma_x);
3159+
31563160
FloatingPointT sigma_y = (sum_z > static_cast<FloatingPointT>(0.0)) ? std::sqrt(sum_dy2_z / sum_z) : static_cast<FloatingPointT>(10.0);
3161+
sigma_y = std::max(static_cast<FloatingPointT>(1e-6), sigma_y);
3162+
31573163
FloatingPointT rho = (sum_z > static_cast<FloatingPointT>(0.0)) ? (sum_dxdy_z / sum_z) / (sigma_x * sigma_y) : static_cast<FloatingPointT>(0.0);
31583164

31593165
// Clamp initial rho to valid bound
@@ -3179,24 +3185,45 @@ namespace TinyDIP
31793185
mapper
31803186
);
31813187

3182-
std::array<std::array<FloatingPointT, 6>, 6> H_damped = acc.H;
3183-
3184-
// Apply damping
3185-
for (int i = 0; i < 6; ++i)
3188+
// Marquardt Scaling to prevent ill-conditioning due to disparate parameter scales
3189+
std::array<FloatingPointT, num_params> scale;
3190+
for (std::size_t i = 0; i < num_params; ++i)
3191+
{
3192+
scale[i] = std::sqrt(std::abs(acc.H[i][i]));
3193+
if (scale[i] < static_cast<FloatingPointT>(1e-12))
3194+
{
3195+
scale[i] = static_cast<FloatingPointT>(1.0); // Prevent division by zero
3196+
}
3197+
}
3198+
3199+
std::array<std::array<FloatingPointT, num_params>, num_params> H_scaled{};
3200+
std::array<FloatingPointT, num_params> g_scaled{};
3201+
3202+
for (std::size_t i = 0; i < num_params; ++i)
3203+
{
3204+
g_scaled[i] = acc.g[i] / scale[i];
3205+
for (std::size_t j = 0; j < num_params; ++j)
3206+
{
3207+
H_scaled[i][j] = acc.H[i][j] / (scale[i] * scale[j]);
3208+
}
3209+
}
3210+
3211+
// Apply damping to the normalized Hessian
3212+
for (std::size_t i = 0; i < num_params; ++i)
31863213
{
3187-
H_damped[i][i] *= (static_cast<FloatingPointT>(1.0) + lambda);
3214+
H_scaled[i][i] += lambda;
31883215
}
31893216

3190-
std::array<FloatingPointT, 6> delta = solve_linear_system_6x6<FloatingPointT>(H_damped, acc.g);
3217+
std::array<FloatingPointT, num_params> delta_scaled = solve_linear_system<num_params, FloatingPointT>(H_scaled, g_scaled);
3218+
3219+
std::array<FloatingPointT, num_params> delta;
3220+
for (std::size_t i = 0; i < num_params; ++i)
3221+
{
3222+
delta[i] = delta_scaled[i] / scale[i];
3223+
}
31913224

31923225
// Check if gradient is small enough (convergence)
3193-
FloatingPointT delta_sq_sum = std::inner_product(
3194-
std::ranges::begin(delta),
3195-
std::ranges::end(delta),
3196-
std::ranges::begin(delta),
3197-
static_cast<FloatingPointT>(0.0)
3198-
);
3199-
3226+
FloatingPointT delta_sq_sum = std::inner_product(std::ranges::begin(delta), std::ranges::end(delta), std::ranges::begin(delta), static_cast<FloatingPointT>(0.0));
32003227
if (delta_sq_sum < tolerance)
32013228
{
32023229
break;
@@ -3205,8 +3232,8 @@ namespace TinyDIP
32053232
FloatingPointT new_A = A + delta[0];
32063233
FloatingPointT new_x0 = x0 + delta[1];
32073234
FloatingPointT new_y0 = y0 + delta[2];
3208-
FloatingPointT new_sigma_x = sigma_x + delta[3];
3209-
FloatingPointT new_sigma_y = sigma_y + delta[4];
3235+
FloatingPointT new_sigma_x = std::max(static_cast<FloatingPointT>(1e-6), std::abs(sigma_x + delta[3]));
3236+
FloatingPointT new_sigma_y = std::max(static_cast<FloatingPointT>(1e-6), std::abs(sigma_y + delta[4]));
32103237
FloatingPointT new_rho = rho + delta[5];
32113238

32123239
// Clamp new rho strictly inside (-1, 1) range to prevent zero division in W
@@ -3231,8 +3258,8 @@ namespace TinyDIP
32313258
A = new_A;
32323259
x0 = new_x0;
32333260
y0 = new_y0;
3234-
sigma_x = std::abs(new_sigma_x); // Standard deviations must remain positive
3235-
sigma_y = std::abs(new_sigma_y);
3261+
sigma_x = new_sigma_x;
3262+
sigma_y = new_sigma_y;
32363263
rho = new_rho;
32373264
current_sse = new_sse;
32383265
lambda /= static_cast<FloatingPointT>(10.0); // Decrease damping factor

0 commit comments

Comments
 (0)