视觉处理算法基础

convolution

本文研究 卷积算法,形态学操作,直方图,区域生长,分水岭算法

卷积操作

卷积滤波原理

  • 卷积核/掩膜:一个m*n大小的加权矩阵
  • 卷积滤波:先将掩膜旋转180度,再依次滑过图像上每个像素,相应滑动区域与掩膜对应元素乘积之和作为像素的新值

\[ (f*g)(x,y)=\sum_i\sum_jf(x-i,y-j)g(i,j) \]

低通滤波(去噪)

压制、弱化或消除图像中的细节、突变、边缘和噪声,就是图像平滑化。

图像平滑是对图像作低通滤波,可在空间域或频率域实现。

空域图像平滑方法主要用低通卷积滤波、中值滤波等;

频域图像平滑常用的低通滤波器有低通梯形滤波器、低通高斯滤波器、低通指数滤波器、巴特沃思低通滤波器等

均值滤波

20220520194347

  • 优点:使用简单,计算方便
  • 特点:核中区域贡献率相同
  • 函数: cv2.blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DE)

中值滤波

20220520194414

  • 优点:对于椒盐噪声有效
  • 特点:中心点的像素值被核中的中位数的像素代替
  • void medianBlur( InputArray src, OutputArray dst, int ksize ); Python:dst = cv.medianBlur( src, ksize[, dst] )

高斯滤波

20220520194441

  • 优点:对于服从正太分布的噪声非常有效
  • 特点:核中区域贡献率与距离中心成正比,权重与高斯分布相关
  • void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX,double sigmaY = 0, int borderType = BORDER_DEFAULT ); Python: dst = cv.GaussianBlur( src, ksize, sigmaX[, dst[, sigmaY[, borderType]]] )

高通滤波(边缘检测)

20220520194506

  • 图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,即从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。
  • 图象的边缘部分集中了图象的大部分信息,图象边缘的确定与提取对于整个图象场景的识别与理解是非常重要的,同时也是图象分割所依赖的重要特征,边缘检测主要是图象的灰度变化的度量、检测和定位。
  • 边缘检测的基本原理: 边缘检测的本质是微分,当相邻两个像素点的灰度值差异越大时,也就是其斜率越陡,也就是微分值越大,进而通过这个来判断边缘,实际中常用差分,x方向和y方向。

Robert算子

20220520194600

Sobel算子

20220520194709

  • 原理:使用两个3X3的矩阵去和原始图片作卷积

  • 分别得到横向G(x)和纵向G(y)的梯度值 20220520194732

  • 如果梯度值大于某一个阈值,则认为该点为边缘点

  • void Sobel( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1,double delta=0, int borderType=BORDER_DEFAULT );

  • Python:dst = cv.Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] )

Laplace算子

20220520194838

20220520194905

void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, doubledelta = 0, int borderType = BORDER_DEFAULT )
Python:dst = cv.Laplacian( src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]] )

LoG算子

  • 原理:首先对图像做高斯滤波,然后再求其拉普拉斯(Laplacian)二阶导数。
  • 即图像与 Laplacian of the Gaussian function 进行滤波运算。
  • 最后,通过检测滤波结果的零交叉(Zero crossings)可以获得图像或物体的边缘。
  • 因而,也被业界简称为Laplacian-of-Gaussian (LoG)算子

Canny算子,核心优点:边缘可自动连通

  1. 对原始图像进行灰度化。Canny算法通常处理的图像为灰度图,因此如果摄像机获取的是彩色图像,那首先就得进行灰度化。对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。
  2. 对图像进行高斯滤波。图像高斯滤波的实现可以用两个一维高斯核分别两次加权实现,也可以通过一个二维高斯核一次卷积实现。
  3. 用一阶偏导的有限差分来计算梯度的幅值方向。关于图像灰度值得梯度可使用一阶有限差分来进行近似,这样就可以得图像在x和y方向上偏导数的两个矩阵。
  4. 对梯度幅值进行非极大值抑制。图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这仅仅是属于图像增强的过程)。
  5. Canny算法中减少假边缘数量的方法是采用双阈值法。选择两个阈值,根据高阈值得到一个边缘图像,这样一个图像含有很少的假边缘,但是由于阈值较高,产生的图像边缘可能不闭合,为解决这样一个问题采用了另外一个低阈值。在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时,该算法会在断点的8邻域点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。
  • void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false );
  • Python:dst = cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )

