轮廓的查找、表达、绘制、特性及匹配
来源:互联网 发布:程序员上班闲 编辑:程序博客网 时间:2024/05/16 01:01
查找轮廓
Image<Gray, Byte>imageGray =imageSource.Convert<Gray, Byte>();//将源图像转换成灰度图像
int thresholdValue= tbThreshold.Value;//用于二值化的阀值
Image<Gray, Byte>imageThreshold =imageGray.ThresholdBinary(newGray(thresholdValue), new Gray(255d)); //对灰度图像二值化
Contour<Point>contour=imageThreshold.FindContours();
轮廓的表达方式
1.顶点的序列
(1)如果用点来表示,那么依次存储的可能是:(0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1);
(2)如果用点间的线段来表达轮廓,那么依次存储的可能是:(0,0),(2,0),(2,2),(0,2)。
以下代码可以用来获取轮廓上的点:
sbContour.AppendFormat("{0},", contour[i]);
(1)获取Freeman链码
Image<Gray,Byte>imageTemp=imageThreshold.Copy();
IntPtr storage = CvInvoke.cvCreateMemStorage(0);
IntPtr ptrFirstChain = IntPtr.Zero;
int total= CvInvoke.cvFindContours(imageTemp.Ptr, storage,ref ptrFirstChain,sizeof(MCvChain), mode, CHAIN_APPROX_METHOD.CV_CHAIN_CODE,new Point(0,0));
(2)遍历Freeman链码上的点
[DllImport("cv200.dll")]
public staticextern void cvStartReadChainPoints(IntPtr ptrChain,IntPtr ptrReader);
//读取Freeman链码的点
[DllImport("cv200.dll")]
public staticextern Point cvReadChainPoint(IntPtr ptrReader);
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet= System.Runtime.InteropServices.CharSet.Ansi)]
//定义链码读取结构
public structMCvChainPtReader
{
//seqReader
public MCvSeqReader seqReader;
/// char
public bytecode;
/// POINT->tagPOINT
public Point pt;
/// char[16]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst= 16)]
public stringdeltas;
}
//将链码指针转换成结构
MCvChain chain=(MCvChain)Marshal.PtrToStructure(ptrChain,typeof(MCvChain));
//定义存放链码上点的列表
List<Point>pointList =new List<Point>(chain.total);
//链码读取结构
MCvChainPtReader chainReader = new MCvChainPtReader();
IntPtr ptrReader = Marshal.AllocHGlobal(sizeof(MCvSeqReader)+ sizeof(byte)+ sizeof(Point)+ 16 * sizeof(byte));
Marshal.StructureToPtr(chainReader, ptrReader, false);
//开始读取链码
cvStartReadChainPoints(ptrChain, ptrReader);
int i = 0;
while (ptrReader!= IntPtr.Zero&& i < chain.total)
{
//依次读取链码上的每个点
Point p =cvReadChainPoint(ptrReader);
if (ptrReader== IntPtr.Zero)
break;
else
{
pointList.Add(p);
sbChain.AppendFormat("{0},", p);
i++;
}
}
imageResult.DrawPolyline(pointList.ToArray(), true,new Bgr(lblExternalColor.BackColor),2);
轮廓之间的组织方式
private voidTravelContour(Contour<Point>contour,ref int total,refStringBuilder sbContour)
{
if (contour!= null)
{
sbContour.Append("------------------------rn");
sbContour.AppendFormat("轮廓{0},右节点:{1},下级节点:{2},外接矩形:({3})rn", total, contour.HNext!= null, contour.VNext!= null, contour.BoundingRectangle);
sbContour.AppendFormat("包含{0}个点(面积:{1},周长:{2}):rn", contour.Total, contour.Area, contour.Perimeter);
for (inti = 0; i <contour.Total; i++)
sbContour.AppendFormat("{0},", contour[i]);
sbContour.Append("rn");
total++;
if (contour.HNext!= null)
TravelContour(contour.HNext, ref total, ref sbContour);
if (contour.VNext!= null)
TravelContour(contour.VNext, ref total, ref sbContour);
}
}
轮廓的绘制
int maxLevel= 0;//绘制的轮廓深度
int.TryParse(txtMaxLevel.Text,out maxLevel);
imageResult.Draw(contour, new Bgr(lblExternalColor.BackColor), new Bgr(lblHoleColor.BackColor), maxLevel, 2);
轮廓的特性
1.轮廓的多边形逼近
2.轮廓的关键点
private voidGetDominantPointsInfo(Contour<Point>contour, ref StringBuilder sbContour, ref Image<Bgr, Byte>imageResult, doubleparameter1, doubleparameter2, doubleparameter3, doubleparameter4, Bgr dominantPointColor)
{
if (contour.Total> 2)
{
MemStorage storage = new MemStorage();
try
{
IntPtr ptrSeq = cvFindDominantPoints(contour.Ptr, storage.Ptr, (int)CV_DOMINANT.CV_DOMINANT_IPAN, parameter1, parameter2, parameter3, parameter4);
Seq<int>seq = new Seq<int>(ptrSeq, storage);
sbContour.AppendFormat("{0}个关键点:rn", seq.Total);
for (inti = 0; i <seq.Total; i++)
{
int idx= seq[i]; //关键点序列中存储的数据是关键点在轮廓中所处位置的索引
Point p =contour[idx]; //得到关键点的坐标
sbContour.AppendFormat("{0}({1},{2}),", idx, p.X, p.Y);
imageResult.Draw(new CircleF(new PointF(p.X, p.Y),3), dominantPointColor,-1);
}
sbContour.Append("rn");
}
catch (CvException ex)
{
sbContour.AppendFormat("在获取关键点时发生异常,错误描述:{0},错误源:{1},错误堆栈:{2}rn错误文件:{3},函数名:{4},行:{5},错误内部描述:{6}rn", ex.Message, ex.Source, ex.StackTrace, ex.FileName, ex.FunctionName, ex.Line, ex.ErrorStr);
}
catch (Exception e)
{
sbContour.AppendFormat("在获取关键点时发生异常,错误描述:{0},错误源:{1},错误堆栈:{2}rn", e.Message, e.Source, e.StackTrace);
}
finally
{
storage.Dispose();
}
}
}
3.轮廓的周长和面积
4.轮廓的边界框
private voidGetEdgeInfo(Contour<Point>contour, stringedge, ref StringBuilder sbContour, ref Image<Bgr, Byte>imageResult, Bgr edgeColor)
{
if (edge== "Rect")
//矩形
imageResult.Draw(contour.BoundingRectangle, edgeColor,2);
else if (edge == "MinAreaRect")
{
//最小矩形
MCvBox2D box = CvInvoke.cvMinAreaRect2(contour.Ptr, IntPtr.Zero);
PointF[] points = box.GetVertices();
Point[] ps = new Point[points.Length];
for (inti = 0; i <points.Length; i++)
ps[i] = newPoint((int)points[i].X, (int)points[i].Y);
imageResult.DrawPolyline(ps, true, edgeColor,2);
}
else if (edge == "Circle")
{
//圆形
PointF center;
float radius;
CvInvoke.cvMinEnclosingCircle(contour.Ptr, outcenter, out radius);
imageResult.Draw(new CircleF(center, radius), edgeColor, 2);
}
else
{
//椭圆
if (contour.Total>= 6)
{
MCvBox2D box = CvInvoke.cvFitEllipse2(contour.Ptr);
imageResult.Draw(new Ellipse(box), edgeColor, 2);
}
else
sbContour.Append("轮廓点数小于6,不能创建外围椭圆。rn");
}
}
5.轮廓的矩
private voidGetMomentsInfo(Contour<Point>contour, ref StringBuilder sbContour)
{
//矩
MCvMoments moments = contour.GetMoments();
//遍历各种情况下的矩、中心矩及归一化矩,必须满足条件:xOrder>=0; yOrder>=0; xOrder+yOrder<=3;
for (intxOrder = 0; xOrder <=3; xOrder++)
{
for (intyOrder = 0; yOrder <=3; yOrder++)
{
if (xOrder+ yOrder <= 3)
{
double spatialMoment= moments.GetSpatialMoment(xOrder, yOrder);
double centralMoment= moments.GetCentralMoment(xOrder, yOrder);
double normalizedCentralMoment= moments.GetNormalizedCentralMome
sbContour.AppendFormat(
}
}
}
//Hu矩
MCvHuMoments huMonents = moments.GetHuMoment();
sbContour.AppendFormat("Hu矩 h1:{0:F09},h2:{1:F09},h3:{2:F09},h4:{3:F09},h5:{4:F09},h6:{5:F09},h7:{6:F09}rn", huMonents.hu1, huMonents.hu2, huMonents.hu3, huMonents.hu4, huMonents.hu5, huMonents.hu6, huMonents.hu7);
}
6.轮廓的轮廓树
private voidGetConvexInfo(Contour<Point>contour,ref StringBuilder sbContour,ref Image<Bgr,Byte>imageResult)
{
if (!contour.Convex)//判断轮廓是否为凸
{
//凸包
Seq<Point>convexHull =contour.GetConvexHull(ORIENTATION.CV_CLOCKWISE);
//缺陷
Seq<MCvConvexityDefect>defects = contour.GetConvexityDefacts(new MemStorage(), ORIENTATION.CV_CLOCKWISE);
//显示信息
sbContour.AppendFormat("轮廓的凸包有{0}个点,依次为:", convexHull.Total);
Point[] points = new Point[convexHull.Total];
for (inti = 0; i <convexHull.Total; i++)
{
Point p = convexHull[i];
points[i] = p;
sbContour.AppendFormat("{0},", p);
}
sbContour.Append("rn");
imageResult.DrawPolyline(points, true,new Bgr(lblConvexColor.BackColor),2);
MCvConvexityDefect defect;
sbContour.AppendFormat("轮廓有{0}个缺陷,依次为:rn", defects.Total);
for (inti = 0; i <defects.Total; i++)
{
defect = defects[i];
sbContour.AppendFormat("缺陷:{0},起点:{1},终点:{2},最深的点:{3},深度:{4}rn", i, defect.StartPoint, defect.EndPoint, defect.DepthPoint, defect.Depth);
}
}
else
sbContour.Append("轮廓是凸的,凸包和轮廓一样。rn");
}
Rectangle rect1 = contour1.BoundingRectangle;
float maxDist1= (float)Math.Sqrt(rect1.Width* rect1.Width+ rect1.Height* rect1.Height);//轮廓的最大距离:这里使用轮廓矩形边界框的对角线长度
int[] bins1= new int[] { 60,20 };
RangeF[] ranges1 = new RangeF[] { new RangeF(0f, 180f), new RangeF(0f, maxDist1) }; //直方图第0维为角度,范围在(0,180),第2维为轮廓两条边缘线段的距离
DenseHistogram hist1 = new DenseHistogram(bins1, ranges1);
CvInvoke.cvCalcPGH(contour1.Ptr, hist1.Ptr);
轮廓的匹配
1.Hu矩匹配
2.轮廓树匹配
3.成对几何直方图匹配
private voidbtnStartMatch_Click(objectsender, EventArgs e)
{
//准备轮廓(这里只比较最外围的轮廓)
Image<Bgr, Byte>image1 = new Image<Bgr,byte>((Bitmap)pbImage1.Image);
Image<Bgr, Byte>image2 = new Image<Bgr,byte>((Bitmap)pbImage2.Image);
Image<Gray, Byte>imageGray1 =image1.Convert<Gray, Byte>();
Image<Gray, Byte>imageGray2 =image2.Convert<Gray, Byte>();
Image<Gray, Byte>imageThreshold1 =imageGray1.ThresholdBinaryInv(newGray(128d), newGray(255d));
Image<Gray, Byte>imageThreshold2 =imageGray2.ThresholdBinaryInv(newGray(128d), newGray(255d));
Contour<Point>contour1 = imageThreshold1.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_EXTERNAL);
Contour<Point>contour2 = imageThreshold2.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_EXTERNAL);
//进行匹配
string result= "";
if (rbHuMoments.Checked)
result = MatchShapes(contour1, contour2);//Hu矩匹配
else if (rbContourTree.Checked)
result = MatchContourTrees(contour1, contour2);//轮廓树匹配
else if (rbPGH.Checked)
result = MatchPghHist(contour1, contour2);//成对几何直方图匹配
txtResult.Text += result;
}
//Hu矩匹配
private stringMatchShapes(Contour<Point>contour1, Contour<Point>contour2)
{
//匹配方法
CONTOURS_MATCH_TYPE matchType = rbHuI1.Checked ? CONTOURS_MATCH_TYPE.CV_CONTOUR_MATCH_I1 : (rbHuI2.Checked? CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I2 : CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I3);
Stopwatch sw = new Stopwatch();
sw.Start();
//匹配
double matchValue= contour1.MatchShapes(contour2, matchType);
sw.Stop();
double time= sw.Elapsed.TotalMilliseconds;
return string.Format("Hu矩匹配({0:G}),结果:{1:F05},用时:{2:F05}毫秒rn", matchType, matchValue, time);
}
//轮廓树匹配
private stringMatchContourTrees(Contour<Point>contour1, Contour<Point>contour2)
{
//生成轮廓树
double thresholdOfCreate= double.Parse(txtThresholdOfCreateCont
IntPtr ptrTree1 = CvInvoke.cvCreateContourTree(contour1.Ptr,new MemStorage().Ptr, thresholdOfCreate);
IntPtr ptrTree2 = CvInvoke.cvCreateContourTree(contour2.Ptr, newMemStorage().Ptr, thresholdOfCreate);
//匹配
double thresholdOfMatch= double.Parse(txtThresholdOfMatchConto
Stopwatch sw = new Stopwatch();
sw.Start();
double matchValue= CvInvoke.cvMatchContourTrees(ptrTree1, ptrTree2, MATCH_CONTOUR_TREE_METHOD.CONTOUR_TREES_MATCH_I1, thresholdOfMatch);
sw.Stop();
double time= sw.Elapsed.TotalMilliseconds;
return string.Format("轮廓树匹配(生成轮廓树的阀值:{0},比较轮廓树的阀值:{1}),结果:{2:F05},用时:{3:F05}毫秒rn", thresholdOfCreate, thresholdOfMatch, matchValue, time);
}
//成对几何直方图匹配
private stringMatchPghHist(Contour<Point>contour1, Contour<Point>contour2)
{
//生成成对几何直方图
Rectangle rect1 = contour1.BoundingRectangle;
float maxDist1= (float)Math.Sqrt(rect1.Width* rect1.Width+ rect1.Height* rect1.Height);//轮廓的最大距离:这里使用轮廓矩形边界框的对角线长度
int[] bins1= new int[] { 60,20 };
RangeF[] ranges1 = new RangeF[] { new RangeF(0f, 180f), new RangeF(0f, maxDist1) }; //直方图第0维为角度,范围在(0,180),第2维为轮廓两条边缘线段的距离
DenseHistogram hist1 = new DenseHistogram(bins1, ranges1);
CvInvoke.cvCalcPGH(contour1.Ptr, hist1.Ptr);
Rectangle rect2 = contour2.BoundingRectangle;
float maxDist2= (float)Math.Sqrt(rect2.Width* rect2.Width+ rect2.Height* rect2.Height);
int[] bins2= new int[] { 60,20 };
RangeF[] ranges2 = new RangeF[] { new RangeF(0f, 180f), new RangeF(0f, maxDist2) };
DenseHistogram hist2 = new DenseHistogram(bins2, ranges2);
CvInvoke.cvCalcPGH(contour2.Ptr, hist2.Ptr);
//匹配
Stopwatch sw = new Stopwatch();
sw.Start();
double compareResult;
HISTOGRAM_COMP_METHOD compareMethod = rbHistCorrel.Checked ? HISTOGRAM_COMP_METHOD.CV_COMP_CORREL : (rbHistChisqr.Checked ? HISTOGRAM_COMP_METHOD.CV_COMP_CHISQR : (rbHistIntersect.Checked? HISTOGRAM_COMP_METHOD.CV_COMP_INTERSECT : HISTOGRAM_COMP_METHOD.CV_COMP_BHATTACHARYYA));
if (rbHistEmd.Checked)
{
//EMD
//将直方图转换成矩阵
Matrix<Single>matrix1 = FormProcessHist.ConvertDenseHistogramToM
Matrix
compareResult
matrix1.Dispose();
matrix2.Dispose();
}
else
{
//直方图对比方式
hist1.Normalize(1d);
hist2.Normalize(1d);
compareResult = CvInvoke.cvCompareHist(hist1.Ptr, hist2.Ptr, compareMethod);
}
sw.Stop();
double time= sw.Elapsed.TotalMilliseconds;
return string.Format("成对几何直方图匹配(匹配方式:{0}),结果:{1:F05},用时:{2:F05}毫秒rn", rbHistEmd.Checked ? "EMD" : compareMethod.ToString("G"), compareResult, time);
}
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- 轮廓的查找、表达、绘制、特性及匹配 c#版
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- 轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- 轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- 轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- 轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- OpenCV_轮廓的查找、表达、绘制、特性及匹配
- android volley获取网络图片制作圆头像
- 将一个进程转变为守护进程所需要的步骤
- 啊手机卡打开拉萨决定了卡结算来得及啊劳动力
- Android通过String.format格式化(动态改变)字符串资源的显示内容
- 修改linux下命令提示行路径长度
- 轮廓的查找、表达、绘制、特性及匹配
- C++实现链表函数
- 如何阅读源代码
- Chapter 12 Concurrent Programming [《CSAPP》 笔记]
- UVa 10003 - Cutting Sticks
- Single Number问题
- RabbitMQ RPC
- Date日期处理
- Storm实战之Trident简介