Skip to content

Commit 2f8c629

Browse files
committed
Merge pull request opencv#19194 from alalek:intelligent_scissors
2 parents a6f7dca + 3eea3dd commit 2f8c629

File tree

14 files changed

+1637
-7
lines changed

14 files changed

+1637
-7
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Intelligent Scissors Example</title>
6+
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
7+
</head>
8+
<body>
9+
<h2>Intelligent Scissors Example</h2>
10+
<p>
11+
Click <b>Start</b> button to launch the code below.<br>
12+
Then click on image to pick source point. After that you can hover mouse pointer over canvas to specify target point candidate.<br>
13+
You can change the code in the &lt;textarea&gt; to investigate more. You can choose another image (need to "Stop" first).
14+
</p>
15+
<div>
16+
<div class="control"><button id="tryIt" disabled>Start</button> <button id="stopIt" disabled>Stop</button></div>
17+
<textarea class="code" rows="20" cols="100" id="codeEditor" spellcheck="false">
18+
</textarea>
19+
<p class="err" id="errorMessage"></p>
20+
</div>
21+
<div id="inputParams">
22+
<div class="caption">canvasInput <input type="file" id="fileInput" name="file" accept="image/*" /></div>
23+
<canvas id="canvasInput"></canvas>
24+
</div>
25+
<div id="result" style="display:none">
26+
<canvas id="canvasOutput"></canvas>
27+
</div>
28+
29+
<script src="utils.js" type="text/javascript"></script>
30+
31+
<script id="codeSnippet" type="text/code-snippet">
32+
let src = cv.imread('canvasInput');
33+
//cv.resize(src, src, new cv.Size(1024, 1024));
34+
cv.imshow('canvasOutput', src);
35+
36+
let tool = new cv.segmentation_IntelligentScissorsMB();
37+
tool.setEdgeFeatureCannyParameters(32, 100);
38+
tool.setGradientMagnitudeMaxLimit(200);
39+
tool.applyImage(src);
40+
41+
let hasMap = false;
42+
43+
let canvas = document.getElementById('canvasOutput');
44+
canvas.addEventListener('click', e => {
45+
let startX = e.offsetX, startY = e.offsetY; console.log(startX, startY);
46+
if (startX < src.cols && startY < src.rows)
47+
{
48+
console.time('buildMap');
49+
tool.buildMap(new cv.Point(startX, startY));
50+
console.timeEnd('buildMap');
51+
hasMap = true;
52+
}
53+
});
54+
canvas.addEventListener('mousemove', e => {
55+
let x = e.offsetX, y = e.offsetY; //console.log(x, y);
56+
let dst = src.clone();
57+
if (hasMap && x >= 0 && x < src.cols && y >= 0 && y < src.rows)
58+
{
59+
let contour = new cv.Mat();
60+
tool.getContour(new cv.Point(x, y), contour);
61+
let contours = new cv.MatVector();
62+
contours.push_back(contour);
63+
let color = new cv.Scalar(0, 255, 0, 255); // RGBA
64+
cv.polylines(dst, contours, false, color, 1, cv.LINE_8);
65+
contours.delete(); contour.delete();
66+
}
67+
cv.imshow('canvasOutput', dst);
68+
dst.delete();
69+
});
70+
canvas.addEventListener('dispose', e => {
71+
src.delete();
72+
tool.delete();
73+
});
74+
</script>
75+
76+
<script type="text/javascript">
77+
let utils = new Utils('errorMessage');
78+
79+
utils.loadCode('codeSnippet', 'codeEditor');
80+
utils.loadImageToCanvas('lena.jpg', 'canvasInput');
81+
utils.addFileInputHandler('fileInput', 'canvasInput');
82+
83+
let disposeEvent = new Event('dispose');
84+
85+
let tryIt = document.getElementById('tryIt');
86+
let stopIt = document.getElementById('stopIt');
87+
88+
tryIt.addEventListener('click', () => {
89+
let e_input = document.getElementById('inputParams');
90+
e_input.style.display = 'none';
91+
92+
let e_result = document.getElementById("result")
93+
e_result.style.display = '';
94+
95+
var e = document.getElementById("canvasOutput");
96+
var e_new = e.cloneNode(true);
97+
e.parentNode.replaceChild(e_new, e); // reset event handlers
98+
99+
stopIt.removeAttribute('disabled');
100+
tryIt.setAttribute('disabled', '');
101+
102+
utils.executeCode('codeEditor');
103+
});
104+
105+
stopIt.addEventListener('click', () => {
106+
let e_input = document.getElementById('inputParams');
107+
e_input.style.display = '';
108+
109+
let e_result = document.getElementById("result")
110+
e_result.style.display = 'none';
111+
112+
var e = document.getElementById("canvasOutput");
113+
e.dispatchEvent(disposeEvent);
114+
115+
var e_new = e.cloneNode(true);
116+
e.parentNode.replaceChild(e_new, e); // reset event handlers
117+
118+
tryIt.removeAttribute('disabled');
119+
stopIt.setAttribute('disabled', '');
120+
});
121+
122+
utils.loadOpenCv(() => {
123+
tryIt.removeAttribute('disabled');
124+
});
125+
</script>
126+
</body>
127+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Intelligent Scissors Demo {#tutorial_js_intelligent_scissors}
2+
=========================
3+
4+
Goal
5+
----
6+
7+
- Here you can check how to use IntelligentScissors tool for image segmentation task.
8+
- Available methods and parameters: @ref cv::segmentation::IntelligentScissorsMB
9+
10+
\htmlonly
11+
<iframe src="../../js_intelligent_scissors.html" width="100%"
12+
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
13+
</iframe>
14+
\endhtmlonly

doc/js_tutorials/js_imgproc/js_table_of_contents_imgproc.markdown

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,7 @@ Image Processing {#tutorial_js_table_of_contents_imgproc}
7777
- @subpage tutorial_js_imgproc_camera
7878

7979
Learn image processing for video capture.
80+
81+
- @subpage tutorial_js_intelligent_scissors
82+
83+
Learn how to use IntelligentScissors tool for image segmentation task.

doc/opencv.bib

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,13 @@ @incollection{Mitzel09
768768
pages = {432--441},
769769
publisher = {Springer}
770770
}
771+
@INPROCEEDINGS{Mortensen95intelligentscissors,
772+
author = {Eric N. Mortensen and William A. Barrett},
773+
title = {Intelligent Scissors for Image Composition},
774+
booktitle = {In Computer Graphics, SIGGRAPH Proceedings},
775+
year = {1995},
776+
pages = {191--198}
777+
}
771778
@inproceedings{Muja2009,
772779
author = {Muja, Marius and Lowe, David G},
773780
title = {Fast Approximate Nearest Neighbors with Automatic Algorithm Configuration},

modules/imgproc/include/opencv2/imgproc.hpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ location of points on the plane, building special graphs (such as NNG,RNG), and
185185
@defgroup imgproc_motion Motion Analysis and Object Tracking
186186
@defgroup imgproc_feature Feature Detection
187187
@defgroup imgproc_object Object Detection
188+
@defgroup imgproc_segmentation Image Segmentation
188189
@defgroup imgproc_c C API
189190
@defgroup imgproc_hal Hardware Acceleration Layer
190191
@{
@@ -3227,6 +3228,9 @@ CV_EXPORTS_AS(EMD) float wrapperEMD( InputArray signature1, InputArray signature
32273228

32283229
//! @} imgproc_hist
32293230

3231+
//! @addtogroup imgproc_segmentation
3232+
//! @{
3233+
32303234
/** @example samples/cpp/watershed.cpp
32313235
An example using the watershed algorithm
32323236
*/
@@ -3254,11 +3258,11 @@ function.
32543258
size as image .
32553259
32563260
@sa findContours
3257-
3258-
@ingroup imgproc_misc
32593261
*/
32603262
CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers );
32613263

3264+
//! @} imgproc_segmentation
3265+
32623266
//! @addtogroup imgproc_filter
32633267
//! @{
32643268

@@ -3304,7 +3308,7 @@ CV_EXPORTS_W void pyrMeanShiftFiltering( InputArray src, OutputArray dst,
33043308

33053309
//! @}
33063310

3307-
//! @addtogroup imgproc_misc
3311+
//! @addtogroup imgproc_segmentation
33083312
//! @{
33093313

33103314
/** @example samples/cpp/grabcut.cpp
@@ -3334,6 +3338,11 @@ CV_EXPORTS_W void grabCut( InputArray img, InputOutputArray mask, Rect rect,
33343338
InputOutputArray bgdModel, InputOutputArray fgdModel,
33353339
int iterCount, int mode = GC_EVAL );
33363340

3341+
//! @} imgproc_segmentation
3342+
3343+
//! @addtogroup imgproc_misc
3344+
//! @{
3345+
33373346
/** @example samples/cpp/distrans.cpp
33383347
An example on using the distance transform
33393348
*/
@@ -4876,4 +4885,8 @@ Point LineIterator::pos() const
48764885

48774886
} // cv
48784887

4888+
4889+
#include "./imgproc/segmentation.hpp"
4890+
4891+
48794892
#endif
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef OPENCV_IMGPROC_SEGMENTATION_HPP
6+
#define OPENCV_IMGPROC_SEGMENTATION_HPP
7+
8+
#include "opencv2/imgproc.hpp"
9+
10+
namespace cv {
11+
12+
namespace segmentation {
13+
14+
//! @addtogroup imgproc_segmentation
15+
//! @{
16+
17+
18+
/** @brief Intelligent Scissors image segmentation
19+
*
20+
* This class is used to find the path (contour) between two points
21+
* which can be used for image segmentation.
22+
*
23+
* Usage example:
24+
* @snippet snippets/imgproc_segmentation.cpp usage_example_intelligent_scissors
25+
*
26+
* Reference: <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.138.3811&rep=rep1&type=pdf">"Intelligent Scissors for Image Composition"</a>
27+
* algorithm designed by Eric N. Mortensen and William A. Barrett, Brigham Young University
28+
* @cite Mortensen95intelligentscissors
29+
*/
30+
class CV_EXPORTS_W_SIMPLE IntelligentScissorsMB
31+
{
32+
public:
33+
CV_WRAP
34+
IntelligentScissorsMB();
35+
36+
/** @brief Specify weights of feature functions
37+
*
38+
* Consider keeping weights normalized (sum of weights equals to 1.0)
39+
* Discrete dynamic programming (DP) goal is minimization of costs between pixels.
40+
*
41+
* @param weight_non_edge Specify cost of non-edge pixels (default: 0.43f)
42+
* @param weight_gradient_direction Specify cost of gradient direction function (default: 0.43f)
43+
* @param weight_gradient_magnitude Specify cost of gradient magnitude function (default: 0.14f)
44+
*/
45+
CV_WRAP
46+
IntelligentScissorsMB& setWeights(float weight_non_edge, float weight_gradient_direction, float weight_gradient_magnitude);
47+
48+
/** @brief Specify gradient magnitude max value threshold
49+
*
50+
* Zero limit value is used to disable gradient magnitude thresholding (default behavior, as described in original article).
51+
* Otherwize pixels with `gradient magnitude >= threshold` have zero cost.
52+
*
53+
* @note Thresholding should be used for images with irregular regions (to avoid stuck on parameters from high-contract areas, like embedded logos).
54+
*
55+
* @param gradient_magnitude_threshold_max Specify gradient magnitude max value threshold (default: 0, disabled)
56+
*/
57+
CV_WRAP
58+
IntelligentScissorsMB& setGradientMagnitudeMaxLimit(float gradient_magnitude_threshold_max = 0.0f);
59+
60+
/** @brief Switch to "Laplacian Zero-Crossing" edge feature extractor and specify its parameters
61+
*
62+
* This feature extractor is used by default according to article.
63+
*
64+
* Implementation has additional filtering for regions with low-amplitude noise.
65+
* This filtering is enabled through parameter of minimal gradient amplitude (use some small value 4, 8, 16).
66+
*
67+
* @note Current implementation of this feature extractor is based on processing of grayscale images (color image is converted to grayscale image first).
68+
*
69+
* @note Canny edge detector is a bit slower, but provides better results (especially on color images): use setEdgeFeatureCannyParameters().
70+
*
71+
* @param gradient_magnitude_min_value Minimal gradient magnitude value for edge pixels (default: 0, check is disabled)
72+
*/
73+
CV_WRAP
74+
IntelligentScissorsMB& setEdgeFeatureZeroCrossingParameters(float gradient_magnitude_min_value = 0.0f);
75+
76+
/** @brief Switch edge feature extractor to use Canny edge detector
77+
*
78+
* @note "Laplacian Zero-Crossing" feature extractor is used by default (following to original article)
79+
*
80+
* @sa Canny
81+
*/
82+
CV_WRAP
83+
IntelligentScissorsMB& setEdgeFeatureCannyParameters(
84+
double threshold1, double threshold2,
85+
int apertureSize = 3, bool L2gradient = false
86+
);
87+
88+
/** @brief Specify input image and extract image features
89+
*
90+
* @param image input image. Type is #CV_8UC1 / #CV_8UC3
91+
*/
92+
CV_WRAP
93+
IntelligentScissorsMB& applyImage(InputArray image);
94+
95+
/** @brief Specify custom features of imput image
96+
*
97+
* Customized advanced variant of applyImage() call.
98+
*
99+
* @param non_edge Specify cost of non-edge pixels. Type is CV_8UC1. Expected values are `{0, 1}`.
100+
* @param gradient_direction Specify gradient direction feature. Type is CV_32FC2. Values are expected to be normalized: `x^2 + y^2 == 1`
101+
* @param gradient_magnitude Specify cost of gradient magnitude function: Type is CV_32FC1. Values should be in range `[0, 1]`.
102+
* @param image **Optional parameter**. Must be specified if subset of features is specified (non-specified features are calculated internally)
103+
*/
104+
CV_WRAP
105+
IntelligentScissorsMB& applyImageFeatures(
106+
InputArray non_edge, InputArray gradient_direction, InputArray gradient_magnitude,
107+
InputArray image = noArray()
108+
);
109+
110+
/** @brief Prepares a map of optimal paths for the given source point on the image
111+
*
112+
* @note applyImage() / applyImageFeatures() must be called before this call
113+
*
114+
* @param sourcePt The source point used to find the paths
115+
*/
116+
CV_WRAP void buildMap(const Point& sourcePt);
117+
118+
/** @brief Extracts optimal contour for the given target point on the image
119+
*
120+
* @note buildMap() must be called before this call
121+
*
122+
* @param targetPt The target point
123+
* @param[out] contour The list of pixels which contains optimal path between the source and the target points of the image. Type is CV_32SC2 (compatible with `std::vector<Point>`)
124+
* @param backward Flag to indicate reverse order of retrived pixels (use "true" value to fetch points from the target to the source point)
125+
*/
126+
CV_WRAP void getContour(const Point& targetPt, OutputArray contour, bool backward = false) const;
127+
128+
#ifndef CV_DOXYGEN
129+
struct Impl;
130+
inline Impl* getImpl() const { return impl.get(); }
131+
protected:
132+
std::shared_ptr<Impl> impl;
133+
#endif
134+
};
135+
136+
//! @}
137+
138+
} // namespace segmentation
139+
} // namespace cv
140+
141+
#endif // OPENCV_IMGPROC_SEGMENTATION_HPP

0 commit comments

Comments
 (0)