形态学操作

膨胀

20220520195022

腐蚀

20220520195037

开闭运算

  • 膨胀和腐蚀不为逆运算,二者级联使用可产生新的形态学运算

  • 开运算:先腐蚀后膨胀,可断开连接 \[ A\circ B=(A\ominus B)\oplus B \]

  • 闭运算:先膨胀后腐蚀,可连接狭缝

\[ A\bullet B=(A\oplus B)\ominus B \]

  • 先开后闭,可以有效去除噪声

dst = cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )

/** @brief Returns a structuring element of the specified size and shape for morphological operations.

The function constructs and returns the structuring element that can be further passed to cv::erode,
cv::dilate or cv::morphologyEx. But you can also construct an arbitrary binary mask yourself and use it as
the structuring element.

@param shape Element shape that could be one of cv::MorphShapes
@param ksize Size of the structuring element.
@param anchor Anchor position within the element. The default value \f$(-1, -1)\f$ means that the
anchor is at the center. Note that only the shape of a cross-shaped element depends on the anchor
position. In other cases the anchor just regulates how much the result of the morphological
operation is shifted.
*/
CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));

/** @brief Performs advanced morphological transformations.

The function morphologyEx can perform advanced morphological transformations using an erosion and dilation as
basic operations.

Any of the operations can be done in-place. In case of multi-channel images, each channel is
processed independently.

@param src Source image. The number of channels can be arbitrary. The depth should be one of
CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
@param dst Destination image of the same size and type as source image.
@param op Type of a morphological operation, see cv::MorphTypes
@param kernel Structuring element. It can be created using cv::getStructuringElement.
@param anchor Anchor position with the kernel. Negative values mean that the anchor is at the
kernel center.
@param iterations Number of times erosion and dilation are applied.
@param borderType Pixel extrapolation method, see cv::BorderTypes
@param borderValue Border value in case of a constant border. The default value has a special
meaning.
@sa dilate, erode, getStructuringElement
*/
CV_EXPORTS_W void morphologyEx( InputArray src, OutputArray dst,
int op, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );

基于阈值的图像分割

灰度直方图

  • 统计各个灰度级别在图像中的出现次数或概率,并用直方图显示出来
  • 具有图像平移、旋转、缩放不变性等众多优点
  • 直方图在进行图像计算处理时代价较小,所以经常用于图像处理

20220520195226

void calcHist( const Mat* arrays, intnarrays, const int* channels, InputArray mask, OutputArrayhist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false );
Python:dst = cv.calcHist( images, channels, mask, histSize, ranges[, hist[, accumulate]] )

阈值分割

  • 假设:图像中的目标区和背景区之间或者不同目标区之间,存在不同的灰度或平均灰度
  • 凡是灰度值包含于z的像素都变成某一灰度值,其他的变成另一个灰度值,则该图像就以z为界被分成两个区域
  • 如果=1和=0,分割后的图像为二值图像
  • 确定最佳阈值,使背景和目标之间的差异最大。可使用灰度直方图来确定最佳阈值

可对图像分块,进行局部自适应阈值分割,以提升分割效果

大津算法

遍历灰度取值,确定最佳阈值,使背景和目标之间的类间方差最大 (因为二者差异最大);

  • 记t为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;
  • 背景点数占图像比例为w1,平均灰度为u1。则图像的总平均灰度为:u=w0u0+w1u1。
  • 前景和背景图象的方差:g=w0(u0-u)(u0-u)+w1(u1-u)(u1-u)=w0w1(u0-u1)*(u0-u1),此公式为方差公式。
  • 可参照概率论课本上面的g的公式也就是下面程序中的sb的表达式。当方差g最大时,可以认为此时前景和背景差异最大,此时的灰度t是最佳阈值sb = w0w1(u1-u0)*(u0-u1)
