Skip to content

Commit da280ac

Browse files
committed
added computeOrientation and edgenms in structured_edge_detection
fixed typo replaces Mat& to Input/OutputArray in public methods. small fix. small fixes
1 parent 478baf9 commit da280ac

File tree

3 files changed

+211
-9
lines changed

3 files changed

+211
-9
lines changed

modules/ximgproc/include/opencv2/ximgproc/structured_edge_detection.hpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,31 @@ class CV_EXPORTS_W StructuredEdgeDetection : public Algorithm
102102
103103
The algorithm underlies this function is much more robust to texture presence, than common
104104
approaches, e.g. Sobel
105-
@param src source image (RGB, float, in [0;1]) to detect edges
106-
@param dst destination image (grayscale, float, in [0;1]) where edges are drawn
105+
@param _src source image (RGB, float, in [0;1]) to detect edges
106+
@param _dst destination image (grayscale, float, in [0;1]) where edges are drawn
107107
@sa Sobel, Canny
108108
*/
109-
CV_WRAP virtual void detectEdges(const Mat &src, CV_OUT Mat &dst) const = 0;
109+
CV_WRAP virtual void detectEdges(cv::InputArray _src, cv::OutputArray _dst) const = 0;
110+
111+
/** @brief The function computes orientation from edge image.
112+
113+
@param _src edge image.
114+
@param _dst orientation image.
115+
*/
116+
CV_WRAP virtual void computeOrientation(cv::InputArray _src, cv::OutputArray _dst) const = 0;
117+
118+
119+
/** @brief The function edgenms in edge image and suppress edges where edge is stronger in orthogonal direction.
120+
121+
@param edge_image edge image from detectEdges function.
122+
@param orientation_image orientation image from computeOrientation function.
123+
@param _dst suppressed image (grayscale, float, in [0;1])
124+
@param r radius for NMS suppression.
125+
@param s radius for boundary suppression.
126+
@param m multiplier for conservative suppression.
127+
@param isParallel enables/disables parallel computing.
128+
*/
129+
CV_WRAP virtual void edgesNms(cv::InputArray edge_image, cv::InputArray orientation_image, cv::OutputArray _dst, int r = 2, int s = 0, float m = 1, bool isParallel = true) const = 0;
110130
};
111131

