void GaussianBlur(const RowMatrixXf& image, const double sigma, RowMatrixXf* out) { int kernel_size = std::ceil(((sigma - 0.8) / 0.3 + 1.0) * 2.0); if (kernel_size % 2 == 0) { kernel_size += 1; } RowVectorXf gauss_kernel(kernel_size); double norm_factor = 0; for (int i = 0; i < gauss_kernel.size(); i++) { gauss_kernel(i) = Gaussian(i, (kernel_size - 1.0) / 2.0, sigma); norm_factor += gauss_kernel(i); } gauss_kernel /= norm_factor; SeparableConvolution2d(image, gauss_kernel, gauss_kernel, REPLICATE, out); }
int mean_shift(struct globals *globals) { int row, col, t, n; int mwrow, mwrow1, mwrow2, mwnrows, mwcol, mwcol1, mwcol2, mwncols, radiusc; double hspat, hspec, hspat2, hspec2, sigmaspat2, sigmaspec2; double hspecad, hspecad2; double ka2; double w, wsum; LARGEINT n_changes; double alpha2, maxdiff2; struct ngbr_stats Rin, Rout, Rn; double diff, diff2; SEGMENT *seg_tmp; double mindiff, mindiffzero, mindiffavg, mindiffzeroavg; double avgdiff, avgdiffavg; LARGEINT nvalid, count; int do_gauss = 0, do_adaptive, do_progressive; Rin.mean = G_malloc(globals->datasize); Rout.mean = G_malloc(globals->datasize); Rn.mean = G_malloc(globals->datasize); alpha2 = globals->alpha * globals->alpha; do_adaptive = globals->ms_adaptive; do_progressive = globals->ms_progressive; globals->candidate_count = 0; flag_clear_all(globals->candidate_flag); /* Set candidate flag to true/1 for all non-NULL cells */ for (row = globals->row_min; row < globals->row_max; row++) { for (col = globals->col_min; col < globals->col_max; col++) { if (!(FLAG_GET(globals->null_flag, row, col))) { FLAG_SET(globals->candidate_flag, row, col); globals->candidate_count++; } } } /* spatial bandwidth */ hspat = globals->hs; if (hspat < 1) { hspat = 1.5; globals->hs = hspat; } hspat2 = hspat * hspat; sigmaspat2 = hspat2 / 9.; radiusc = hspat; /* radius in cells truncated to integer */ mwnrows = mwncols = radiusc * 2 + 1; /* estimate spectral bandwidth for given spatial bandwidth */ mindiffavg = mindiffzeroavg = 0; avgdiffavg = 0; nvalid = 0; G_message(_("Estimating spectral bandwidth for spatial bandwidth %g..."), hspat); G_percent_reset(); for (row = globals->row_min; row < globals->row_max; row++) { G_percent(row - globals->row_min, globals->row_max - globals->row_min, 4); mwrow1 = row - radiusc; mwrow2 = mwrow1 + mwnrows; if (mwrow1 < globals->row_min) mwrow1 = globals->row_min; if (mwrow2 > globals->row_max) mwrow2 = globals->row_max; for (col = globals->col_min; col < globals->col_max; col++) { if ((FLAG_GET(globals->null_flag, row, col))) continue; /* get current band values */ Segment_get(globals->bands_in, (void *)Rin.mean, row, col); mwcol1 = col - radiusc; mwcol2 = mwcol1 + mwncols; if (mwcol1 < globals->col_min) mwcol1 = globals->col_min; if (mwcol2 > globals->col_max) mwcol2 = globals->col_max; /* get minimum spectral distance for this cell */ count = 0; mindiff = globals->max_diff; mindiffzero = globals->max_diff; avgdiff = 0; for (mwrow = mwrow1; mwrow < mwrow2; mwrow++) { for (mwcol = mwcol1; mwcol < mwcol2; mwcol++) { if ((FLAG_GET(globals->null_flag, mwrow, mwcol))) continue; if (mwrow == row && mwcol == col) continue; diff = mwrow - row; diff2 = diff * diff; diff = mwcol - col; diff2 += diff * diff; if (diff2 <= hspat2) { Segment_get(globals->bands_in, (void *)Rn.mean, mwrow, mwcol); /* get spectral distance */ diff2 = (globals->calculate_similarity)(&Rin, &Rn, globals); if (mindiff > diff2) mindiff = diff2; if (mindiffzero > diff2 && diff2 > 0) mindiffzero = diff2; avgdiff += sqrt(diff2); count++; } } } if (count) { nvalid++; if (mindiff > 0) mindiffavg += sqrt(mindiff); mindiffzeroavg += sqrt(mindiffzero); if (avgdiff > 0) avgdiffavg += avgdiff / count; } } } G_percent(1, 1, 1); if (!nvalid) { G_fatal_error(_("Empty moving windows")); } mindiffavg /= nvalid; mindiffzeroavg /= nvalid; avgdiffavg /= nvalid; G_debug(1, "Average minimum difference to neighbours: %g", mindiffavg); G_debug(1, "Average minimum difference excl zero to neighbours: %g", mindiffzeroavg); G_debug(1, "Average average difference to neighbours: %g", avgdiffavg); /* use avgdiffavg as hspec for adaptive bandwidth */ hspec = globals->hr; if (hspec < 0 || hspec >= 1) { hspec = sqrt(avgdiffavg / 10.0); hspec = avgdiffavg; hspec = mindiffzeroavg; if (do_progressive) G_message(_("Initial range bandwidth: %g"), hspec); else G_message(_("Estimated range bandwidth: %g"), hspec); globals->hr = hspec; } else { G_message(_("Estimated range bandwidth: %g"), mindiffzeroavg); } if (do_adaptive) { /* bandwidth is now standard deviation for adaptive bandwidth * using a gaussian function with range bandwith used as * bandwidth for the gaussian function * the aim is to produce similar but improved results with * adaptive bandwidth * thus increase bandwidth */ hspec = sqrt(hspec); } hspec2 = hspec * hspec; sigmaspec2 = hspec2 / 9.; if (!do_progressive) { G_message(_("Spatial bandwidth: %g"), hspat); G_message(_("Range bandwidth: %g"), hspec); } G_debug(4, "Starting to process %ld candidate cells", globals->candidate_count); t = 0; n_changes = 1; maxdiff2 = 0; while (t < globals->end_t && n_changes > 0) { G_message(_("Processing pass %d..."), ++t); /* cells within an object should become more similar with each pass * therefore the spectral bandwidth could be decreased * and the spatial bandwidth could be increased */ /* spatial bandwidth: double the area covered by the moving window * area = M_PI * hspat * hspat * new hspat = sqrt(M_PI * hspat * hspat * 2 / M_PI) * no good, too large increases */ if (do_progressive) { if (t > 1) hspat *= 1.1; hspat2 = hspat * hspat; sigmaspat2 = hspat2 / 9.; radiusc = hspat; /* radius in cells truncated to integer */ mwnrows = mwncols = radiusc * 2 + 1; /* spectral bandwidth: reduce by 0.7 */ if (t > 1) hspec *= 0.9; hspec2 = hspec * hspec; sigmaspec2 = hspec2 / 9.; G_verbose_message(_("Spatial bandwidth: %g"), hspat); G_verbose_message(_("Range bandwidth: %g"), hspec); } n_changes = 0; maxdiff2 = 0; /* swap input and output */ seg_tmp = globals->bands_in; globals->bands_in = globals->bands_out; globals->bands_out = seg_tmp; /*process candidate cells */ G_percent_reset(); for (row = globals->row_min; row < globals->row_max; row++) { G_percent(row - globals->row_min, globals->row_max - globals->row_min, 4); mwrow1 = row - radiusc; mwrow2 = mwrow1 + mwnrows; if (mwrow1 < globals->row_min) mwrow1 = globals->row_min; if (mwrow2 > globals->row_max) mwrow2 = globals->row_max; for (col = globals->col_min; col < globals->col_max; col++) { if ((FLAG_GET(globals->null_flag, row, col))) continue; /* get current band values */ Segment_get(globals->bands_in, (void *)Rin.mean, row, col); /* init output */ for (n = 0; n < globals->nbands; n++) Rout.mean[n] = 0.; mwcol1 = col - radiusc; mwcol2 = mwcol1 + mwncols; if (mwcol1 < globals->col_min) mwcol1 = globals->col_min; if (mwcol2 > globals->col_max) mwcol2 = globals->col_max; hspecad2 = hspec2; if (do_adaptive) { /* adapt initial range bandwith */ ka2 = hspec2; /* OTB: conductance parameter */ avgdiff = 0; count = 0; for (mwrow = mwrow1; mwrow < mwrow2; mwrow++) { for (mwcol = mwcol1; mwcol < mwcol2; mwcol++) { if ((FLAG_GET(globals->null_flag, mwrow, mwcol))) continue; if (mwrow == row && mwcol == col) continue; diff = mwrow - row; diff2 = diff * diff; diff = mwcol - col; diff2 += diff * diff; if (diff2 <= hspat2) { Segment_get(globals->bands_in, (void *)Rn.mean, mwrow, mwcol); /* get spectral distance */ diff2 = (globals->calculate_similarity)(&Rin, &Rn, globals); avgdiff += sqrt(diff2); count++; } } } hspecad2 = 0; if (avgdiff > 0) { avgdiff /= count; hspecad = hspec; /* OTB-like, contrast enhancing */ hspecad = exp(-avgdiff * avgdiff / (2 * ka2)) * avgdiff; /* preference for large regions, from Perona Malik 1990 * if the settings are right, it could be used to reduce noise */ /* hspecad = 1 / (1 + (avgdiff * avgdiff / (2 * hspec2))); */ hspecad2 = hspecad * hspecad; G_debug(1, "avg spectral diff: %g", avgdiff); G_debug(1, "initial hspec2: %g", hspec2); G_debug(1, "adapted hspec2: %g", hspecad2); } } /* actual mean shift */ wsum = 0; for (mwrow = mwrow1; mwrow < mwrow2; mwrow++) { for (mwcol = mwcol1; mwcol < mwcol2; mwcol++) { if ((FLAG_GET(globals->null_flag, mwrow, mwcol))) continue; diff = mwrow - row; diff2 = diff * diff; diff = mwcol - col; diff2 += diff * diff; if (diff2 <= hspat2) { w = 1; if (do_gauss) w = gauss_kernel(diff2, sigmaspat2); Segment_get(globals->bands_in, (void *)Rn.mean, mwrow, mwcol); /* check spectral distance */ diff2 = (globals->calculate_similarity)(&Rin, &Rn, globals); if (diff2 <= hspecad2) { if (do_gauss) w *= gauss_kernel(diff2, sigmaspec2); wsum += w; for (n = 0; n < globals->nbands; n++) Rout.mean[n] += w * Rn.mean[n]; } } } } if (wsum > 0) { for (n = 0; n < globals->nbands; n++) Rout.mean[n] /= wsum; } else { for (n = 0; n < globals->nbands; n++) Rout.mean[n] = Rin.mean[n]; } /* put new band values */ Segment_put(globals->bands_out, (void *)Rout.mean, row, col); /* if the squared difference between old and new band values * is larger than alpha2, then increase n_changes */ diff2 = (globals->calculate_similarity)(&Rin, &Rout, globals); if (diff2 > alpha2) n_changes++; if (maxdiff2 < diff2) maxdiff2 = diff2; } } G_percent(1, 1, 1); G_message(_("Changes > threshold: %d, largest change: %g"), n_changes, sqrt(maxdiff2)); } if (n_changes > 1) G_message(_("Mean shift stopped at %d due to reaching max iteration limit, more changes may be possible"), t); else G_message(_("Mean shift converged after %d iterations"), t); /* identify connected components */ cluster_bands(globals); /* remove small regions */ remove_small_clumps(globals); return TRUE; }