enum AdaptiveThresholdTypes {
/** the threshold value \f$T(x,y)\f$ is a mean of the \f$\texttt{blockSize} \times
\texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$ minus C */
ADAPTIVE_THRESH_MEAN_C = 0,
/** the threshold value \f$T(x, y)\f$ is a weighted sum (cross-correlation with a Gaussian
window) of the \f$\texttt{blockSize} \times \texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$
minus C . The default sigma (standard deviation) is used for the specified blockSize . See
cv::getGaussianKernel*/
ADAPTIVE_THRESH_GAUSSIAN_C = 1
};

enum ThresholdTypes {
THRESH_BINARY = 0, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{maxval}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f]
THRESH_BINARY_INV = 1, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f]
THRESH_TRUNC = 2, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f]
THRESH_TOZERO = 3, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f]
THRESH_TOZERO_INV = 4, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f]
THRESH_MASK = 7,
THRESH_OTSU = 8, //!< flag, use Otsu algorithm to choose the optimal threshold value
THRESH_TRIANGLE = 16 //!< flag, use Triangle algorithm to choose the optimal threshold value
};

/** @brief Applies an adaptive threshold to an array.

The function transforms a grayscale image to a binary image according to the formulae:
- **THRESH_BINARY**
\f[dst(x,y) = \fork{\texttt{maxValue}}{if \(src(x,y) > T(x,y)\)}{0}{otherwise}\f]
- **THRESH_BINARY_INV**
\f[dst(x,y) = \fork{0}{if \(src(x,y) > T(x,y)\)}{\texttt{maxValue}}{otherwise}\f]
where \f$T(x,y)\f$ is a threshold calculated individually for each pixel (see adaptiveMethod parameter).

The function can process the image in-place.

@param src Source 8-bit single-channel image.
@param dst Destination image of the same size and the same type as src.
@param maxValue Non-zero value assigned to the pixels for which the condition is satisfied
@param adaptiveMethod Adaptive thresholding algorithm to use, see cv::AdaptiveThresholdTypes
@param thresholdType Thresholding type that must be either THRESH_BINARY or THRESH_BINARY_INV,
see cv::ThresholdTypes.
@param blockSize Size of a pixel neighborhood that is used to calculate a threshold value for the
pixel: 3, 5, 7, and so on.
@param C Constant subtracted from the mean or weighted mean (see the details below). Normally, it
is positive but may be zero or negative as well.

@sa threshold, blur, GaussianBlur
*/
CV_EXPORTS_W void adaptiveThreshold( InputArray src, OutputArray dst,
double maxValue, int adaptiveMethod,
int thresholdType, int blockSize, double C );

基于区域的图像分割

区域生长分割

从种子点开始,按照一定准则(如相邻像素灰度相似性)向周围扩散,将邻域相似像素加入区域中

  1. 对图像顺序扫描!找到第1个还没有归属的像素, 设该像素为(x 0 , y 0 );
  2. 以(x 0 , y 0 )为中心, 考虑(x 0 , y 0 )的8邻域像素(x, y),如果(x, y)满足生 长准则, 将(x, y)与 (x 0 , y 0 )合并, 同时将(x, y)压入堆栈;
  3. 从堆栈中取出一个像素, 把它当作(x 0 , y 0 )返回到步骤2;
  4. 当堆栈为空时,返回到步骤1;
  5. 重复步骤1 - 4直到图像中的每个点都有归属时。生长结束。