112132
/*!

modules/ximgproc/samples/structured_edge_detection.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,20 @@ int main( int argc, const char** argv )
5959
createStructuredEdgeDetection(modelFilename);
6060
pDollar->detectEdges(image, edges);
6161

62+
// computes orientation from edge map
63+
Mat orientation_map;
64+
pDollar->computeOrientation(edges, orientation_map);
65+
66+
// suppress edges
67+
Mat edge_nms;
68+
pDollar->edgesNms(edges, orientation_map, edge_nms, 2, 0, 1, true);
69+
6270
if ( outFilename.size() == 0 )
6371
{
6472
namedWindow("edges", 1);
6573
imshow("edges", edges);
74+
namedWindow("edges nms", 1);
75+
imshow("edges nms", edge_nms);
6676
waitKey(0);
6777
}
6878
else

modules/ximgproc/src/structured_edge_detection.cpp

Lines changed: 178 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,81 @@ static void gradientHist(const cv::Mat &src, cv::Mat &magnitude, cv::Mat &histog
252252
}
253253
}
254254

255+
/*!
256+
* The class parallelizing the edgenms algorithm.
257+
*
258+
* \param E : edge image
259+
* \param O : orientation image
260+
* \param dst : destination image
261+
* \param r : radius for NMS suppression
262+
* \param s : radius for boundary suppression
263+
* \param m : multiplier for conservative suppression
264+
*/
265+
class NmsInvoker : public cv::ParallelLoopBody
266+
{
267+
268+
private:
269+
const cv::Mat &E;
270+
const cv::Mat &O;
271+
cv::Mat &dst;
272+
const int r;
273+
const float m;
274+
275+
public:
276+
NmsInvoker(const cv::Mat &_E, const cv::Mat &_O, cv::Mat &_dst, const int _r, const float _m)
277+
: E(_E), O(_O), dst(_dst), r(_r), m(_m)
278+
{
279+
}
280+
281+
void operator()(const cv::Range &range) const
282+
{
283+
for (int x = range.start; x < range.end; x++)
284+
{
285+
const float *e_ptr = E.ptr<float>(x);
286+
const float *o_ptr = O.ptr<float>(x);
287+
float *dst_ptr = dst.ptr<float>(x);
288+
for (int y=0; y < E.cols; y++)
289+
{
290+
float e = e_ptr[y];
291+
dst_ptr[y] = e;
292+
if (!e) continue;
293+
e *= m;
294+
float coso = cos(o_ptr[y]);
295+
float sino = sin(o_ptr[y]);
296+
for (int d=-r; d<=r; d++)
297+
{
298+
if (d)
299+
{
300+
float xdcos = x+d*coso;
301+
float ydsin = y+d*sino;
302+
xdcos = xdcos < 0 ? 0 : (xdcos > E.rows - 1.001f ? E.rows - 1.001f : xdcos);
303+
ydsin = ydsin < 0 ? 0 : (ydsin > E.cols - 1.001f ? E.cols - 1.001f : ydsin);
304+
int x0 = (int)xdcos;
305+
int y0 = (int)ydsin;
306+
int x1 = x0 + 1;
307+
int y1 = y0 + 1;
308+
float dx0 = xdcos - x0;
309+
float dy0 = ydsin - y0;
310+
float dx1 = 1 - dx0;
311+
float dy1 = 1 - dy0;
312+
float e0 = E.at<float>(x0, y0) * dx1 * dy1 +
313+
E.at<float>(x1, y0) * dx0 * dy1 +
314+
E.at<float>(x0, y1) * dx1 * dy0 +
315+
E.at<float>(x1, y1) * dx0 * dy0;
316+
317+
if(e < e0)
318+
{
319+
dst_ptr[y] = 0;
320+
break;
321+
}
322+
}
323+
}
324+
325+
}
326+
}
327+
}
328+
};
329+
255330
/********************* RFFeatureGetter class *********************/
256331

