Skip to content

Commit 76151e5

Browse files
committed
rework kfactor computation
* get rid of sharing buffers when creating scale space pyramid, the performace impact is neglegible
1 parent c133518 commit 76151e5

File tree

1 file changed

+69
-21
lines changed

1 file changed

+69
-21
lines changed

modules/features2d/src/kaze/AKAZEFeatures.cpp

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,65 @@ class NonLinearScalarDiffusionStep : public ParallelLoopBody
233233
float step_size_;
234234
};
235235

236+
/**
237+
* @brief This function computes a good empirical value for the k contrast factor
238+
* given two gradient images, the percentile (0-1), the temporal storage to hold
239+
* gradient norms and the histogram bins
240+
* @param Lx Horizontal gradient of the input image
241+
* @param Ly Vertical gradient of the input image
242+
* @param nbins Number of histogram bins
243+
* @return k contrast factor
244+
*/
245+
static inline float
246+
compute_kcontrast(const cv::Mat& Lx, const cv::Mat& Ly, float perc, int nbins)
247+
{
248+
CV_INSTRUMENT_REGION()
249+
250+
CV_Assert(nbins > 2);
251+
CV_Assert(!Lx.empty());
252+
253+
// temporary square roots of dot product
254+
Mat modgs (Lx.rows - 2, Lx.cols - 2, CV_32F);
255+
const int total = modgs.cols * modgs.rows;
256+
float *modg = modgs.ptr<float>();
257+
258+
for (int i = 1; i < Lx.rows - 1; i++) {
259+
const float *lx = Lx.ptr<float>(i) + 1;
260+
const float *ly = Ly.ptr<float>(i) + 1;
261+
const int cols = Lx.cols - 2;
262+
263+
for (int j = 0; j < cols; j++)
264+
*modg++ = sqrtf(lx[j] * lx[j] + ly[j] * ly[j]);
265+
}
266+
modg = modgs.ptr<float>();
267+
268+
// Get the maximum
269+
float hmax = *std::max_element(modg, modg + total);
270+
271+
if (hmax == 0.0f)
272+
return 0.03f; // e.g. a blank image
273+
274+
// Compute the bin numbers: the value range [0, hmax] -> [0, nbins-1]
275+
modgs *= (nbins - 1) / hmax;
276+
277+
// Count up histogram
278+
std::vector<int> hist(nbins, 0);
279+
for (int i = 0; i < total; i++)
280+
hist[(int)modg[i]]++;
281+
282+
// Now find the perc of the histogram percentile
283+
const int nthreshold = (int)((total - hist[0]) * perc); // Exclude hist[0] as background
284+
int nelements = 0;
285+
for (int k = 1; k < nbins; k++) {
286+
if (nelements >= nthreshold)
287+
return (float)hmax * k / nbins;
288+
289+
nelements += hist[k];
290+
}
291+
292+
return 0.03f;
293+
}
294+
236295
/**
237296
* @brief This method creates the nonlinear scale space for a given image
238297
* @param img Input image for which the nonlinear scale space needs to be created
@@ -253,21 +312,15 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const Mat& img)
253312
return 0;
254313
}
255314

256-
// First compute the kcontrast factor
257-
float kcontrast = compute_k_percentile(img, options_.kcontrast_percentile, 1.0f, options_.kcontrast_nbins, 0, 0);
258-
259-
// temporaries for diffusity computation, to reuse the same memory to improve locality
260-
Size base_size = evolution_[0].Lt.size();
261-
// buffers
262-
Mat Lx_buf (base_size, CV_32F);
263-
Mat Ly_buf (base_size, CV_32F);
264-
Mat Lflow_buf (base_size, CV_32F);
265-
Mat Lstep_buf (base_size, CV_32F);
266-
// views pointing to buffers
267-
Mat Lx (base_size, CV_32F, Lx_buf.data);
268-
Mat Ly (base_size, CV_32F, Ly_buf.data);
269-
Mat Lflow (base_size, CV_32F, Lflow_buf.data);
270-
Mat Lstep (base_size, CV_32F, Lstep_buf.data);
315+
// derivatives, flow and diffusion step
316+
Mat Lx, Ly, Lflow, Lstep;
317+
318+
// compute derivatives for computing k contrast, reuse Lflow for gaussian
319+
GaussianBlur(img, Lflow, Size(5, 5), 1.0f, 1.0f, BORDER_REPLICATE);
320+
Scharr(Lflow, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT);
321+
Scharr(Lflow, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT);
322+
// compute the kcontrast factor
323+
float kcontrast = compute_kcontrast(Lx, Ly, options_.kcontrast_percentile, options_.kcontrast_nbins);
271324

272325
// Now generate the rest of evolution levels
273326
for (size_t i = 1; i < evolution_.size(); i++) {
@@ -277,12 +330,6 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const Mat& img)
277330
// new octave will be half the size
278331
resize(evolution_[i - 1].Lt, e.Lt, e.size, 0, 0, INTER_AREA);
279332
kcontrast *= 0.75f;
280-
281-
// resize temporary views to buffers to prevent reallocation
282-
Lx = Mat(e.size, CV_32F, Lx_buf.data);
283-
Ly = Mat(e.size, CV_32F, Ly_buf.data);
284-
Lflow = Mat(e.size, CV_32F, Lflow_buf.data);
285-
Lstep = Mat(e.size, CV_32F, Lstep_buf.data);
286333
}
287334
else {
288335
evolution_[i - 1].Lt.copyTo(e.Lt);
@@ -317,6 +364,7 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const Mat& img)
317364
std::vector<float> &tsteps = tsteps_[i - 1];
318365
for (size_t j = 0; j < tsteps.size(); j++) {
319366
// Lstep must be preallocated before this parallel loop
367+
Lstep.create(e.Lt.size(), e.Lt.type());
320368
const float step_size = tsteps[j] * 0.5f;
321369
parallel_for_(Range(0, e.Lt.rows), NonLinearScalarDiffusionStep(e.Lt, Lflow, Lstep, step_size));
322370
e.Lt += Lstep;

0 commit comments

Comments
 (0)