text: 
  /*M///////////////////////////////////////////////////////////////////////////////////////
  //
  //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  //
  //  By downloading, copying, installing or using the software you agree to this license.
  //  If you do not agree to this license, do not download, install,
  //  copy or use the software.
  //
  //
  //                           License Agreement
  //                For Open Source Computer Vision Library
  //
  // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
  // Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
  // Third party copyrights are property of their respective owners.
  //
  //   * Redistribution's of source code must retain the above copyright notice,
  //     this list of conditions and the following disclaimer.
  //
  //   * Redistribution's in binary form must reproduce the above copyright notice,
  //     this list of conditions and the following disclaimer in the documentation
  //     and/or other materials provided with the distribution.
  //
  //   * The name of Intel Corporation may not be used to endorse or promote products
  //     derived from this software without specific prior written permission.
  //
  // This software is provided by the copyright holders and contributors "as is" and
  // any express or implied warranties, including, but not limited to, the implied
  // warranties of merchantability and fitness for a particular purpose are disclaimed.
  // In no event shall the Intel Corporation or contributors be liable for any direct,
  // indirect, incidental, special, exemplary, or consequential damages
  // (including, but not limited to, procurement of substitute goods or services;
  // loss of use, data, or profits; or business interruption) however caused
  // and on any theory of liability, whether in contract, strict liability,
  // or tort (including negligence or otherwise) arising in any way out of
  // the use of this software, even if advised of the possibility of such damage.
  //
  //M*/
  
  /*
  Generate Edge Boxes object proposals in given image(s).
  Compute Edge Boxes object proposals as described in:
    C. Lawrence Zitnick and Piotr Dollár
    "Edge Boxes: Locating Object Proposals from Edges", ECCV 2014.
  The proposal boxes are fast to compute and give state-of-the-art recall.
  OpenCV port by: Leonardo Lontra <lhe dot lontra at gmail dot com>
  */
  
  #include "precomp.hpp"
  using namespace cv;
  using namespace std;
  
  inline int clamp(int v, int min, int max)
  {
    return v < min ? min : v > max ? max : v;
  }
  
  namespace cv
  {
  namespace ximgproc
  {
  
  class EdgeBoxesImpl : public EdgeBoxes
  {
  public:
  
    EdgeBoxesImpl(float alpha,
                    float beta,
                    float eta,
                    float minScore,
                    int maxBoxes,
                    float edgeMinMag,
                    float edgeMergeThr,
                    float clusterMinMag,
                    float maxAspectRatio,
                    float minBoxArea,
                    float gamma,
                    float kappa);
  
      virtual void getBoundingBoxes(InputArray edge_map, InputArray orientation_map, std::vector<Rect> &boxes, OutputArray scores = noArray()) CV_OVERRIDE;
  
      float getAlpha() const CV_OVERRIDE { return _alpha; }
      void setAlpha(float value) CV_OVERRIDE
      {
        _alpha = value;
        _sxStep = sqrt(1 / _alpha);
        _ayStep = (1 + _alpha) / (2 * _alpha);
        _xyStepRatio = (1 - _alpha) / (1 + _alpha);
      }
  
      float getBeta() const CV_OVERRIDE { return _beta; }
      void setBeta(float value) CV_OVERRIDE { _beta = value; }
  
      float getEta() const CV_OVERRIDE { return _eta; }
      void setEta(float value) CV_OVERRIDE { _eta = value; }
  
      float getMinScore() const CV_OVERRIDE { return _minScore; }
      void setMinScore(float value) CV_OVERRIDE { _minScore = value; }
  
      int getMaxBoxes() const CV_OVERRIDE { return _maxBoxes; }
      void setMaxBoxes(int value) CV_OVERRIDE { _maxBoxes = value; }
  
      float getEdgeMinMag() const CV_OVERRIDE { return _edgeMinMag; }
      void setEdgeMinMag(float value) CV_OVERRIDE { _edgeMinMag = value; }
  
      float getEdgeMergeThr() const CV_OVERRIDE { return _edgeMergeThr; }
      void setEdgeMergeThr(float value) CV_OVERRIDE { _edgeMergeThr = value; }
  
      float getClusterMinMag() const CV_OVERRIDE { return _clusterMinMag; }
      void setClusterMinMag(float value) CV_OVERRIDE { _clusterMinMag = value; }
  
      float getMaxAspectRatio() const CV_OVERRIDE { return _maxAspectRatio; }
      void setMaxAspectRatio(float value) CV_OVERRIDE { _maxAspectRatio = value; }
  
      float getMinBoxArea() const CV_OVERRIDE { return _minBoxArea; }
      void setMinBoxArea(float value) CV_OVERRIDE { _minBoxArea = value; }
  
      float getGamma() const CV_OVERRIDE { return _gamma; }
      void setGamma(float value) CV_OVERRIDE { _gamma = value; }
  
      float getKappa() const CV_OVERRIDE { return _kappa; }
      void setKappa(float value) CV_OVERRIDE
      {
        _kappa = value;
        _scaleNorm.resize(10000);
        for (int i = 0; i < 10000; i++) _scaleNorm[i] = pow(1.f / i, _kappa);
      }
  
      //! the destructor
      virtual ~EdgeBoxesImpl() {}
  
  private:
      float _alpha;
      float _beta;
      float _eta;
      float _minScore;
      int _maxBoxes;
      float _edgeMinMag;
      float _edgeMergeThr;
      float _clusterMinMag;
      float _maxAspectRatio;
      float _minBoxArea;
      float _gamma;
      float _kappa;
  
      // edge segment information (see clusterEdges)
      int h, w;                         // image dimensions
      int _segCnt;                      // total segment count
      Mat _segIds;                      // segment ids (-1/0 means no segment)
      vector<float> _segMag;            // segment edge magnitude sums
      vector<Point2i> _segP;            // segment lower-right pixel
      vector<vector<float> > _segAff;   // segment affinities
      vector<vector<int> > _segAffIdx;  // segment neighbors
  
      // data structures for efficiency (see prepDataStructs)
      Mat _segIImg, _magIImg;
      Mat _hIdxImg, _vIdxImg;
      vector<vector<int> > _hIdxs, _vIdxs;
      vector<float> _scaleNorm;
      float _sxStep, _ayStep, _xyStepRatio;
  
      // data structures for efficiency (see scoreBox)
      Mat _sWts;
      Mat _sDone, _sMap, _sIds;
      int _sId;
  
      // helper routines
      static bool boxesCompare(const Box &a, const Box &b) { return a.score < b.score; }
      void clusterEdges(Mat &edgeMap, Mat &orientationMap);
      void prepDataStructs(Mat &edgeMap);
      void scoreAllBoxes(Boxes &boxes);
      void scoreBox(Box &box);
      void refineBox(Box &box);
      float boxesOverlap(Box &a, Box &b);
      void boxesNms(Boxes &boxes, float thr, float eta, int maxBoxes);
  };
  
  
  EdgeBoxesImpl::EdgeBoxesImpl(float alpha,
                               float beta,
                               float eta,
                               float minScore,
                               int maxBoxes,
                               float edgeMinMag,
                               float edgeMergeThr,
                               float clusterMinMag,
                               float maxAspectRatio,
                               float minBoxArea,
                               float gamma,
                               float kappa)
      : _alpha(alpha),
        _beta(beta),
        _eta(eta),
        _minScore(minScore),
        _maxBoxes(maxBoxes),
        _edgeMinMag(edgeMinMag),
        _edgeMergeThr(edgeMergeThr),
        _clusterMinMag(clusterMinMag),
        _maxAspectRatio(maxAspectRatio),
        _minBoxArea(minBoxArea),
        _gamma(gamma),
        _kappa(kappa)
  
  {
    // initialize step sizes
    _sxStep = sqrt(1 / _alpha);
    _ayStep = (1 + _alpha) / (2 * _alpha);
    _xyStepRatio = (1 - _alpha) / (1 + _alpha);
  
    // create _scaleNorm
    _scaleNorm.resize(10000);
    for (int i = 0; i < 10000; i++) _scaleNorm[i] = pow(1.f / i, _kappa);
  
  }
  
  
  void EdgeBoxesImpl::clusterEdges(Mat &edgeMap, Mat &orientationMap)
  {
      int x, y, xd, yd, i, j;
  
      // greedily merge connected edge pixels into clusters (create _segIds)
      _segIds = Mat::zeros(w, h, DataType<int>::type);
      _segCnt = 1;
      for (x = 0; x < w; x++)
      {
          const float *e_ptr = edgeMap.ptr<float>(x);
          int *s_ptr = _segIds.ptr<int>(x);
          for (y = 0; y < h; y++)
          {
              if (x == 0 || y == 0 || x == w - 1 || y == h - 1 || e_ptr[y] <= _edgeMinMag)
              {
                  s_ptr[y] = -1;
              }
          }
      }
  
      for (x = 1; x < w - 1; x++)
      {
          int *s_ptr = _segIds.ptr<int>(x);
          for (y = 1; y < h - 1; y++)
          {
              if (s_ptr[y] != 0) continue;
              float sumv = 0;
              int x0 = x;
              int y0 = y;
              vector<float> vs;
              vector<int> xs, ys;
  
              while (sumv < _edgeMergeThr)
              {
                  _segIds.at<int>(x0, y0) = _segCnt;
                  float o0 = orientationMap.at<float>(x0, y0);
                  float o1, v;
                  bool found;
                  for (xd = -1; xd <= 1; xd++)
                  {
                      const int *s0_ptr = _segIds.ptr<int>(x0 + xd);
                      const float *o_ptr = orientationMap.ptr<float>(x0 + xd);
                      for (yd = -1; yd <= 1; yd++)
                      {
                          if (s0_ptr[y0 + yd] != 0) continue;
                          found = false;
                          for (i = 0; i < (int)xs.size(); i++)
                          {
                              if (xs[i] == x0 + xd && ys[i] == y0 + yd)
                              {
                                  found = true;
                                  break;
                              }
                          }
                          if (found) continue;
                          o1 = o_ptr[y0 + yd];
                          v = fabs(o1 - o0) / (float)CV_PI;
                          if (v > .5f) v = 1 - v;
                          vs.push_back(v);
                          xs.push_back(x0 + xd);
                          ys.push_back(y0 + yd);
                      }
                  }
                  float minv = 1000;
                  j = 0;
                  for (i = 0; i < (int)vs.size(); i++)
                  {
                      if (vs[i] < minv)
                      {
                          minv = vs[i];
                          x0 = xs[i];
                          y0 = ys[i];
                          j = i;
                      }
                  }
                  sumv += minv;
                  if (minv < 1000) vs[j] = 1000;
              }
  
              _segCnt++;
          }
      }
  
      // merge or remove small segments
      _segMag.resize(_segCnt, 0);
      for (x = 1; x < w - 1; x++)
      {
          const float *e_ptr = edgeMap.ptr<float>(x);
          const int *s_ptr = _segIds.ptr<int>(x);
          for (y = 1; y < h - 1; y++)
          {
            j = s_ptr[y];
            if (j > 0) _segMag[j] += e_ptr[y];
          }
      }
  
      for (x = 1; x < w - 1; x++)
      {
          int *s_ptr = _segIds.ptr<int>(x);
          for (y = 1; y < h - 1; y++)
          {
            j = s_ptr[y];
            if (j > 0 && _segMag[j] <= _clusterMinMag)
                s_ptr[y] = 0;
          }
      }
  
      i = 1;
      while (i > 0)
      {
          i = 0;
          for (x = 1; x < w - 1; x++)
          {
              int *s0_ptr = _segIds.ptr<int>(x);
              const float *o0_ptr = orientationMap.ptr<float>(x);
              for (y = 1; y < h - 1; y++)
              {
                  if (s0_ptr[y] != 0) continue;
                  float o0 = o0_ptr[y];
                  float o1, v, minv = 1000;
                  j = 0;
  
                  for (xd = -1; xd <= 1; xd++)
                  {
                      const int *s1_ptr = _segIds.ptr<int>(x+xd);
                      const float *o1_ptr = orientationMap.ptr<float>(x+xd);
                      for (yd = -1; yd <= 1; yd++)
                      {
                          if (s1_ptr[y + yd] <= 0) continue;
                          o1 = o1_ptr[y + yd];
                          v = fabs(o1 - o0) / (float)CV_PI;
                          if (v > .5f) v = 1 - v;
                          if (v < minv)
                          {
                              minv = v;
                              j = s1_ptr[y + yd];
                          }
                      }
                  }
  
                  s0_ptr[y] = j;
                  if (j > 0) i++;
              }
          }
      }
  
      // compactify representation
      _segMag.assign(_segCnt, 0);
      vector<int> map(_segCnt, 0);
      _segCnt = 1;
      for (x = 1; x < w - 1; x++)
      {
          const float *e_ptr = edgeMap.ptr<float>(x);
          const int *s_ptr = _segIds.ptr<int>(x);
          for (y = 1; y < h - 1; y++)
          {
              j = s_ptr[y];
              if (j > 0) _segMag[j] += e_ptr[y];
          }
      }
  
      for (i = 0; i < (int)_segMag.size(); i++)
      {
        if (_segMag[i] > 0) map[i] = _segCnt++;
      }
  
      for (x = 1; x < w - 1; x++)
      {
          int *s_ptr = _segIds.ptr<int>(x);
          for (y = 1; y < h - 1; y++)
          {
            j = s_ptr[y];
            if (j > 0) s_ptr[y] = map[j];
          }
      }
  
      // compute positional means and recompute _segMag
      _segMag.assign(_segCnt, 0);
      vector<float> meanX(_segCnt, 0), meanY(_segCnt, 0);
      vector<float> meanOx(_segCnt, 0), meanOy(_segCnt, 0), meanO(_segCnt, 0);
      for (x = 1; x < w - 1; x++)
      {
          int *s_ptr = _segIds.ptr<int>(x);
          const float *e_ptr = edgeMap.ptr<float>(x);
          const float *o_ptr = orientationMap.ptr<float>(x);
          for (y = 1; y < h - 1; y++)
          {
              j = s_ptr[y];
              if (j <= 0) continue;
              float m = e_ptr[y];
              float o = o_ptr[y];
              _segMag[j] += m;
              meanOx[j] += m * cos(2 * o);
              meanOy[j] += m * sin(2 * o);
              meanX[j] += m * x;
              meanY[j] += m * y;
          }
      }
  
      for (i = 0; i < _segCnt; i++)
      {
          if (_segMag[i] > 0)
          {
              float m = _segMag[i];
              meanX[i] /= m;
              meanY[i] /= m;
              meanO[i] = atan2(meanOy[i] / m, meanOx[i] / m) / 2;
          }
      }
  
      // compute segment affinities
      _segAff.resize(_segCnt);
      _segAffIdx.resize(_segCnt);
      for (i = 0; i < _segCnt; i++)
      {
        _segAff[i].resize(0);
        _segAffIdx[i].resize(0);
      }
  
      const int rad = 2;
      for (x = rad; x < w - rad; x++)
      {
          const int *s0_ptr = _segIds.ptr<int>(x);
          for (y = rad; y < h - rad; y++)
          {
              int s0 = s0_ptr[y];
              if (s0 <= 0) continue;
              for (xd = -rad; xd <= rad; xd++)
              {
                  const int *s1_ptr = _segIds.ptr<int>(x+xd);
                  for (yd = -rad; yd <= rad; yd++)
                  {
                      int s1 = s1_ptr[y + yd];
                      if (s1 <= s0) continue;
                      bool found = false;
  
                      for (i = 0; i < (int)_segAffIdx[s0].size(); i++)
                      {
                          if (_segAffIdx[s0][i] == s1)
                          {
                              found = true;
                              break;
                          }
                      }
                      if (found) continue;
                      float o = atan2(meanY[s0] - meanY[s1], meanX[s0] - meanX[s1]) + (float)CV_PI / 2.0f;
                      float a = fabs(cos(meanO[s0] - o) * cos(meanO[s1] - o));
                      a = pow(a, _gamma);
                      _segAff[s0].push_back(a);
                      _segAffIdx[s0].push_back(s1);
                      _segAff[s1].push_back(a);
                      _segAffIdx[s1].push_back(s0);
                  }
              }
          }
      }
  
      // compute _segC and _segR
      _segP.resize(_segCnt);
      for (x = 1; x < w - 1; x++)
      {
          const int *s_ptr = _segIds.ptr<int>(x);
          for (y = 1; y < h - 1; y++)
          {
              j = s_ptr[y];
              if (j > 0)
              {
                  _segP[j] = Point2i(x, y);
              }
          }
      }
  }
  
  
  void EdgeBoxesImpl::prepDataStructs(Mat &edgeMap)
  {
      int y, x, i;
  
      // create _segIImg
      Mat E1 = Mat::zeros(w, h, DataType<float>::type);
  
      for (i=0; i < _segCnt; i++)
      {
        if (_segMag[i] > 0) E1.at<float>(_segP[i].x, _segP[i].y) = _segMag[i];
      }
  
      _segIImg = Mat::zeros(w+1, h+1, DataType<float>::type);
      _magIImg = Mat::zeros(w+1, h+1, DataType<float>::type);
  
      for (x=1; x < w; x++)
      {
        const float *e_ptr = edgeMap.ptr<float>(x);
        const float *e1_ptr = E1.ptr<float>(x);
        const float *si0_ptr = _segIImg.ptr<float>(x);
        float *si1_ptr = _segIImg.ptr<float>(x+1);
        const float *mi0_ptr = _magIImg.ptr<float>(x);
        float *mi1_ptr =_magIImg.ptr<float>(x+1);
        for (y=1; y < h; y++)
        {
          // create _segIImg
          si1_ptr[y+1] = e1_ptr[y] + si0_ptr[y+1] + si1_ptr[y] - si0_ptr[y];
          float e = e_ptr[y] > _edgeMinMag ? e_ptr[y] : 0;
          // create _magIImg
          mi1_ptr[y+1] = e +mi0_ptr[y+1] + mi1_ptr[y] - mi0_ptr[y];
        }
      }
  
      // create remaining data structures
      int s = 0;
      int s1;
  
      _hIdxs.resize(h);
      _hIdxImg = Mat::zeros(w, h, DataType<int>::type);
      for (y = 0; y < h; y++)
      {
          s = 0;
          _hIdxs[y].push_back(s);
          for (x = 0; x < w; x++)
          {
              s1 = _segIds.at<int>(x, y);
              if (s1 != s)
              {
                  s = s1;
                  _hIdxs[y].push_back(s);
              }
              _hIdxImg.at<int>(x, y) = (int)_hIdxs[y].size() - 1;
          }
      }
  
      _vIdxs.resize(w);
      _vIdxImg = Mat::zeros(w, h, DataType<int>::type);
      for (x = 0; x < w; x++)
      {
          s = 0;
          _vIdxs[x].push_back(s);
          for (y = 0; y < h; y++)
          {
              s1 = _segIds.at<int>(x, y);
              if (s1 != s)
              {
                  s = s1;
                  _vIdxs[x].push_back(s);
              }
              _vIdxImg.at<int>(x, y) = (int)_vIdxs[x].size() - 1;
          }
      }
  
      // initialize scoreBox() data structures
      int n = _segCnt + 1;
      _sWts = Mat::zeros(n, 1, DataType<float>::type);
      _sDone = Mat(n, 1, DataType<int>::type,Scalar(-1));
      _sMap = Mat::zeros(n, 1, DataType<int>::type);
      _sIds = Mat::zeros(n, 1, DataType<int>::type);
      _sId = 0;
  }
  
  
  void EdgeBoxesImpl::scoreBox(Box &box)
  {
      int i, j, k, q, bh, bw, y0, x0, y1, x1, y0m, y1m, x0m, x1m;
      float *sWts = (float *)_sWts.data;
      int *sDone = (int *)_sDone.data;
      int *sMap = (int *)_sMap.data;
      int *sIds = (int *)_sIds.data;
      int sId = _sId++;
  
      // add edge count inside box
      y1 = clamp(box.y + box.h, 0, h - 1);
      y0 = box.y = clamp(box.y, 0, h - 1);
      x1 = clamp(box.x + box.w, 0, w - 1);
      x0 = box.x = clamp(box.x, 0, w - 1);
      bh = box.h = y1 - box.y;
      bh /= 2;
      bw = box.w = x1 - box.x;
      bw /= 2;
      float v = _segIImg.at<float>(x0, y0) + _segIImg.at<float>(x1 + 1, y1 + 1)
                - _segIImg.at<float>(x1 + 1, y0) - _segIImg.at<float>(x0, y1 + 1);
  
      // subtract middle quarter of edges
      y0m = y0 + bh / 2;
      y1m = y0m + bh;
      x0m = x0 + bw / 2;
      x1m = x0m + bw;
      v -= _magIImg.at<float>(x0m, y0m) + _magIImg.at<float>(x1m + 1, y1m + 1)
           - _magIImg.at<float>(x1m + 1, y0m) - _magIImg.at<float>(x0m, y1m + 1);
  
      // short circuit computation if impossible to score highly
      float norm = _scaleNorm[bw + bh];
      box.score = v * norm;
      if (box.score < _minScore)
      {
          box.score = 0;
          return;
      }
  
      // find interesecting segments along four boundaries
      int cs, ce, rs, re, n = 0;
      cs = _hIdxImg.at<int>(x0, y0);
      ce = _hIdxImg.at<int>(x1, y0); // top
      for (i = cs; i <= ce; i++)
      {
          j = _hIdxs[y0][i];
          if (j > 0 && sDone[j] != sId)
          {
              sIds[n] = j;
              sWts[n] = 1;
              sDone[j] = sId;
              sMap[j] = n++;
          }
      }
  
      cs = _hIdxImg.at<int>(x0, y1);
      ce = _hIdxImg.at<int>(x1, y1); // bottom
      for (i = cs; i <= ce; i++)
      {
          j = _hIdxs[y1][i];
          if (j > 0 && sDone[j] != sId)
          {
              sIds[n] = j;
              sWts[n] = 1;
              sDone[j] = sId;
              sMap[j] = n++;
          }
      }
  
      rs = _vIdxImg.at<int>(x0, y0);
      re = _vIdxImg.at<int>(x0, y1); // left
      for (i = rs; i <= re; i++)
      {
          j = _vIdxs[x0][i];
          if (j > 0 && sDone[j] != sId)
          {
              sIds[n] = j;
              sWts[n] = 1;
              sDone[j] = sId;
              sMap[j] = n++;
          }
      }
  
      rs = _vIdxImg.at<int>(x1, y0);
      re = _vIdxImg.at<int>(x1, y1); // right
      for (i = rs; i <= re; i++)
      {
          j = _vIdxs[x1][i];
          if (j > 0 && sDone[j] != sId)
          {
              sIds[n] = j;
              sWts[n] = 1;
              sDone[j] = sId;
              sMap[j] = n++;
          }
      }
  
      // follow connected paths and set weights accordingly (ws=1 means remove)
      for (i = 0; i < n; i++)
      {
          float ws = sWts[i];
          j = sIds[i];
          for (k = 0; k < (int)_segAffIdx[j].size(); k++)
          {
              q = _segAffIdx[j][k];
              float wq = ws * _segAff[j][k];
              if (wq < .05f) continue; // short circuit for efficiency
              if (sDone[q] == sId)
              {
                  if (wq > sWts[sMap[q]])
                  {
                      sWts[sMap[q]] = wq;
                      i = min(i, sMap[q] - 1);
                  }
              }
              else if (_segP[q].x >= x0 && _segP[q].x <= x1 && _segP[q].y >= y0 && _segP[q].y <= y1)
              {
                  sIds[n] = q;
                  sWts[n] = wq;
                  sDone[q] = sId;
                  sMap[q] = n++;
              }
          }
      }
      // finally remove segments connected to boundaries
      for (i = 0; i < n; i++)
      {
          k = sIds[i];
          if (_segP[k].x >= x0 && _segP[k].x <= x1 && _segP[k].y >= y0 && _segP[k].y <= y1) v -= sWts[i] * _segMag[k];
      }
  
      v *= norm;
      if (v < _minScore) v = 0;
      box.score = v;
  }
  
  
  void EdgeBoxesImpl::refineBox(Box &box)
  {
      int yStep = (int)(box.h * _xyStepRatio);
      int xStep = (int)(box.w * _xyStepRatio);
      while (1)
      {
          // prepare for iteration
          yStep /= 2;
          xStep /= 2;
          if (yStep <= 2 && xStep <= 2) break;
          yStep = max(1, yStep);
          xStep = max(1, xStep);
          Box B;
          // search over y start
          B = box;
          B.y = box.y - yStep;
          B.h = B.h + yStep;
          scoreBox(B);
  
          if (B.score <= box.score)
          {
              B = box;
              B.y = box.y + yStep;
              B.h = B.h - yStep;
              scoreBox(B);
          }
          if (B.score > box.score) box = B;
          // search over y end
          B = box;
          B.h = B.h + yStep;
          scoreBox(B);
  
          if (B.score <= box.score)
          {
              B = box;
              B.h = B.h - yStep;
              scoreBox(B);
          }
          if (B.score > box.score) box = B;
          // search over x start
          B = box;
          B.x = box.x - xStep;
          B.w = B.w + xStep;
          scoreBox(B);
  
          if (B.score <= box.score)
          {
              B = box;
              B.x = box.x + xStep;
              B.w = B.w - xStep;
              scoreBox(B);
          }
  
          if (B.score > box.score) box = B;
          // search over x end
          B = box;
          B.w = B.w + xStep;
          scoreBox(B);
  
          if (B.score <= box.score)
          {
              B = box;
              B.w = B.w - xStep;
              scoreBox(B);
          }
          if (B.score > box.score) box = B;
      }
  }
  
  void EdgeBoxesImpl::scoreAllBoxes(Boxes &boxes)
  {
      // get list of all boxes roughly distributed in grid
      boxes.resize(0);
      int ayRad, sxNum;
      float minSize = sqrt(_minBoxArea);
      ayRad = (int)(log(_maxAspectRatio) / log(_ayStep * _ayStep));
      sxNum = (int)(ceil(log(max(w, h) / minSize) / log(_sxStep)));
  
      for (int s = 0; s < sxNum; s++)
      {
          int a, y, x, bh, bw, ky, kx = -1;
          float ay, sx;
          for (a = 0; a < 2 * ayRad + 1; a++)
          {
              ay = pow(_ayStep, float(a - ayRad));
              sx = minSize * pow(_sxStep, float(s));
              bh = (int)(sx / ay);
              ky = max(2, (int)(bh * _xyStepRatio));
              bw = (int)(sx * ay);
              kx = max(2, (int)(bw * _xyStepRatio));
              for (x = 0; x < w - bw + kx; x += kx)
              {
                  for (y = 0; y < h - bh + ky; y += ky)
                  {
                      Box b;
                      b.y = y;
                      b.x = x;
                      b.h = bh;
                      b.w = bw;
                      boxes.push_back(b);
                  }
              }
          }
      }
  
      // score all boxes, refine top candidates
      int i, k = 0, m = (int)boxes.size();
      for (i = 0; i < m; i++)
      {
          scoreBox(boxes[i]);
          if (!boxes[i].score) continue;
          k++;
          refineBox(boxes[i]);
      }
      sort(boxes.rbegin(), boxes.rend(), boxesCompare);
      boxes.resize(k);
  }
  
  
  float EdgeBoxesImpl::boxesOverlap(Box &a, Box &b)
  {
      float areai, areaj, areaij;
      int y0, y1, x0, x1, y1i, x1i, y1j, x1j;
      y1i = a.y + a.h;
      x1i = a.x + a.w;
      if (a.y >= y1i || a.x >= x1i) return 0;
  
      y1j = b.y + b.h;
      x1j = b.x + b.w;
      if (a.y >= y1j || a.x >= x1j) return 0;
  
      areai = (float) a.w * a.h;
      y0 = max(a.y, b.y);
      y1 = min(y1i, y1j);
      areaj = (float) b.w * b.h;
      x0 = max(a.x, b.x);
      x1 = min(x1i, x1j);
      areaij = (float) max(0, y1 - y0) * max(0, x1 - x0);
      return areaij / (areai + areaj - areaij);
  }
  
  
  void EdgeBoxesImpl::boxesNms(Boxes &boxes, float thr, float eta, int maxBoxes)
  {
      sort(boxes.rbegin(), boxes.rend(), boxesCompare);
      if (thr > .99f) return;
  
      const int nBin = 10000;
      const float step = 1 / thr;
      const float lstep = log(step);
  
      vector<Boxes> kept;
      kept.resize(nBin + 1);
      int n = (int) boxes.size();
      int i = 0;
      int j, k, b;
      int m = 0;
      int d = 1;
  
      while (i < n && m < maxBoxes)
      {
          b = boxes[i].w * boxes[i].h;
  
          bool keep = 1;
          b = clamp((int)(ceil(log(float(b)) / lstep)), d, nBin - d);
          for (j = b - d; j <= b + d; j++)
          {
              for (k = 0; k < (int)kept[j].size(); k++)
              {
                  if (keep)
                      keep = boxesOverlap(boxes[i], kept[j][k]) <= thr;
              }
          }
  
          if (keep)
          {
              kept[b].push_back(boxes[i]);
              m++;
          }
  
          i++;
          if (keep && eta < 1.0f && thr > .5f)
          {
              thr *= eta;
              d = (int)ceil(log(1.0f / thr) / lstep);
          }
      }
  
      boxes.resize(m);
      i = 0;
      for (j = 0; j < nBin; j++)
      {
          for (k = 0; k < (int)kept[j].size(); k++)
          {
              boxes[i++] = kept[j][k];
          }
      }
      sort(boxes.rbegin(), boxes.rend(), boxesCompare);
  }
  
  
  void EdgeBoxesImpl::getBoundingBoxes(InputArray edge_map, InputArray orientation_map, std::vector<Rect> &boxes, OutputArray scores)
  {
      CV_Assert(edge_map.depth() == CV_32F);
      CV_Assert(orientation_map.depth() == CV_32F);
  
      Mat E = edge_map.getMat().t();
      Mat O = orientation_map.getMat().t();
      std::vector<float> _scores;
  
      h = E.cols;
      w = E.rows;
  
      clusterEdges(E, O);
      prepDataStructs(E);
  
      Boxes b;
      scoreAllBoxes(b);
      boxesNms(b, _beta, _eta, _maxBoxes);
  
      // create output boxes
      int n = (int) b.size();
      boxes.resize(n);
  
      if (scores.needed())
      {
          _scores.resize(n);
      }
  
      for(int i=0; i < n; i++)
      {
          boxes[i] = Rect((int)b[i].x + 1, (int)b[i].y + 1, (int)b[i].w, (int)b[i].h);
          if (scores.needed())
          {
              _scores[i] = b[i].score;
          }
      }
  
      // return scores if asked for
      if (scores.needed())
      {
          Mat(_scores).copyTo(scores);
      }
  }
  
  
  Ptr<EdgeBoxes> createEdgeBoxes(float alpha,
                                float beta,
                                float eta,
                                float minScore,
                                int   maxBoxes,
                                float edgeMinMag,
                                float edgeMergeThr,
                                float clusterMinMag,
                                float maxAspectRatio,
                                float minBoxArea,
                                float gamma,
                                float kappa)
  {
      return makePtr<EdgeBoxesImpl>(alpha,
                                    beta,
                                    eta,
                                    minScore,
                                    maxBoxes,
                                    edgeMinMag,
                                    edgeMergeThr,
                                    clusterMinMag,
                                    maxAspectRatio,
                                    minBoxArea,
                                    gamma,
                                    kappa);
  }
  
  }
  }
  