|
|
|
@ -252,6 +252,81 @@ static void gradientHist(const cv::Mat &src, cv::Mat &magnitude, cv::Mat &histog |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* The class parallelizing the edgenms algorithm. |
|
|
|
|
* |
|
|
|
|
* \param E : edge image |
|
|
|
|
* \param O : orientation image |
|
|
|
|
* \param dst : destination image |
|
|
|
|
* \param r : radius for NMS suppression |
|
|
|
|
* \param s : radius for boundary suppression |
|
|
|
|
* \param m : multiplier for conservative suppression |
|
|
|
|
*/ |
|
|
|
|
class NmsInvoker : public cv::ParallelLoopBody |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
const cv::Mat &E; |
|
|
|
|
const cv::Mat &O; |
|
|
|
|
cv::Mat &dst; |
|
|
|
|
const int r; |
|
|
|
|
const float m; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
NmsInvoker(const cv::Mat &_E, const cv::Mat &_O, cv::Mat &_dst, const int _r, const float _m) |
|
|
|
|
: E(_E), O(_O), dst(_dst), r(_r), m(_m) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void operator()(const cv::Range &range) const |
|
|
|
|
{ |
|
|
|
|
for (int x = range.start; x < range.end; x++) |
|
|
|
|
{ |
|
|
|
|
const float *e_ptr = E.ptr<float>(x); |
|
|
|
|
const float *o_ptr = O.ptr<float>(x); |
|
|
|
|
float *dst_ptr = dst.ptr<float>(x); |
|
|
|
|
for (int y=0; y < E.cols; y++) |
|
|
|
|
{ |
|
|
|
|
float e = e_ptr[y]; |
|
|
|
|
dst_ptr[y] = e; |
|
|
|
|
if (!e) continue; |
|
|
|
|
e *= m; |
|
|
|
|
float coso = cos(o_ptr[y]); |
|
|
|
|
float sino = sin(o_ptr[y]); |
|
|
|
|
for (int d=-r; d<=r; d++) |
|
|
|
|
{ |
|
|
|
|
if (d) |
|
|
|
|
{ |
|
|
|
|
float xdcos = x+d*coso; |
|
|
|
|
float ydsin = y+d*sino; |
|
|
|
|
xdcos = xdcos < 0 ? 0 : (xdcos > E.rows - 1.001f ? E.rows - 1.001f : xdcos); |
|
|
|
|
ydsin = ydsin < 0 ? 0 : (ydsin > E.cols - 1.001f ? E.cols - 1.001f : ydsin); |
|
|
|
|
int x0 = (int)xdcos; |
|
|
|
|
int y0 = (int)ydsin; |
|
|
|
|
int x1 = x0 + 1; |
|
|
|
|
int y1 = y0 + 1; |
|
|
|
|
float dx0 = xdcos - x0; |
|
|
|
|
float dy0 = ydsin - y0; |
|
|
|
|
float dx1 = 1 - dx0; |
|
|
|
|
float dy1 = 1 - dy0; |
|
|
|
|
float e0 = E.at<float>(x0, y0) * dx1 * dy1 + |
|
|
|
|
E.at<float>(x1, y0) * dx0 * dy1 + |
|
|
|
|
E.at<float>(x0, y1) * dx1 * dy0 + |
|
|
|
|
E.at<float>(x1, y1) * dx0 * dy0; |
|
|
|
|
|
|
|
|
|
if(e < e0) |
|
|
|
|
{ |
|
|
|
|
dst_ptr[y] = 0; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/********************* RFFeatureGetter class *********************/ |
|
|
|
|
|
|
|
|
|
namespace cv |
|
|
|
@ -442,21 +517,23 @@ public: |
|
|
|
|
/*!
|
|
|
|
|
* The function detects edges in src and draw them to dst |
|
|
|
|
* |
|
|
|
|
* \param src : source image (RGB, float, in [0;1]) to detect edges |
|
|
|
|
* \param dst : destination image (grayscale, float, in [0;1]) |
|
|
|
|
* \param _src : source image (RGB, float, in [0;1]) to detect edges |
|
|
|
|
* \param _dst : destination image (grayscale, float, in [0;1]) |
|
|
|
|
* where edges are drawn |
|
|
|
|
*/ |
|
|
|
|
void detectEdges(const cv::Mat &src, cv::Mat &dst) const |
|
|
|
|
void detectEdges(cv::InputArray _src, cv::OutputArray _dst) const |
|
|
|
|
{ |
|
|
|
|
CV_Assert( src.type() == CV_32FC3 ); |
|
|
|
|
CV_Assert( _src.type() == CV_32FC3 ); |
|
|
|
|
|
|
|
|
|
dst.create( src.size(), cv::DataType<float>::type ); |
|
|
|
|
_dst.createSameSize( _src, cv::DataType<float>::type ); |
|
|
|
|
_dst.setTo(0); |
|
|
|
|
Mat dst = _dst.getMat(); |
|
|
|
|
|
|
|
|
|
int padding = ( __rf.options.patchSize |
|
|
|
|
- __rf.options.patchInnerSize )/2; |
|
|
|
|
|
|
|
|
|
cv::Mat nSrc; |
|
|
|
|
copyMakeBorder( src, nSrc, padding, padding, |
|
|
|
|
copyMakeBorder( _src, nSrc, padding, padding, |
|
|
|
|
padding, padding, BORDER_REFLECT ); |
|
|
|
|
|
|
|
|
|
NChannelsMat features; |
|
|
|
@ -469,6 +546,101 @@ public: |
|
|
|
|
predictEdges( features, dst ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* The function computes orientation from edge image. |
|
|
|
|
* |
|
|
|
|
* \param src : edge image. |
|
|
|
|
* \param dst : orientation image. |
|
|
|
|
* \param r : filter radius. |
|
|
|
|
*/ |
|
|
|
|
void computeOrientation(cv::InputArray _src, cv::OutputArray _dst) const |
|
|
|
|
{ |
|
|
|
|
CV_Assert( _src.type() == CV_32FC1 ); |
|
|
|
|
|
|
|
|
|
cv::Mat Oxx, Oxy, Oyy; |
|
|
|
|
|
|
|
|
|
_dst.createSameSize( _src, _src.type() ); |
|
|
|
|
_dst.setTo(0); |
|
|
|
|
|
|
|
|
|
Mat src = _src.getMat(); |
|
|
|
|
cv::Mat E_conv = imsmooth(src, __rf.options.gradientNormalizationRadius); |
|
|
|
|
|
|
|
|
|
Sobel(E_conv, Oxx, -1, 2, 0); |
|
|
|
|
Sobel(E_conv, Oxy, -1, 1, 1); |
|
|
|
|
Sobel(E_conv, Oyy, -1, 0, 2); |
|
|
|
|
|
|
|
|
|
Mat dst = _dst.getMat(); |
|
|
|
|
float *o = dst.ptr<float>(); |
|
|
|
|
float *oxx = Oxx.ptr<float>(); |
|
|
|
|
float *oxy = Oxy.ptr<float>(); |
|
|
|
|
float *oyy = Oyy.ptr<float>(); |
|
|
|
|
for (int i = 0; i < dst.rows * dst.cols; i++) |
|
|
|
|
{ |
|
|
|
|
int xysign = -((oxy[i] > 0) - (oxy[i] < 0)); |
|
|
|
|
o[i] = (atan((oyy[i] * xysign / (oxx[i] + 1e-5))) > 0) ? (float) fmod( |
|
|
|
|
atan((oyy[i] * xysign / (oxx[i] + 1e-5))), M_PI) : (float) fmod( |
|
|
|
|
atan((oyy[i] * xysign / (oxx[i] + 1e-5))) + M_PI, M_PI); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* The function suppress edges where edge is stronger in orthogonal direction |
|
|
|
|
* \param edge_image : edge image from detectEdges function. |
|
|
|
|
* \param orientation_image : orientation image from computeOrientation function. |
|
|
|
|
* \param _dst : suppressed image (grayscale, float, in [0;1]) |
|
|
|
|
* \param r : radius for NMS suppression. |
|
|
|
|
* \param s : radius for boundary suppression. |
|
|
|
|
* \param m : multiplier for conservative suppression. |
|
|
|
|
* \param isParallel: enables/disables parallel computing. |
|
|
|
|
*/ |
|
|
|
|
void edgesNms(cv::InputArray edge_image, cv::InputArray orientation_image, cv::OutputArray _dst, int r, int s, float m, bool isParallel) const |
|
|
|
|
{ |
|
|
|
|
CV_Assert(edge_image.type() == CV_32FC1); |
|
|
|
|
CV_Assert(orientation_image.type() == CV_32FC1); |
|
|
|
|
|
|
|
|
|
cv::Mat E = edge_image.getMat(); |
|
|
|
|
cv::Mat O = orientation_image.getMat(); |
|
|
|
|
cv::Mat E_t = E.t(); |
|
|
|
|
cv::Mat O_t = O.t(); |
|
|
|
|
|
|
|
|
|
cv::Mat dst = _dst.getMat(); |
|
|
|
|
dst.create(E.cols, E.rows, E.type()); |
|
|
|
|
dst.setTo(0); |
|
|
|
|
|
|
|
|
|
cv::Range sizeRange = cv::Range(0, E_t.rows); |
|
|
|
|
NmsInvoker body = NmsInvoker(E_t, O_t, dst, r, m); |
|
|
|
|
if (isParallel) |
|
|
|
|
{ |
|
|
|
|
cv::parallel_for_(sizeRange, body); |
|
|
|
|
} else |
|
|
|
|
{ |
|
|
|
|
body(sizeRange); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
s = s > E_t.rows / 2 ? E_t.rows / 2 : s; |
|
|
|
|
s = s > E_t.cols / 2 ? E_t.cols / 2 : s; |
|
|
|
|
for (int x=0; x<s; x++) |
|
|
|
|
{ |
|
|
|
|
for (int y=0; y<E_t.cols; y++) |
|
|
|
|
{ |
|
|
|
|
dst.at<float>(x, y) *= x / (float)s; |
|
|
|
|
dst.at<float>(E_t.rows-1-x, y) *= x / (float)s; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (int x=0; x < E_t.rows; x++) |
|
|
|
|
{ |
|
|
|
|
for (int y=0; y < s; y++) |
|
|
|
|
{ |
|
|
|
|
dst.at<float>(x, y) *= y / (float)s; |
|
|
|
|
dst.at<float>(x, E_t.cols-1-y) *= y / (float)s; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
transpose(dst, dst); |
|
|
|
|
dst.copyTo(_dst); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
/*!
|
|
|
|
|
* Private method used by process method. The function |
|
|
|
|