/** @brief Fills a connected component with the given color.

The function cv::floodFill fills a connected component starting from the seed point with the specified
color. The connectivity is determined by the color/brightness closeness of the neighbor pixels. The
pixel at \f$(x,y)\f$ is considered to belong to the repainted domain if:

where \f$src(x',y')\f$ is the value of one of pixel neighbors that is already known to belong to the
component. That is, to be added to the connected component, a color/brightness of the pixel should
be close enough to:
- Color/brightness of one of its neighbors that already belong to the connected component in case
of a floating range.
- Color/brightness of the seed point in case of a fixed range.

Use these functions to either mark a connected component with the specified color in-place, or build
a mask and then extract the contour, or copy the region to another image, and so on.

@param image Input/output 1- or 3-channel, 8-bit, or floating-point image. It is modified by the
function unless the FLOODFILL_MASK_ONLY flag is set in the second variant of the function. See
the details below.
@param mask Operation mask that should be a single-channel 8-bit image, 2 pixels wider and 2 pixels
taller than image. Since this is both an input and output parameter, you must take responsibility
of initializing it. Flood-filling cannot go across non-zero pixels in the input mask. For example,
an edge detector output can be used as a mask to stop filling at edges. On output, pixels in the
mask corresponding to filled pixels in the image are set to 1 or to the a value specified in flags
as described below. It is therefore possible to use the same mask in multiple calls to the function
to make sure the filled areas do not overlap.
@param seedPoint Starting point.
@param newVal New value of the repainted domain pixels.
@param loDiff Maximal lower brightness/color difference between the currently observed pixel and
one of its neighbors belonging to the component, or a seed pixel being added to the component.
@param upDiff Maximal upper brightness/color difference between the currently observed pixel and
one of its neighbors belonging to the component, or a seed pixel being added to the component.
@param rect Optional output parameter set by the function to the minimum bounding rectangle of the
repainted domain.
@param flags Operation flags. The first 8 bits contain a connectivity value. The default value of
4 means that only the four nearest neighbor pixels (those that share an edge) are considered. A
connectivity value of 8 means that the eight nearest neighbor pixels (those that share a corner)
will be considered. The next 8 bits (8-16) contain a value between 1 and 255 with which to fill
the mask (the default value is 1). For example, 4 | ( 255 \<\< 8 ) will consider 4 nearest
neighbours and fill the mask with a value of 255. The following additional options occupy higher
bits and therefore may be further combined with the connectivity and mask fill values using
bit-wise or (|), see cv::FloodFillFlags.

@note Since the mask is larger than the filled image, a pixel \f$(x, y)\f$ in image corresponds to the
pixel \f$(x+1, y+1)\f$ in the mask .

@sa findContours
*/
CV_EXPORTS_W int floodFill( InputOutputArray image, InputOutputArray mask,
Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0,
Scalar loDiff = Scalar(), Scalar upDiff = Scalar(),
int flags = 4 );

分水岭算法

20220520195354

假设我们在盆地的最小值点,打一个洞,然后往盆地里面注水,并阻止两个盆地的水汇集,我们会在两个盆地的水汇集的时刻,在交接的边缘线上(也即分水岭线),建一个坝,来阻止两个盆地的水汇集成一片水域。这样图像就被分成2个像素集,一个是注水盆地像素集,一个是分水岭线像素集。

  • 局部最小值点,该点对应一个盆地的最低点,当我们在盆地里滴一滴水的时候,由于重力作用,水最终会汇聚到该点。注意:可能存在一个最小值面,该平面内的都是最小值点。

  • 盆地的其它位置点,该位置滴的水滴会汇聚到局部最小点。

  • 盆地的边缘点,是该盆地和其它盆地交接点,在该点滴一滴水,会等概率的流向任何一个盆地。

  • 由于噪声点或者其它干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为很多很小的局部极值点的存在

  • 为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是指定mark图像,在这个区域的洪水淹没过程中,水平面都是从定义的marker开始的,这样可以避免一些很小的噪声极值区域的分割。

/** @brief Performs a marker-based image segmentation using the watershed algorithm.

The function implements one of the variants of watershed, non-parametric marker-based segmentation
algorithm, described in @cite Meyer92 .

Before passing the image to the function, you have to roughly outline the desired regions in the
image markers with positive (\>0) indices. So, every region is represented as one or more connected
components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary
mask using findContours and drawContours (see the watershed.cpp demo). The markers are "seeds" of
the future image regions. All the other pixels in markers , whose relation to the outlined regions
is not known and should be defined by the algorithm, should be set to 0's. In the function output,
each pixel in markers is set to a value of the "seed" components or to -1 at boundaries between the
regions.

@note Any two neighbor connected components are not necessarily separated by a watershed boundary
(-1's pixels); for example, they can touch each other in the initial marker image passed to the
function.

@param image Input 8-bit 3-channel image.
@param markers Input/output 32-bit single-channel image (map) of markers. It should have the same
size as image .

@sa findContours

@ingroup imgproc_misc
*/
CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers );

