第一章 数字图像基础
1. opencv简介
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效,由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
Opencv其应用非常广泛,如无人机、无人驾驶等都能看到Opencv的踪迹,很多机器学习也都是基于opencv视觉库。要学好opencv,必看opencv官网,官网包含Opencv各个版本下载链接、Opencv各平台安装说明以及各API接口函数说明文档,是学习Opencv最好的网站。
学习Opencv不要上来就要弄明白各个细节,入门时,应该多实践,看着教程跑示例,先对Opencv有个基本的认知,再深入去了解背后的算法和c++语言。
2. Mat类
在正式介绍之前,先简单一下数字图像的基本概念。在计算机看来一副图像只是一堆亮度各异的点,一副尺寸为M × N的图像可以用一个M × N的矩阵来表示,阵元素的值表示这个位置上像素的亮度,一般来说越大该点亮。一般来说,灰度图用2维矩阵表示,彩色(多通道)图像用3维矩阵(M x N x 3)表示。对于图像显示来说,目前大部分设备都是用无符号8位整数(类型为CV_8U)表示像素亮度。
早期的 OpenCV 中,使用IplImage和CvMat数据结构来表示图像。IplImage和CvMat都是C语言的结构。使用这两个结构的问题是内存需要手动管理,开发者必须清楚的知道何时需要申请内存,释放内存。这个给开发者带来了一定的负担,开发者应该将更多精力用于算法设计,因此在新版本的 OpenCV中引入了Mat类。
Mat本质上是由两个数据部分组成的类:Header和Pointer。Header中主要包含矩阵的大小,存储方式,存储地址等信息,Pointer中存储指向像素值的指针。
示例一:创建3行两列5通道矩阵
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat M(3,2,CV_8UC(5));
cout<<M <<endl;
}
示例二:创建3行两列3通道矩阵,并赋值
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat M(3,2,CV_8UC3,Scalar(0,0,255));
cout <<"M="<<endl <<" "<<M <<endl;
}
示例三:特殊矩阵
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
//0矩阵
Mat Z=Mat::zeros(2,3,CV_8UC1);
cout<<Z<<endl;
//1矩阵
Mat O=Mat::ones(2,3,CV_32F);
cout<<O<<endl;
//单位矩阵
Mat E=Mat::eye(2,3,CV_64F);
cout<<E<<endl;
}
3. 图像显示
示例一:显示图片
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//读取图片,当前目录下,名为1.jpg的图片
Mat img0=imread("1.jpg");
//图像拷贝
Mat img1=img0.clone();
//图像拷贝
Mat img2;
img2=img0;
//创建同大小,同类型图像,但并不拷贝像素点
Mat img3;
img3.create(img0.size(),img0.type());
imshow("img0",img0);
imshow("img1",img1);
imshow("img2",img2);
imshow("img3",img3);
//这里一定要延时,因为imshow加载图片需要时间,这里0表示无限延时,停在此处
waitKey(0);
}
示例二:at()函数遍历像素点,渐变赋值
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//创建图像,大小为600*800
Mat grayim(600,800,CV_8UC1);
Mat colorim(600,800,CV_8UC3);
for(int i=0;i<grayim.rows;++i)
for(int j=0;j<grayim.cols;++j)
grayim.at <uchar>(i,j)=(i+j)%255;
for(int i=0;i<colorim.rows;++i)
for(int j=0;j<colorim.cols;++j)
{
//Vec3b表示8U类型的RGB彩色图像向量
//pixel变量描述RGB颜色
Vec3b pixel;
//0通道的B分量
pixel[0]=i%255;
//1通道的G分量
pixel[1]=j%255;
//2通道的R分量
pixel[2]=0;
colorim.at<Vec3b>(i,j)=pixel;
}
imshow("grayim",grayim);
imshow("colorim",colorim);
waitKey(0);
return 0;
}
示例三:MatIterator_容器遍历像素点,随机赋值
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat grayim(600,800,CV_8UC1);
Mat colorim(600,800,CV_8UC3);
MatIterator_<uchar> grayit,grayend;
for(grayit=grayim.begin<uchar>(),grayend=grayim.end<uchar>();grayit!=grayend;++grayit)
*grayit=rand()%255;
MatIterator_<Vec3b> colorit,colorend;
for(colorit=colorim.begin<Vec3b>(),colorend=colorim.end<Vec3b>();colorit!=colorend;
++colorit)
{
(*colorit)[0]=rand()%255;
(*colorit)[1]=rand()%255;
(*colorit)[2]=rand()%255;
}
imshow("grayim",grayim);
imshow("colorim",colorim);
waitKey(0);
return 0;
}
示例四:打开摄像头
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//从摄像头读入视频
VideoCapture capture(0);
//循环显示每一帧
while(1)
{
//定义一个Mat变量,用于存储每一帧的图像
Mat frame;
//读取当前帧
capture>>frame;
//显示当前帧
imshow("read video",frame);
//延时30ms
waitKey(30);
}
return 0;
}
示例五:打开网络摄像头
//使用mjpg-streamer工具将图像发布到网络上
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int, char**)
{
VideoCapture vcap;
Mat image;
const std::string videoStreamAddress = "http://192.168.23.128:8080/?action=stream?dummy=param.mjpg";
/* it may be an address of an mjpeg stream,
e.g. "http://user:pass@cam_address:8081/cgi/mjpg/mjpg.cgi?.mjpg" */
//open the video stream and make sure it's opened
if(!vcap.open(videoStreamAddress))
{
cout << "Error opening video stream or file" << endl;
return -1;
}
while(1)
{
if(!vcap.read(image))
{
cout << "No frame" << endl;
waitKey();
}
imshow("Output Window", image);
if(waitKey(1) >= 0)
break;
}
}
第二章 数字图像处理
1. 图像滤波
图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制。消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。
图像滤波的目的有两个:一是抽出对象的特征作为图像识别的特征模式;另一个是为适应图像处理的要求,消除图像数字化时所混入的噪声。
#include <opencv2/opencv.hpp>
using namespace cv;
//调整各滤波函数参数大小,观察滤波后的效果
int main()
{
Mat src=imread("lena.jpg");
Mat dst;
//高斯滤波,低通就是模糊,高通就是锐化
GaussianBlur(src,dst,Size(5,5),0,0);
imwrite("gauss.jpg",dst);
//中值滤波
medianBlur(src,dst,3);
imwrite("med.jpg",dst);
//均值滤波
blur(src,dst,Size(3,3),Point(-1,-1));
imwrite("mean.jpg",dst);
//双边滤波
bilateralFilter(src,dst,5,10.0,2.0);
//将生成的图片写入当前目录
imwrite("bil.jpg",dst);
waitKey();
return 0;
}
2. 图像膨胀与腐蚀
膨胀与腐蚀能实现多种多样的功能,主要如下:
- 消除噪声
- 分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素
- 寻找图像中的明显的极大值区域或极小值区域
- 求出图像的梯度
腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
示例一:图像膨胀
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat image = imread("1.jpg");
imshow("src", image);
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//进行膨胀操作
dilate(image,out, element);
imshow(" ", out);
waitKey(0);
return 0;
}
示例二:图像腐蚀
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("1.jpg");
imshow("src", srcImage);
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat dstImage;
//进行腐蚀操作
erode(srcImage, dstImage, element);
imshow(" ", dstImage);
waitKey(0);
return 0;
}
3. 边缘检测
缘检测一般步骤:
- 滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。
- 增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。
- 检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。
边缘检测有canny算子、laplace算子、sobel算子以及scharr算子算法
示例一:canny算子
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat src=imread("1.jpg");
Mat src1=src.clone();
imshow("src",src);
Canny(src,src,150,100,3);
imshow("result1", src);
Mat dst,edge,gray;
//创建与src同类型和大小的矩阵(dst)
dst.create(src1.size(),src1.type());
//将原图像转换为灰度图像
cvtColor(src1,gray,CV_BGR2GRAY );
//先用使用3x3内核来降噪
blur(gray,edge,Size(3,3));
//运行Canny算子
Canny(edge,edge,3,9,3);
//将dst内的所有元素设置为0
dst=Scalar::all(0);
//使用Canny算子输出的边缘图edge作为掩码,将原图src拷到目标图dst中
//copyTo将src1中edge矩阵对应的非零部分复制到dst中
src1.copyTo(dst,edge);
imshow("result2",dst);
waitKey(0);
return 0;
}
示例二:sobel算子
Sobel()
算子是一个主要用作边缘检测的离散微分算子,它结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y,dst;
Mat src = imread("1.jpg");
imshow("src",src);
//求 X方向梯度
Sobel(src,rad_x,CV_16S,1,0,3,1,1,BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
imshow("X方向Sobel",abs_grad_x);
//求Y方向梯度
Sobel(src,grad_y,CV_16S,0,1,3,1,1,BORDER_DEFAULT );
convertScaleAbs(grad_y,abs_grad_y);
imshow("Y方向Sobel",abs_grad_y);
//合并梯度(近似)
addWeighted(abs_grad_x,0.5,abs_grad_y,0.5,0,dst);
imshow("整体方向Sobel",dst);
waitKey(0);
return 0;
}
示例三:laplace算子
laplacian()
函数可以计算出图像经过拉普拉斯变换后的结果
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat src,src_gray,dst, abs_dst;
src = imread("1.jpg");
imshow("src", src);
//使用高斯滤波消除噪声
GaussianBlur(src,src,Size(3,3),0,0,BORDER_DEFAULT);
//转换为灰度图
cvtColor(src,src_gray,CV_RGB2GRAY);
//使用Laplace函数
Laplacian(src_gray,dst,CV_16S,3,1,0,BORDER_DEFAULT);
//计算绝对值,并将结果转换成8位
convertScaleAbs(dst,abs_dst );
imshow("图像Laplace变换",abs_dst);
waitKey(0);
return 0;
}
示例四:scharr滤波器
使用Scharr()
滤波器运算符计算x或y方向的图像差分,它的参数变量和Sobel基本上是一样的,除了没有ksize核的大小。
#include <opencv2/opencv.hpp>
using namespace cv;
int main( )
{
//创建grad_x和grad_y矩阵
Mat grad_x, grad_y;
Mat abs_grad_x,abs_grad_y,dst;
Mat src = imread("1.jpg");
imshow("src", src);
//求X方向梯度
Scharr(src,grad_x, CV_16S,1,0,1,0,BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x );
imshow("X方向Scharr", abs_grad_x);
//求Y方向梯度
Scharr(src,grad_y,CV_16S,0,1,1,0,BORDER_DEFAULT);
convertScaleAbs(grad_y,abs_grad_y);
imshow("Y方向Scharr",abs_grad_y);
//合并梯度(近似)
addWeighted(abs_grad_x,0.5,abs_grad_y,0.5,0,dst);
imshow("合并梯度后Scharr",dst);
waitKey(0);
return 0;
}
4. 图像融合
addWeighted()
这个函数的作用是,计算两个数组(图像阵列)的加权和。
void addWeighted(InputArray src1,double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
- src1:表示需要加权的第一个数组。
- alpha:表示第一个数组的权重
- src2:表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。
- beta:表示第二个数组的权重值。
- dst:输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。
- gamma:一个加到权重总和上的标量值。
- dtype:输出阵列的可选深度,有默认值-1。
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat image1=imread("1.jpg",1);
Mat image2=imread("2.jpg",1);
Mat image;
addWeighted(image1,0.8,image2,0.2,0.0,image);
imshow("1",image1);
imshow("2",image2);
imshow("new",image);
waitKey(0);
return 0;
}
5. 分离颜色通道
- split()函数将一个多通道数组分离成几个单通道数组。
- merge()函数的功能是split函数的逆向操作,将多个数组组合合并成一个多通道的数组。
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage;
Mat grayImage;
Mat imageBlueChannel;
vector<Mat>channels;
srcImage=imread("1.jpg");
imshow("src",srcImage);
//读取灰度图像
grayImage=imread("1.jpg",0);
//分离颜色通道
split(srcImage,channels);
//选取蓝色通道
imageBlueChannel=channels.at(0);
//融合蓝色通道与灰度图像
addWeighted(imageBlueChannel,1.0,grayImage,0.5,0.0,imageBlueChannel);
//合并颜色通道,相当于对原图像蓝色通道加强
merge(channels,imageBlueChannel);
imshow("blue",imageBlueChannel);
waitKey(0);
return 0;
}
}
6. RGB与HSV空间转换
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage=imread("1.jpg");
cvtColor(srcImage,srcImage,COLOR_BGR2HSV);
imshow(" ",srcImage);
waitKey();
}
第三章 图像缩放
1. 图像旋转
getRotationMatrix2D()
函数主要用于获得图像绕着某一点的旋转矩阵。
getRotationMatrix2D(Point2f center,double angle,double scale)
- Point2f center:表示旋转的中心点。
- double angle:表示旋转的角度。
- double scale:图像缩放因子。
#include <opencv2/opencv.hpp>
using namespace cv;
void img_rotate(Mat src_img,Mat* rotate_img,int angle)
{
Point2f center=Point2f(src_img.cols/2,src_img.rows/2);
Mat rotateMat=getRotationMatrix2D(center,angle,1);
warpAffine(src_img,*rotate_img,rotateMat,src_img.size());
}
int main()
{
Mat srcImage,dstImage;
srcImage=imread("1.jpg");
img_rotate(srcImage,&dstImage,180);
imshow(" ",dstImage);
waitKey();
}
2. 图像缩放
示例一:resize函数
resize()
函数将源图像精确地转换为指定尺寸的目标图像。
void resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
- src:输入图像。
- dst:输出图像。
- dsize:输出图像的大小。
- fx:沿水平轴的缩放系数。
- fy:沿垂直轴的缩放系数。
- interpolation:用于指定插值方式,默认为INTER_LINEAR(线性插值)。
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("1.jpg");
Mat tmpImage,dstImage1,dstImage2;
tmpImage=srcImage;
//进行尺寸调整操作
resize(tmpImage,dstImage1,Size( tmpImage.cols/2, tmpImage.rows/2 ),(0,0),(0,0),3);
resize(tmpImage,dstImage2,Size( tmpImage.cols*2, tmpImage.rows*2 ),(0,0),(0,0),3);
imshow("resize1", dstImage1);
imshow("resize2", dstImage2);
waitKey(0);
return 0;
}
示例二:pyrUp函数
pyrUp()
函数的作用是向上采样并模糊一张图像,说白了就是放大一张图片。
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("1.jpg");
Mat tmpImage,dstImage;
tmpImage=srcImage;
imshow("srcImage", srcImage);
pyrUp( tmpImage, dstImage, Size( tmpImage.cols*2, tmpImage.rows*2 ) );
imshow("dstImage", dstImage);
waitKey(0);
return 0;
}
示例三:pyrDown函数
pyrDown()
函数的作用是向下采样并模糊一张图片,说白了就是缩小一张图片。
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("1.jpg");
Mat tmpImage,dstImage;
tmpImage=srcImage;
imshow("srcImage", srcImage);
pyrDown( tmpImage, dstImage, Size( tmpImage.cols/2, tmpImage.rows/2 ) );
imshow("dstImage", dstImage);
waitKey(0);
return 0;
}
第四章 图像分割
1. 图像分割
#include <opencv2/opencv.hpp>
using namespace cv;
//剪切图片为m * n 块
void Cut_img(Mat src_img,int m,int n,Vector<Mat> ceil_img)
{
int t = m * n;
int height = src_img.rows;
int width = src_img.cols;
int ceil_height = height/m;
int ceil_width = width/n;
Mat roi_img,tmp_img;
Point p1,p2;
for(int i = 0;i<m;i++)
for(int j = 0;j<n;j++)
{
Rect rect(i+j*ceil_width,j+i*ceil_height,ceil_width,ceil_height);
src_img(rect).copyTo(roi_img);
ceil_img.push_back(roi_img);
imshow("roi_img",roi_img);
//关闭当前显示中的图像,才可以显示其他图像
waitKey(0);
}
}
int main()
{
Mat img = imread("lena.jpg",1);
imshow("src img",img);
int m = 3;
int n = 3;
Vector<Mat> ceil_img = m*n;
Cut_img(img,m,n,ceil_img);
waitKey();
return 0;
}
2. 获取感兴趣区
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void getROI(Mat src_image,Mat* dst_roi,Rect rect);//获取兴趣区
void setRect(Rect* rect,int x,int y,int width,int height);//设置矩形区
int main()
{
Mat image=imread("1.jpg");
Mat roi;
Rect rect;
imshow("Origin",image);
cout<<"width:"<<image.cols<<" "<<"height:"<<image.rows<<endl;
setRect(&rect,320,240,150,150);
getROI(image,&roi,rect);
imshow("ROI",roi);
waitKey(0);
return 0;
}
void setRect(Rect* rect,int x,int y,int width,int height)
{
(*rect).x=x;
(*rect).y=y;
(*rect).width=width;
(*rect).height=height;
}
void getROI(Mat src_image,Mat* dst_roi,Rect rect)
{
Mat img=src_image.clone();
int cols=img.cols,rows=img.rows;
if(cols - 1 - rect.x<rect.width||rows - 1 - rect.y<rect.height)
cout<<"over area!"<<endl;
*dst_roi=img(Rect(rect.x,rect.y,rect.width,rect.height));
}
3. 图像拼接
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat combine1,combine2,combine;
Mat img1 = imread("1.jpg",1);
Mat img2 = imread("2.jpg",1);
Mat img3 = imread("3.jpg",1);
Mat img4 = imread("4.jpg",1);
hconcat(img1,img2,combine1);
hconcat(img3,img4,combine2);
vconcat(combine1,combine2,combine);
imshow("roi_img",roi_img);
waitKey(0);
}
第五章 图像识别
1. 直线识别
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("1.jpg");
Mat midImage,dstImage;
Canny(srcImage, midImage, 50, 200, 3);
cvtColor(midImage,dstImage, CV_GRAY2BGR);
vector<Vec4i> lines;
HoughLinesP(midImage, lines, 1, CV_PI/180, 80, 50, 10 );
//依次在图中绘制出每条线段
for(size_t i = 0; i <lines.size(); i++)
{
Vec4i l = lines[i];
line(dstImage,Point(l[0], l[1]),Point(l[2], l[3]),Scalar(186,88,255),1,CV_AA);
}
imshow(" ", dstImage);
waitKey(0);
return 0;
}
2. 矩形识别
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat src_image=imread("1.jpg");
Mat gray_image;
cvtColor(src_image,gray_image,CV_BGR2GRAY);
imshow("src",src_image);
Mat binary_image;
//局部自适应二值化函数
adaptiveThreshold(gray_image,binary_image,255,CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY_INV,25,10);
imshow("binary",binary_image);
Mat de_noise=binary_image.clone();
medianBlur(binary_image,de_noise,5); //中值滤波
Mat dilate_image;//膨胀
Mat element=getStructuringElement(MORPH_RECT,Size(20,20));
dilate(de_noise,dilate_image,element);
imshow("dilate",dilate_image);
//外部加框,检测连通域,每一个连通域以一系列的点表示,FindContours方法只能得到第一个域
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
//CV_RETR_EXTERNAL只检测外部轮廓,可根据自身需求进行调整
findContours(dilate_image,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
Mat contours_image(dilate_image.rows,dilate_image.cols,CV_8U,Scalar(255));
int index=0;
for(;index>=0;index=hierarchy[index][0])
{
Scalar color(rand()&255,rand()&255,rand()&255);
drawContours(contours_image,contours,index,Scalar(0),1,8,hierarchy);//描绘字符的外轮廓
Rect rect=boundingRect(contours[index]);//检测外轮廓
rectangle(contours_image,rect,Scalar(0,0,255),3);//对外轮廓加矩形框
}
imshow("zt",contours_image);
waitKey(0);
}
3. 圆形识别
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("1.jpg");
Mat midImage,dstImage;
cvtColor(srcImage,midImage, CV_BGR2GRAY);//转化边缘检测后的图为灰度图
GaussianBlur( midImage, midImage, Size(9, 9), 2, 2 );
vector<Vec3f> circles;
HoughCircles( midImage, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100, 0, 0 );
//依次在图中绘制出圆
for( size_t i = 0; i <circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
//绘制圆心
circle( srcImage, center, 3, Scalar(0,255,0), -1, 8, 0 );
//绘制圆轮廓
circle( srcImage, center, radius, Scalar(155,50,255), 3, 8, 0 );
}
imshow(" ", srcImage);
waitKey(0);
return 0;
}