- 浏览: 213267 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
synack:
写的很好,图文并茂,语言简单清晰,赞!
SkipList 跳表 -
king_c:
jiandandecaicai 写道你好,请教一下是如何通过E ...
从Hadoop URL 中读取数据 -
jiandandecaicai:
你好,请教一下是如何通过Eclipse来连接Hadop机群的, ...
从Hadoop URL 中读取数据
图像特征检测综述(Image Feature Detection)
作者:王先荣
前言
图像特征提取是计算机视觉和图像处理中的一个概念。它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。本文主要探讨如何提取图像中的“角点”这一特征,及其相关的内容。而诸如直方图、边缘、区域等内容在前文中有所提及,请查看相关文章。OpenCv(EmguCv)中实现了多种角点特征的提取方法,包括:Harris角点、ShiTomasi角点、亚像素级角点、SURF角点、Star关键点、FAST关键点、Lepetit关键点等等,本文将逐一介绍如何检测这些角点。在此之前将会先介绍跟角点检测密切相关的一些变换,包括Sobel算子、拉普拉斯算子、Canny算子、霍夫变换。另外,还会介绍一种广泛使用而OpenCv中并未实现的SIFT角点检测,以及最近在OpenCv中实现的MSER区域检测。所要讲述的内容会很多,我这里尽量写一些需要注意的地方及实现代码,而参考手册及书本中有的内容将一笔带过或者不会提及。
Sobel算子
Sobel算子用多项式计算来拟合导数计算,可以用OpenCv中的cvSobel函数或者EmguCv中的Image<TColor,TDepth>.Sobel方法来进行计算。需要注意的是,xorder和yorder中必须且只能有一个为非零值,即只能计算x方向或者y反向的导数;如果将方形滤波器的宽度设置为特殊值CV_SCHARR(-1),将使用Scharr滤波器代替Sobel滤波器。
使用Sobel滤波器的示例代码如下:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> //Sobel算子 private string SobelFeatureDetect() { //获取参数 int xOrder = int.Parse((string)cmbSobelXOrder.SelectedItem); int yOrder = int.Parse((string)cmbSobelYOrder.SelectedItem); int apertureSize = int.Parse((string)cmbSobelApertureSize.SelectedItem); if ((xOrder == 0 && yOrder == 0) || (xOrder != 0 && yOrder != 0)) return "Sobel算子,参数错误:xOrder和yOrder中必须且只能有一个非零。\r\n"; //计算 Stopwatch sw = new Stopwatch(); sw.Start(); Image<Gray, Single> imageDest = imageSourceGrayscale.Sobel(xOrder, yOrder, apertureSize); sw.Stop(); //显示 pbResult.Image = imageDest.Bitmap; //释放资源 imageDest.Dispose(); //返回 return string.Format("·Sobel算子,用时{0:F05}毫秒,参数(x方向求导阶数:{1},y方向求导阶数:{2},方形滤波器宽度:{3})\r\n", sw.Elapsed.TotalMilliseconds, xOrder, yOrder, apertureSize); }
拉普拉斯算子
拉普拉斯算子可以用作边缘检测;可以用OpenCv中的cvLaplace函数或者EmguCv中的Image<TColor,TDepth>.Laplace方法来进行拉普拉斯变换。需要注意的是:OpenCv的文档有点小错误,apertureSize参数值不能为CV_SCHARR(-1)。
使用拉普拉斯变换的示例代码如下:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> //拉普拉斯变换 private string LaplaceFeatureDetect() { //获取参数 int apertureSize = int.Parse((string)cmbLaplaceApertureSize.SelectedItem); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); Image<Gray, Single> imageDest = imageSourceGrayscale.Laplace(apertureSize); sw.Stop(); //显示 pbResult.Image = imageDest.Bitmap; //释放资源 imageDest.Dispose(); //返回 return string.Format("·拉普拉斯变换,用时{0:F05}毫秒,参数(方形滤波器宽度:{1})\r\n", sw.Elapsed.TotalMilliseconds, apertureSize); }
Canny算子
Canny算子也可以用作边缘检测;可以用OpenCv中的cvCanny函数或者EmguCv中的Image<TColor,TDepth>.Canny方法来进行Canny边缘检测。所不同的是,Image<TColor,TDepth>.Canny方法可以用于检测彩色图像的边缘,但是它只能使用apertureSize参数的默认值3;
而cvCanny只能处理灰度图像,不过可以自定义apertureSize。cvCanny和Canny的方法参数名有点点不同,下面是参数对照表。
Image<TColor,TDepth>.Canny CvInvoke.cvCanny
thresh lowThresh
threshLinking highThresh
3 apertureSize
值得注意的是,apertureSize只能取3,5或者7,这可以在cvcanny.cpp第87行看到:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> aperture_size &= INT_MAX; if( (aperture_size & 1) == 0 || aperture_size < 3 || aperture_size > 7 ) CV_ERROR( CV_StsBadFlag, "" );
使用Canny算子的示例代码如下:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> //Canny算子 private string CannyFeatureDetect() { //获取参数 double lowThresh = double.Parse(txtCannyLowThresh.Text); double highThresh = double.Parse(txtCannyHighThresh.Text); int apertureSize = int.Parse((string)cmbCannyApertureSize.SelectedItem); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); Image<Gray, Byte> imageDest = null; Image<Bgr, Byte> imageDest2 = null; if (rbCannyUseCvCanny.Checked) { imageDest = new Image<Gray, byte>(imageSourceGrayscale.Size); CvInvoke.cvCanny(imageSourceGrayscale.Ptr, imageDest.Ptr, lowThresh, highThresh, apertureSize); } else imageDest2 = imageSource.Canny(new Bgr(lowThresh, lowThresh, lowThresh), new Bgr(highThresh, highThresh, highThresh)); sw.Stop(); //显示 pbResult.Image = rbCannyUseCvCanny.Checked ? imageDest.Bitmap : imageDest2.Bitmap; //释放资源 if (imageDest != null) imageDest.Dispose(); if (imageDest2 != null) imageDest2.Dispose(); //返回 return string.Format("·Canny算子,用时{0:F05}毫秒,参数(方式:{1},阀值下限:{2},阀值上限:{3},方形滤波器宽度:{4})\r\n", sw.Elapsed.TotalMilliseconds, rbCannyUseCvCanny.Checked ? "cvCanny" : "Image<TColor, TDepth>.Canny", lowThresh, highThresh, apertureSize); }
另外,在http://www.china-vision.net/blog/user2/15975/archives/2007/804.html有一种自动获取Canny算子高低阀值的方法,作者提供了用C语言实现的代码。我将其改写成了C#版本,代码如下:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> /// <summary> /// 计算图像的自适应Canny算子阀值 /// </summary> /// <param name="imageSrc">源图像,只能是256级灰度图像</param> /// <param name="apertureSize">方形滤波器的宽度</param> /// <param name="lowThresh">阀值下限</param> /// <param name="highThresh">阀值上限</param> unsafe void AdaptiveFindCannyThreshold(Image<Gray, Byte> imageSrc, int apertureSize, out double lowThresh, out double highThresh) { //计算源图像x方向和y方向的1阶Sobel算子 Size size = imageSrc.Size; Image<Gray, Int16> imageDx = new Image<Gray, short>(size); Image<Gray, Int16> imageDy = new Image<Gray, short>(size); CvInvoke.cvSobel(imageSrc.Ptr, imageDx.Ptr, 1, 0, apertureSize); CvInvoke.cvSobel(imageSrc.Ptr, imageDy.Ptr, 0, 1, apertureSize); Image<Gray, Single> image = new Image<Gray, float>(size); int i, j; DenseHistogram hist = null; int hist_size = 255; float[] range_0 = new float[] { 0, 256 }; double PercentOfPixelsNotEdges = 0.7; //计算边缘的强度,并保存于图像中 float maxv = 0; float temp; byte* imageDataDx = (byte*)imageDx.MIplImage.imageData.ToPointer(); byte* imageDataDy = (byte*)imageDy.MIplImage.imageData.ToPointer(); byte* imageData = (byte*)image.MIplImage.imageData.ToPointer(); int widthStepDx = imageDx.MIplImage.widthStep; int widthStepDy = widthStepDx; int widthStep = image.MIplImage.widthStep; for (i = 0; i < size.Height; i++) { short* _dx = (short*)(imageDataDx + widthStepDx * i); short* _dy = (short*)(imageDataDy + widthStepDy * i); float* _image = (float*)(imageData + widthStep * i); for (j = 0; j < size.Width; j++) { temp = (float)(Math.Abs(*(_dx + j)) + Math.Abs(*(_dy + j))); *(_image + j) = temp; if (maxv < temp) maxv = temp; } } //计算直方图 range_0[1] = maxv; hist_size = hist_size > maxv ? (int)maxv : hist_size; hist = new DenseHistogram(hist_size, new RangeF(range_0[0], range_0[1])); hist.Calculate<Single>(new Image<Gray, Single>[] { image }, false, null); int total = (int)(size.Height * size.Width * PercentOfPixelsNotEdges); double sum = 0; int icount = hist.BinDimension[0].Size; for (i = 0; i < icount; i++) { sum += hist[i]; if (sum > total) break; } //计算阀值 highThresh = (i + 1) * maxv / hist_size; lowThresh = highThresh * 0.4; //释放资源 imageDx.Dispose(); imageDy.Dispose(); image.Dispose(); hist.Dispose(); }
霍夫变换
霍夫变换是一种在图像中寻找直线、圆及其他简单形状的方法,在OpenCv中实现了霍夫线变换和霍夫圆变换。值得注意的地方有以下几点:(1)HoughLines2需要先计算Canny边缘,然后再检测直线;(2)HoughLines2计算结果的获取随获取方式的不同而不同;(3)HoughCircles检测结果似乎不正确。
使用霍夫变换的示例代码如下所示:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> //霍夫线变换 private string HoughLinesFeatureDetect() { //获取参数 HOUGH_TYPE method = rbHoughLinesSHT.Checked ? HOUGH_TYPE.CV_HOUGH_STANDARD : (rbHoughLinesPPHT.Checked ? HOUGH_TYPE.CV_HOUGH_PROBABILISTIC : HOUGH_TYPE.CV_HOUGH_MULTI_SCALE); double rho = double.Parse(txtHoughLinesRho.Text); double theta = double.Parse(txtHoughLinesTheta.Text); int threshold = int.Parse(txtHoughLinesThreshold.Text); double param1 = double.Parse(txtHoughLinesParam1.Text); double param2 = double.Parse(txtHoughLinesParam2.Text); MemStorage storage = new MemStorage(); int linesCount = 0; StringBuilder sbResult = new StringBuilder(); //计算,先运行Canny边缘检测(参数来自Canny算子属性页),然后再用计算霍夫线变换 double lowThresh = double.Parse(txtCannyLowThresh.Text); double highThresh = double.Parse(txtCannyHighThresh.Text); int apertureSize = int.Parse((string)cmbCannyApertureSize.SelectedItem); Image<Gray, Byte> imageCanny = new Image<Gray, byte>(imageSourceGrayscale.Size); CvInvoke.cvCanny(imageSourceGrayscale.Ptr, imageCanny.Ptr, lowThresh, highThresh, apertureSize); Stopwatch sw = new Stopwatch(); sw.Start(); IntPtr ptrLines = CvInvoke.cvHoughLines2(imageCanny.Ptr, storage.Ptr, method, rho, theta, threshold, param1, param2); Seq<LineSegment2D> linesSeq = null; Seq<PointF> linesSeq2 = null; if (method == HOUGH_TYPE.CV_HOUGH_PROBABILISTIC) linesSeq = new Seq<LineSegment2D>(ptrLines, storage); else linesSeq2 = new Seq<PointF>(ptrLines, storage); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); if (linesSeq != null) { linesCount = linesSeq.Total; foreach (LineSegment2D line in linesSeq) { imageResult.Draw(line, new Bgr(255d, 0d, 0d), 4); sbResult.AppendFormat("{0}-{1},", line.P1, line.P2); } } else { linesCount = linesSeq2.Total; foreach (PointF line in linesSeq2) { float r = line.X; float t = line.Y; double a = Math.Cos(t), b = Math.Sin(t); double x0 = a * r, y0 = b * r; int x1 = (int)(x0 + 1000 * (-b)); int y1 = (int)(y0 + 1000 * (a)); int x2 = (int)(x0 - 1000 * (-b)); int y2 = (int)(y0 - 1000 * (a)); Point pt1 = new Point(x1, y1); Point pt2 = new Point(x2, y2); imageResult.Draw(new LineSegment2D(pt1, pt2), new Bgr(255d, 0d, 0d), 4); sbResult.AppendFormat("{0}-{1},", pt1, pt2); } } pbResult.Image = imageResult.Bitmap; //释放资源 imageCanny.Dispose(); imageResult.Dispose(); storage.Dispose(); //返回 return string.Format("·霍夫线变换,用时{0:F05}毫秒,参数(变换方式:{1},距离精度:{2},弧度精度:{3},阀值:{4},参数1:{5},参数2:{6}),找到{7}条直线\r\n{8}", sw.Elapsed.TotalMilliseconds, method.ToString("G"), rho, theta, threshold, param1, param2, linesCount, linesCount != 0 ? (sbResult.ToString() + "\r\n") : ""); } //霍夫圆变换 private string HoughCirclesFeatureDetect() { //获取参数 double dp = double.Parse(txtHoughCirclesDp.Text); double minDist = double.Parse(txtHoughCirclesMinDist.Text); double param1 = double.Parse(txtHoughCirclesParam1.Text); double param2 = double.Parse(txtHoughCirclesParam2.Text); int minRadius = int.Parse(txtHoughCirclesMinRadius.Text); int maxRadius = int.Parse(txtHoughCirclesMaxRadius.Text); StringBuilder sbResult = new StringBuilder(); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); CircleF[][] circles = imageSourceGrayscale.HoughCircles(new Gray(param1), new Gray(param2), dp, minDist, minRadius, maxRadius); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); int circlesCount = 0; foreach (CircleF[] cs in circles) { foreach (CircleF circle in cs) { imageResult.Draw(circle, new Bgr(255d, 0d, 0d), 4); sbResult.AppendFormat("圆心{0}半径{1},", circle.Center, circle.Radius); circlesCount++; } } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·霍夫圆变换,用时{0:F05}毫秒,参数(累加器图像的最小分辨率:{1},不同圆之间的最小距离:{2},边缘阀值:{3},累加器阀值:{4},最小圆半径:{5},最大圆半径:{6}),找到{7}个圆\r\n{8}", sw.Elapsed.TotalMilliseconds, dp, minDist, param1, param2, minRadius, maxRadius, circlesCount, sbResult.Length > 0 ? (sbResult.ToString() + "\r\n") : ""); }
Harris角点
cvCornerHarris函数检测的结果实际上是一幅包含Harris角点的浮点型单通道图像,可以使用类似下面的代码来计算包含Harris角点的图像:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> //Harris角点 private string CornerHarrisFeatureDetect() { //获取参数 int blockSize = int.Parse(txtCornerHarrisBlockSize.Text); int apertureSize = int.Parse(txtCornerHarrisApertureSize.Text); double k = double.Parse(txtCornerHarrisK.Text); //计算 Image<Gray, Single> imageDest = <span sty发表评论
-
openface 人脸识别开放平台
2014-08-10 17:27 1711using System; using System.Co ... -
新中新二代身份证dll调用,报尝试读取或写入受保护的内存,这通常指示其他内存已损坏 这个错
2014-06-26 04:04 869新中新二代身份证dll调用问题: ... -
【OpenCV学习笔记】2.3 OpenCV2.2摄像头读取视频的问题和解决(VS2010)
2014-06-18 16:38 3878摄像头读取视频这一块研究了很长时间,终于弄好了。刚开始 ... -
C# 4.0 并行计算部分 [转发]
2014-05-03 15:24 1010沿用微软的写法,System.Threading.Task ... -
vector 转换成 数组 - [C++]
2013-12-13 18:06 4653参考: http://topic.csdn.n ... -
convert file into byte array
2012-05-12 23:16 890private byte [] StreamFile(s ... -
使用.NET中的XML注释-- 创建帮助文档入门篇
2012-04-07 11:24 1454一.摘要 在本系列 ... -
C#访问和操作MYSQL数据库
2012-03-23 09:59 1567这里介绍下比较简单的方式,引用MySql.Data.dll ... -
C#访问和操作MYSQL数据库
2012-03-23 09:58 1这里介绍下比较简单的方式,引用MySql.Data.dll ... -
http://www.microsoft.com/china/MSDN/library/langtool/VCSharp/USgetstart_vcsharp.
2012-03-22 21:26 938http://www.microsoft.com/china/ ... -
使用Signature Tool自动生成P/Invoke调用Windows API的C#函数声明
2012-03-17 22:47 1240在网上看到很多网友在.NET程序中调用Win32 API, ... -
MarshalAs
2012-03-17 22:04 1292MarshalAs是提供向非托管代码封送数据时的规则。比如S ... -
Timeout expired. The timeout period elapsed prior ..
2012-02-26 19:13 1577关于这个问题,要 ... -
c#winform编程中获取cpu个数的方法 详细出处参考:http://www.ityoudao.com/Web/Csharp_590_1542.html
2012-02-23 18:44 908前些时间,为了配置合更加快速有效地制作Sphinx分词搜 ... -
C# socket 服务端实例
2011-12-08 19:50 1000IPAddress ipAddress; ... -
C# 文件操作
2011-12-08 19:40 710文件读取: Console.W ... -
C# 多线程处理相关说明: WaitHandle,waitCallback, ThreadPool.QueueUserWorkItem
2011-09-03 15:33 4050最近接触C#的一个项目,里面用到了多线程处理,这里做个备 ... -
c#的ThreadPool使用笔记(一)
2011-09-03 15:27 1823摘要:系列文章,从 ... -
C# 调用C++ DLL编码问题
2011-08-29 14:25 1499今天用C#调用C++写的一个DLL 死活不成功 ... -
关于global.asax 总结经验
2011-08-25 14:13 13591. 关于global.asax 总结 ...
相关推荐
图像边缘检测方法研究综述
图像边缘检测综述方法研究
基于深度学习的光学遥感图像目标检测方法综述.pdf
这里是几篇比较齐全的关于图像特征提取的综述论文。
面向视频图像的烟雾检测算法有效克服了传统烟雾探测器靠近火源才能工作的不足,但是由于场景的复杂性和环境因素的不确定性,面向视频图像的烟雾检测算法仍然面临着巨大的挑战。首先简单介绍了烟雾检测技术的基本流程,...
对图像边缘检测技术进行了综述,对想对数字图像边缘检测技术了解的人很有帮助~
图像检测图像检测图像检测图像检测图像检测图像检测图像检测图像检测图像检测图像检测 图像检测图像检测图像检测图像检测图像检测图像检测图像检测图像检测图像检测图像检测 图像检测图像检测图像检测图像检测图像...
基于小样本学习的图像分类技术综述 基于小样本学习的图像分类技术综述 基于小样本学习的图像分类技术综述 基于小样本学习的图像分类技术综述 基于小样本学习的图像分类技术综述 基于小样本学习的图像分类技术综述 ...
图像边缘检测入门必看综述,介绍了用于边缘检测的常用算子算法,很适合对图像边缘检测了解的同学
基于深度学习的图像目标检测算法综述.pdf
对图像特特征检测和匹配的说明比较 YUANLI原理说明
文档介绍了图像特征检测以及匹配的通用方法,优秀的参考文献
图像边缘检测和特征提取
图像的边缘是图像最基本也是最重要的特征之一。边缘检测一直是计算机视觉和图像处理领域的经典研究课题之一。图像分析和理解的第一步常常是边缘检测。边缘检测的目的是去发现图像中关于形状和反射或透射比的信息,是...
针对传统边缘检测算子在检测效果与抗噪方面的不足,提出基于图像特征的边缘检测方法,综合考虑图像的梯度特征、相位特征以及噪声的影响,以方向能量和由直方图差分算子计算的亮度梯度为图像特征进行边缘检测。...
图像特征提取方法的综述,有助于图像特征提取
义出能够描述并区分不同重采样痕迹的两个特征量,将待测图像重叠分块,计算每块的特征量,然后利用特征量的不一致性检测定 位篡改区域。实验结果表明,该方法能够区分旋转与缩放的操作历史痕迹,进行篡改伪造图像的...
卷积神经网络在图像分类和目标检测应用综述_周俊宇....
基于深度学习的图像边缘检测算法综述.pdf