基于边缘轮廓的分割

  • 可以在边缘检测的基础上,基于闭合边缘构建分割后的结果
  • 在分割前需要进行边缘检测
  • OpenCV提供几种基于边缘轮廓的分割方法,包括简单的基于封闭曲线的方法,以及基于活动廓线(active contour的方法)。本文只介绍前者
  • 该算法基于Suzuki, S. 1985的方法,相对古老,但OpenCV基于此提供了关于图像描述的支持,比较好用
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
Mat src;
// the first command-line parameter must be a filename of the binary
// (black-n-white) image
if( argc != 2 || !(src=imread(argv[1], 0)).data)
return -1;

Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);

src = src > 1;
namedWindow( "Source", 1 );
imshow( "Source", src );

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;

findContours( src, contours, hierarchy,
RETR_CCOMP, CHAIN_APPROX_SIMPLE );

// iterate through all the top-level contours,
// draw each connected component with its own random color
int idx = 0;
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( dst, contours, idx, color, FILLED, 8, hierarchy );
}

namedWindow( "Components", 1 );
imshow( "Components", dst );
waitKey(0);
}

void findContours( InputOutputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode, int method, Point offset=Point());

  • image : 单通道图像矩阵,可以是灰度图,但更常用的是经过边缘检测算子处理后的二值图像;
  • contours :定义为“vector<vector> contours”,是一个轮廓列表;
  • hierarchy : 存在嵌套轮廓时,分别为第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号;
  • mode : 定义轮廓的检索模式, 包括CV_RETR_EXTERNAL只检测最外围轮廓, CV_RETR_LIST检测所有轮廓,但不建立等级关系等;
  • method : 包括CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours等;
  • offset : 所有的轮廓信息相对于原始图像对应点的偏移量, 缺省不设置。
/** @brief Finds contours in a binary image.

The function retrieves contours from the binary image using the algorithm @cite Suzuki85 . The contours
are a useful tool for shape analysis and object detection and recognition. See squares.cpp in the
OpenCV sample directory.

@param image Source, an 8-bit single-channel image. Non-zero pixels are treated as 1's. Zero
pixels remain 0's, so the image is treated as binary . You can use cv::compare, cv::inRange, cv::threshold ,
cv::adaptiveThreshold, cv::Canny, and others to create a binary image out of a grayscale or color one.
If mode equals to cv::RETR_CCOMP or cv::RETR_FLOODFILL, the input can also be a 32-bit integer image of labels (CV_32SC1).
@param contours Detected contours. Each contour is stored as a vector of points (e.g.
std::vector<std::vector<cv::Point> >).
@param hierarchy Optional output vector (e.g. std::vector<cv::Vec4i>), containing information about the image topology. It has
as many elements as the number of contours. For each i-th contour contours[i], the elements
hierarchy[i][0] , hiearchy[i][1] , hiearchy[i][2] , and hiearchy[i][3] are set to 0-based indices
in contours of the next and previous contours at the same hierarchical level, the first child
contour and the parent contour, respectively. If for the contour i there are no next, previous,
parent, or nested contours, the corresponding elements of hierarchy[i] will be negative.
@param mode Contour retrieval mode, see cv::RetrievalModes
@param method Contour approximation method, see cv::ContourApproximationModes
@param offset Optional offset by which every contour point is shifted. This is useful if the
contours are extracted from the image ROI and then they should be analyzed in the whole image
context.
*/
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());

/** @brief Draws contours outlines or filled contours.

The function draws contour outlines in the image if \f$\texttt{thickness} \ge 0\f$ or fills the area
bounded by the contours if \f$\texttt{thickness}<0\f$ . The example below shows how to retrieve
connected components from the binary image and label them: :

@param image Destination image.
@param contours All the input contours. Each contour is stored as a point vector.
@param contourIdx Parameter indicating a contour to draw. If it is negative, all the contours are drawn.
@param color Color of the contours.
@param thickness Thickness of lines the contours are drawn with. If it is negative (for example,
thickness=CV_FILLED ), the contour interiors are drawn.
@param lineType Line connectivity. See cv::LineTypes.
@param hierarchy Optional information about hierarchy. It is only needed if you want to draw only
some of the contours (see maxLevel ).
@param maxLevel Maximal level for drawn contours. If it is 0, only the specified contour is drawn.
If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function
draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This
parameter is only taken into account when there is hierarchy available.
@param offset Optional contour shift parameter. Shift all the drawn contours by the specified
\f$\texttt{offset}=(dx,dy)\f$ .
*/
CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX, Point offset = Point() );