guide
This article mainly introduces how to use the EdgeDrawing module to find circles in OpenCV (detailed steps + code).
background introduction
Starting from OpenCV4.5.2, the open source library ED_Lib is encapsulated in the Contrib module to find straight lines, line segments, ellipses and circles in images. Github address:
https://github.com/CihanTopal/ED_Lib
Introduction to algorithm principle:
The Edge Drawing (ED) algorithm is an active approach to the edge detection problem. In contrast to many other existing edge detection algorithms that follow a subtractive approach (i.e., after applying a gradient filter on the image, pixels are eliminated according to multiple rules, such as non-maximum suppression and hysteresis in Canny), the ED algorithm adopts an additive strategy The work is to select the edge pixels one by one, so it is called "edge drawing". We then process these randomly shaped edge segments to extract higher level edge features, i.e. lines, circles, ellipses, etc. A popular method for extracting edge pixels from thresholded gradient magnitudes is non-maximum suppression, which tests whether each pixel has a maximum gradient response along its gradient direction, and eliminates it if not. However, this method does not check the state of adjacent pixels, so may result in low-quality (in terms of edge continuity, smoothness, thinness, positioning) edge fragments. Instead of non-maximum suppression, ED targets a set of edge pixels and connects them by maximizing the total gradient response of edge segments. Therefore, it can extract high-quality edge fragments without additional lag steps.
Introductory documents used in OpenCV:
https://docs.opencv.org/4.5.2/d1/d1c/classcv_1_1ximgproc_1_1EdgeDrawing.html
Steps for usage
The EdgeDrawing class is in Contrib's ximgproc module, and it needs to meet the following conditions to use it in C++:
① OpenCV >= 4.5.2
② CMake compiles the Contrib module
③ Include edge_drawing.hpp header file
Use in Python needs to install opencv-python-contrib >=4.5.2
[1] Demonstration in Python:
#公众号--计算机视觉之家
'''
This example illustrates how to use cv.ximgproc.EdgeDrawing class.
Usage:
ed.py [<image_name>]
image argument defaults to board.jpg
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
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
lsrc = src.copy()
esrc = src.copy()
ed = cv.ximgproc.createEdgeDrawing()
# 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 = 20 # try changing this value between 5 to 100
EDParams.NFAValidation = True # defaut value try to swich it to False
ed.setParams(EDParams)
# Detect edges
# you should call this before detectLines() and detectEllipses()
ed.detectEdges(gray)
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)
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
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)
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)
cv.imshow("detected circles and ellipses", esrc)
cv.waitKey(0)
print('Done')
if __name__ == '__main__':
print(__doc__)
main()
cv.destroyAllWindows()
Execution command: ed.py [<image_name>]
Example 1: edge_drawing.py 1.png
Example 2: edge_drawing.py 2.png
Example 3: edge_drawing.py 3.png
In the above figure, green indicates found ellipses, and red indicates found circles. Of course, EdgeDrawing can also obtain edge information and find straight lines, the effect is as follows:
[2] Demonstration in C++:
//公众号--计算机视觉之家
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ximgproc/edge_drawing.hpp>
using namespace std;
using namespace cv;
using namespace ximgproc;
int main()
{
Mat src = imread("./imgs/11.bmp");
if (src.empty())
{
cout << "src image is empty, check again!" << endl;
return -1;
}
//resize(src, src, Size(), 0.2, 0.2);
imshow("src", src);
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
double start = static_cast<double>(getTickCount()); //计时开始
Ptr<EdgeDrawing> ed = createEdgeDrawing();
ed->params.EdgeDetectionOperator = EdgeDrawing::PREWITT;
ed->params.MinPathLength = 50; // try changing this value between 5 to 1000
ed->params.PFmode = false; //defaut value try to swich it to true
ed->params.MinLineLength = 10; // try changing this value between 5 to 100
ed->params.NFAValidation = false; // defaut value try to swich it to false
ed->params.GradientThresholdValue = 20;
Example 1:
Example 2:
Example 3:
brief summary
Generally speaking, the method of finding circles and straight lines provided by EdgeDrawing is easy to use and has good effect. In simple cases, the default parameters can be used. For parameter adjustment, you can refer to the documentation and try it yourself. Here are a few commonly used ones for a brief explanation.
Ptr<EdgeDrawing> ed = createEdgeDrawing();
ed->params.EdgeDetectionOperator = EdgeDrawing::LSD;
ed->params.MinPathLength = 50; // try changing this value between 5 to 1000
ed->params.PFmode = false; //defaut value try to swich it to true
ed->params.MinLineLength = 10; // try changing this value between 5 to 100
ed->params.NFAValidation = true; // defaut value try to swich it to false
ed->params.GradientThresholdValue = 20;
[1] The gradient operator used by the algorithm can be selected from 4 types, and the default is PREWITT. You can set different gradient operators to try out the effect.
[2] GradientThresholdValue, the smaller the value, the more circles with low contrast can be found. For example, the following are the effects of gradient thresholds of 100 and 50 respectively:
【3】NFAValidation: The default value is true. Indicates whether to use the NFA (number of false alarms) algorithm for line and ellipse validation. When set to false, more circles or lines can be found.
【4】MinPathLength: Minimum connection pixel length processing to create edge segments. In gradient images, the minimum length of connected pixels processed to create edge segments. Pixels with a value higher than GradientThresholdValue will be processed, the default is 10. For example, the following are the effects of gradient thresholds of 50 and 10 respectively (the smaller the value, the smaller the circle is found):