257332
namespace cv
@@ -442,21 +517,23 @@ class StructuredEdgeDetectionImpl : public StructuredEdgeDetection
442517
/*!
443518
* The function detects edges in src and draw them to dst
444519
*
445-
* \param src : source image (RGB, float, in [0;1]) to detect edges
446-
* \param dst : destination image (grayscale, float, in [0;1])
520+
* \param _src : source image (RGB, float, in [0;1]) to detect edges
521+
* \param _dst : destination image (grayscale, float, in [0;1])
447522
* where edges are drawn
448523
*/
449-
void detectEdges(const cv::Mat &src, cv::Mat &dst) const
524+
void detectEdges(cv::InputArray _src, cv::OutputArray _dst) const
450525
{
451-
CV_Assert( src.type() == CV_32FC3 );
526+
CV_Assert( _src.type() == CV_32FC3 );
452527

453-
dst.create( src.size(), cv::DataType<float>::type );
528+
_dst.createSameSize( _src, cv::DataType<float>::type );
529+
_dst.setTo(0);
530+
Mat dst = _dst.getMat();
454531

455532
int padding = ( __rf.options.patchSize
456533
- __rf.options.patchInnerSize )/2;
457534

458535
cv::Mat nSrc;
459-
copyMakeBorder( src, nSrc, padding, padding,
536+
copyMakeBorder( _src, nSrc, padding, padding,
460537
padding, padding, BORDER_REFLECT );
461538

462539
NChannelsMat features;
@@ -469,6 +546,101 @@ class StructuredEdgeDetectionImpl : public StructuredEdgeDetection
469546
predictEdges( features, dst );
470547
}
471548

549+
/*!
550+
* The function computes orientation from edge image.
551+
*
552+
* \param src : edge image.
553+
* \param dst : orientation image.
554+
* \param r : filter radius.
555+
*/
556+
void computeOrientation(cv::InputArray _src, cv::OutputArray _dst) const
557+
{
558+
CV_Assert( _src.type() == CV_32FC1 );
559+
560+
cv::Mat Oxx, Oxy, Oyy;
561+
562+
_dst.createSameSize( _src, _src.type() );
563+
_dst.setTo(0);
564+
565+
Mat src = _src.getMat();
566+
cv::Mat E_conv = imsmooth(src, __rf.options.gradientNormalizationRadius);
567+
568+
Sobel(E_conv, Oxx, -1, 2, 0);
569+
Sobel(E_conv, Oxy, -1, 1, 1);
570+
Sobel(E_conv, Oyy, -1, 0, 2);
571+
572+
Mat dst = _dst.getMat();
573+
float *o = dst.ptr<float>();
574+
float *oxx = Oxx.ptr<float>();
575+
float *oxy = Oxy.ptr<float>();
576+
float *oyy = Oyy.ptr<float>();
577+
for (int i = 0; i < dst.rows * dst.cols; i++)
578+
{
579+
int xysign = -((oxy[i] > 0) - (oxy[i] < 0));
580+
o[i] = (atan((oyy[i] * xysign / (oxx[i] + 1e-5))) > 0) ? (float) fmod(
581+
atan((oyy[i] * xysign / (oxx[i] + 1e-5))), M_PI) : (float) fmod(
582+
atan((oyy[i] * xysign / (oxx[i] + 1e-5))) + M_PI, M_PI);
583+
}
584+
}
585+
586+
/*!
587+
* The function suppress edges where edge is stronger in orthogonal direction
588+
* \param edge_image : edge image from detectEdges function.
589+
* \param orientation_image : orientation image from computeOrientation function.
590+
* \param _dst : suppressed image (grayscale, float, in [0;1])
591+
* \param r : radius for NMS suppression.
592+
* \param s : radius for boundary suppression.
593+
* \param m : multiplier for conservative suppression.
594+
* \param isParallel: enables/disables parallel computing.
595+
*/
596+
void edgesNms(cv::InputArray edge_image, cv::InputArray orientation_image, cv::OutputArray _dst, int r, int s, float m, bool isParallel) const
597+
{
598+
CV_Assert(edge_image.type() == CV_32FC1);
599+
CV_Assert(orientation_image.type() == CV_32FC1);
600+
601+
cv::Mat E = edge_image.getMat();
602+
cv::Mat O = orientation_image.getMat();
603+
cv::Mat E_t = E.t();
604+
cv::Mat O_t = O.t();
605+
606+
cv::Mat dst = _dst.getMat();
607+
dst.create(E.cols, E.rows, E.type());
608+
dst.setTo(0);
609+
610+
cv::Range sizeRange = cv::Range(0, E_t.rows);
611+
NmsInvoker body = NmsInvoker(E_t, O_t, dst, r, m);
612+
if (isParallel)
613+
{
614+
cv::parallel_for_(sizeRange, body);
615+
} else
616+
{
617+
body(sizeRange);
618+
}
619+
620+
s = s > E_t.rows / 2 ? E_t.rows / 2 : s;
621+
s = s > E_t.cols / 2 ? E_t.cols / 2 : s;
622+
for (int x=0; x<s; x++)
623+
{
624+
for (int y=0; y<E_t.cols; y++)
625+
{
626+
dst.at<float>(x, y) *= x / (float)s;
627+
dst.at<float>(E_t.rows-1-x, y) *= x / (float)s;
628+
}
629+
}
630+
631+
for (int x=0; x < E_t.rows; x++)
632+
{
633+
for (int y=0; y < s; y++)
634+
{
635+
dst.at<float>(x, y) *= y / (float)s;
636+
dst.at<float>(x, E_t.cols-1-y) *= y / (float)s;
637+
}
638+
}
639+
transpose(dst, dst);
640+
dst.copyTo(_dst);
641+
}
642+
643+
472644
protected:
473645
/*!
474646
* Private method used by process method. The function

0 commit comments

Comments
 (0)