Skip to content

Commit

Permalink
Added EDColor support to EdgeDrawing class.
Browse files Browse the repository at this point in the history
Fixed a bug where params.PFmode = true caused incorrect edge detection results.
Updated edge_drawing.py to support the new EDColor feature.
Created a new edge_drawing_demo.cpp for demonstrating the EDColor feature.
Updated ximgproc.bib to include references for EDColor.
Added tests for PFmode and EDColor in test_fld.cpp.
Added CV_WRAP to Params::read and Params::write functions to expose them to Python bindings.
  • Loading branch information
sturkmen72 committed Sep 18, 2024
1 parent 438e67f commit 447ab56
Show file tree
Hide file tree
Showing 8 changed files with 935 additions and 223 deletions.
9 changes: 9 additions & 0 deletions modules/ximgproc/doc/ximgproc.bib
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,15 @@ @article{akinlar2013edcircles
publisher={Elsevier}
}

@article{akinlar201782,
title = {ColorED: Color edge and segment detection by Edge Drawing (ED)},
author = {Cuneyt Akinlar and Cihan Topal},
journal = {Journal of Visual Communication and Image Representation},
volume = {44},
pages = {82-94},
year = {2017},
publisher={Academic Press}

@article{loke2021accelerated,
title={Accelerated superpixel image segmentation with a parallelized DBSCAN algorithm},
author={Loke, Seng Cheong and MacDonald, Bruce A and Parsons, Matthew and W{\"u}nsche, Burkhard Claus},
Expand Down
48 changes: 36 additions & 12 deletions modules/ximgproc/include/opencv2/ximgproc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,42 @@
@defgroup ximgproc_fast_line_detector Fast line detector
@defgroup ximgproc_edge_drawing EdgeDrawing
EDGE DRAWING LIBRARY FOR GEOMETRIC FEATURE EXTRACTION AND VALIDATION
Edge Drawing (ED) algorithm is an proactive approach on edge detection problem. In contrast to many other existing edge detection algorithms which follow a subtractive
approach (i.e. after applying gradient filters onto an image eliminating pixels w.r.t. several rules, e.g. non-maximal suppression and hysteresis in Canny), ED algorithm
works via an additive strategy, i.e. it picks edge pixels one by one, hence the name Edge Drawing. Then we process those random shaped edge segments to extract higher level
edge features, i.e. lines, circles, ellipses, etc. The popular method of extraction edge pixels from the thresholded gradient magnitudes is non-maximal supression that tests
every pixel whether it has the maximum gradient response along its gradient direction and eliminates if it does not. However, this method does not check status of the
neighboring pixels, and therefore might result low quality (in terms of edge continuity, smoothness, thinness, localization) edge segments. Instead of non-maximal supression,
ED points a set of edge pixels and join them by maximizing the total gradient response of edge segments. Therefore it can extract high quality edge segments without need for
an additional hysteresis step.
@defgroup ximgproc_edge_drawing Edge Drawing
Edge Drawing (ED) algorithm for geometric feature extraction and validation.
The Edge Drawing (ED) algorithm is a proactive approach to the edge detection problem.
In contrast to many existing edge detection algorithms, which follow a subtractive
approach (i.e., applying gradient filters and eliminating pixels based on several rules,
such as non-maximal suppression and hysteresis in the Canny Edge Detector), the ED algorithm
operates via an additive strategy. It selects edge pixels one by one and connects them,
hence the name Edge Drawing.
ED offers several key advantages:
1. **Additive Strategy**: Instead of eliminating non-edge pixels after gradient filtering,
ED incrementally builds up edge segments by selecting and connecting pixels based on
their gradient response. This differs from traditional methods, which rely on
non-maximal suppression and hysteresis to filter out non-edge pixels.
2. **Edge Pixel Selection**: ED selects edge pixels by analyzing their local gradient
response, while also considering neighboring pixels. This results in smoother and
more continuous edge segments, as ED aims to maximize the overall gradient strength
along the edge segment.
3. **Edge Segment Formation**: Traditional methods, such as non-maximal suppression,
check whether a pixel has the maximum gradient response along its gradient direction,
eliminating it otherwise. However, this approach doesn't consider neighboring pixels,
often resulting in lower-quality edge segments. ED, on the other hand, joins a set of
edge pixels together by maximizing the total gradient response of the segment, leading
to high-quality, well-localized edges.
4. **Higher-Level Feature Extraction**: After forming edge segments, ED enables the
extraction of higher-level geometric features such as lines, circles, ellipses, and
other shapes, making it useful for tasks involving geometric feature extraction and validation.
The ED algorithm produces continuous, smooth, and localized edge segments, making it ideal
for applications requiring precise edge detection and geometric shape analysis.
@defgroup ximgproc_fourier Fourier descriptors
Expand Down
10 changes: 5 additions & 5 deletions modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace ximgproc
//! @addtogroup ximgproc_edge_drawing
//! @{

/** @brief Class implementing the ED (EdgeDrawing) @cite topal2012edge, EDLines @cite akinlar2011edlines, EDPF @cite akinlar2012edpf and EDCircles @cite akinlar2013edcircles algorithms
/** @brief Class implementing the ED (EdgeDrawing) @cite topal2012edge, EDLines @cite akinlar2011edlines, EDPF @cite akinlar2012edpf, EDCircles @cite akinlar2013edcircles and ColorED @cite akinlar201782 algorithms.
*/

class CV_EXPORTS_W EdgeDrawing : public Algorithm
Expand Down Expand Up @@ -66,13 +66,13 @@ class CV_EXPORTS_W EdgeDrawing : public Algorithm
//! Default value is 1.3
CV_PROP_RW double MaxErrorThreshold;

void read(const FileNode& fn);
void write(FileStorage& fs) const;
CV_WRAP void read(const FileNode& fn);
CV_WRAP void write(FileStorage& fs) const;
};

/** @brief Detects edges in a grayscale image and prepares them to detect lines and ellipses.
/** @brief Detects edges in a grayscale or color image and prepares them to detect lines and ellipses.
@param src 8-bit, single-channel, grayscale input image.
@param src 8-bit, single-channel (CV_8UC1) or color (CV_8UC3, CV_8UC4) input image.
*/
CV_WRAP virtual void detectEdges(InputArray src) = 0;

Expand Down
149 changes: 106 additions & 43 deletions modules/ximgproc/samples/edge_drawing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
#!/usr/bin/python

'''
This example illustrates how to use cv.ximgproc.EdgeDrawing class.
This example script illustrates how to use cv.ximgproc.EdgeDrawing class.
It uses the OpenCV library to load an image, and then use the EdgeDrawing class
to detect edges, lines, and ellipses. The detected features are then drawn and displayed.
The main loop allows the user changing parameters of EdgeDrawing by pressing following keys:
to toggle the grayscale conversion press 'space' key
to increase MinPathLength value press '/' key
to decrease MinPathLength value press '*' key
to increase MinLineLength value press '+' key
to decrease MinLineLength value press '-' key
to toggle NFAValidation value press 'n' key
to toggle PFmode value press 'p' key
to save parameters to file press 's' key
to load parameters from file press 'l' key
The program exits when the Esc key is pressed.
Usage:
ed.py [<image_name>]
Expand All @@ -16,71 +33,117 @@
import random as rng
import sys

rng.seed(12345)

def main():
try:
fn = sys.argv[1]
except IndexError:
fn = 'board.jpg'

src = cv.imread(cv.samples.findFile(fn))
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
cv.imshow("source", src)

ssrc = src.copy()*0
def EdgeDrawingDemo(src, ed, EDParams, convert_to_gray):
rng.seed(12345)
ssrc = np.zeros_like(src)
lsrc = src.copy()
esrc = src.copy()

ed = cv.ximgproc.createEdgeDrawing()
img_to_detect = cv.cvtColor(src, cv.COLOR_BGR2GRAY) if convert_to_gray else src

# you can change parameters (refer the documentation to see all parameters)
EDParams = cv.ximgproc_EdgeDrawing_Params()
EDParams.MinPathLength = 50 # try changing this value between 5 to 1000
EDParams.PFmode = False # defaut value try to swich it to True
EDParams.MinLineLength = 10 # try changing this value between 5 to 100
EDParams.NFAValidation = True # defaut value try to swich it to False
cv.imshow("source image", img_to_detect)

ed.setParams(EDParams)
print("")
print("convert_to_gray:", convert_to_gray)
print("MinPathLength:", EDParams.MinPathLength)
print("MinLineLength:", EDParams.MinLineLength)
print("PFmode:", EDParams.PFmode)
print("NFAValidation:", EDParams.NFAValidation)

tm = cv.TickMeter()
tm.start()

# Detect edges
# you should call this before detectLines() and detectEllipses()
ed.detectEdges(gray)
ed.detectEdges(img_to_detect)

segments = ed.getSegments()
lines = ed.detectLines()
ellipses = ed.detectEllipses()

#Draw detected edge segments
for i in range(len(segments)):
color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
cv.polylines(ssrc, [segments[i]], False, color, 1, cv.LINE_8)
tm.stop()

print("Detection time : {:.2f} ms. using the parameters above".format(tm.getTimeMilli()))

# Draw detected edge segments
for segment in segments:
color = (rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256))
cv.polylines(ssrc, [segment], False, color, 1, cv.LINE_8)

cv.imshow("detected edge segments", ssrc)

#Draw detected lines
if lines is not None: # Check if the lines have been found and only then iterate over these and add them to the image
# Draw detected lines
if lines is not None: # Check if the lines have been found and only then iterate over these and add them to the image
lines = np.uint16(np.around(lines))
for i in range(len(lines)):
cv.line(lsrc, (lines[i][0][0], lines[i][0][1]), (lines[i][0][2], lines[i][0][3]), (0, 0, 255), 1, cv.LINE_AA)
for line in lines:
cv.line(lsrc, (line[0][0], line[0][1]), (line[0][2], line[0][3]), (0, 0, 255), 1, cv.LINE_AA)

cv.imshow("detected lines", lsrc)

#Draw detected circles and ellipses
if ellipses is not None: # Check if circles and ellipses have been found and only then iterate over these and add them to the image
for i in range(len(ellipses)):
center = (int(ellipses[i][0][0]), int(ellipses[i][0][1]))
axes = (int(ellipses[i][0][2])+int(ellipses[i][0][3]),int(ellipses[i][0][2])+int(ellipses[i][0][4]))
angle = ellipses[i][0][5]
color = (0, 0, 255)
if ellipses[i][0][2] == 0:
color = (0, 255, 0)
cv.ellipse(esrc, center, axes, angle,0, 360, color, 2, cv.LINE_AA)
# Draw detected circles and ellipses
if ellipses is not None: # Check if circles and ellipses have been found and only then iterate over these and add them to the image
for ellipse in ellipses:
center = (int(ellipse[0][0]), int(ellipse[0][1]))
axes = (int(ellipse[0][2] + ellipse[0][3]), int(ellipse[0][2] + ellipse[0][4]))
angle = ellipse[0][5]

color = (0, 255, 0) if ellipse[0][2] == 0 else (0, 0, 255)

cv.ellipse(esrc, center, axes, angle, 0, 360, color, 2, cv.LINE_AA)

cv.imshow("detected circles and ellipses", esrc)
cv.waitKey(0)
print('Done')

def main():
try:
fn = sys.argv[1]
except IndexError:
fn = 'board.jpg'
src = cv.imread(cv.samples.findFile(fn))
if src is None:
print("Error loading image")
return

ed = cv.ximgproc.createEdgeDrawing()

# Set parameters (refer to the documentation for all parameters)
EDParams = cv.ximgproc_EdgeDrawing_Params()
EDParams.MinPathLength = 10 # try changing this value by pressing '/' and '*' keys
EDParams.MinLineLength = 10 # try changing this value by pressing '+' and '-' keys
EDParams.PFmode = False # default value is False, try switching by pressing 'p' key
EDParams.NFAValidation = True # default value is True, try switching by pressing 'n' key

convert_to_gray = True
key = 0

while key != 27:
ed.setParams(EDParams)
EdgeDrawingDemo(src, ed, EDParams, convert_to_gray)
key = cv.waitKey()
if key == 32: # space key
convert_to_gray = not convert_to_gray
if key == 112: # 'p' key
EDParams.PFmode = not EDParams.PFmode
if key == 110: # 'n' key
EDParams.NFAValidation = not EDParams.NFAValidation
if key == 43: # '+' key
EDParams.MinLineLength = EDParams.MinLineLength + 5
if key == 45: # '-' key
EDParams.MinLineLength = max(0, EDParams.MinLineLength - 5)
if key == 47: # '/' key
EDParams.MinPathLength = EDParams.MinPathLength + 20
if key == 42: # '*' key
EDParams.MinPathLength = max(0, EDParams.MinPathLength - 20)
if key == 115: # 's' key
fs = cv.FileStorage("ed-params.xml",cv.FileStorage_WRITE)
EDParams.write(fs)
fs.release()
print("parameters saved to ed-params.xml")
if key == 108: # 'l' key
fs = cv.FileStorage("ed-params.xml",cv.FileStorage_READ)
if fs.isOpened():
EDParams.read(fs.root())
fs.release()
print("parameters loaded from ed-params.xml")

if __name__ == '__main__':
print(__doc__)
Expand Down
Loading

0 comments on commit 447ab56

Please sign in to comment.