opencv subdiv2d未声明标示符cvsubdiv2d、CvSubdiv2DPoint、cvInitSubdivDelaunay2D等,怎么解?

Wikipedia,自由的百科全书
(修订版本间差异)
10:50 日的修订版本
请在下面写下本书的翻译错误,谢谢!-- 17:26 日 (CST)
提交错误较多的读者有可能得到赠书。
具体请看(活动已经结束)-- 11:29 日 (CST)
新加入的勘误请按页序插入。格式参考其他页。 --
倒数第二段,倒数第四行“正式在他们组织的一次。。。”应为“正是在他们组织的一次。。。”(by Joseph)
ps:我不小心把38页的那个删了。。。版主改回来吧
412页,图11-4中。某个摄像机透镜的镜像畸变图,应该是某个摄像机的径向畸变图
函数void example2_4(IplImage *image)后少了一个{
IPlImage *doPyrDown( IplImage* in, int filter = IPL_GAUSSIAN_5x5)
IPlImage *doPyrDown( IplImage* in, int filter = CV_GAUSSIAN_5x5)
例2-6 第9行,cvCreateImage(cvSize(cvGetSize(in),IPL_DEPTH_8U,1); 应为cvCreateImage(cvGetSize(in),IPL_DEPTH_8U,1);
(by michaellee@OpenCV China)
表3-1下面第三行最后:
它被传递给给val[0]……多了一个 “给” 字。(by wxf198861)
第一段最后“一个用于新建一个为二位矩阵的例程具有以下原型”后面,cvMat* cvCreateMat(...) 第一个c字母应该大写(Winking | Delbert)
例3-1上面一段倒数第3行:“可通过调用函数vGetSize(CvMat*)”
应该为:“可通过调用函数cvGetSize(CvMat*)”。(by FlyZhy)
正文第二段最后一句话:当这个矩阵不再需要时,
应该是:当矩阵不再需要时。
前者容易让人误会成cvCloneMat创建的新矩阵。(by mayitbe)
不小心把38页删掉了!真的不好意思!麻烦版主改回来。(Delbert已经找回)
例3-5下面第一段第一行第一词:遗憾的憾写错了。。。(by wxf198861)
倒数第5行:“cvGet*D中有四个函数返回的是整型的”,应该为:“cvGet*D中有四个函数返回的是double型的”。(by FlyZhy)
第6行开头:“...指针解引用的时候,”应改为:“...指针被引用的时候,”(xmchenzb)
第8行中“...行数据元素的是用...”,应该为:“...数据元素是用...”(liikelyxuhy)
倒数第6行开头“便可可以”应该为“便可以”(liikelyxuhy)
第3行:"nchannals"应该为"nChannels" (by deltat)
第4行"冗于"应该为"冗余" (by deltat)
"重要度别"
"重要区别"
(chenxinqiu50)
“我们已经知道是种类型的”
改为 “我们已经知道这种类型的”
(loop.k)
21行 “必须对偏移并进行调整”,“并”字应去掉 (lcykk)
第1行 “仅最大化HSV图像“S”和“V”部分” 改为 “仅最大化RGB图像“G”和“R”部分”
10行 “用imageROI来增加某范围的像素”有歧义,
应该为“用imageROI来增加某范围的像素值”
例3-12中:
cvSetImage ROI(src, cvRect(x,y,width,height));
cvSetImageROI(src, cvRect(x,y,width,height));
cvAnd, cvAnds
缺85页 CVScale(chenxinqiu50)
英文原书中的表格中也缺少,我已经提交勘误到。 14:08 日 (CST)
第二行 "cvInvert 求矩阵的转置" 应为"cvInvert 求矩阵的逆" (by deltat)
倒数12行 "多通道所组" 应为 "多通道数组"
(loop.k)
57页 4,9,14行,const 应该改成const CvArr* (by Zxyang2008)
因该改为 CvArr*而不是const CvArr*。不能用静态的。(by Easycode)
59 页 23 行 ,cvSetImage ROI 应该改 成cvSetImageROI.(by xssh123)
在58页和59页中出现的alpha、beta、gamma(代码中的变量或数学变量)字体不一致。(by FlyZhy)
cvDiv()函数缺少 const CvArr*
mask 参数,但在函数说明中有此参数介绍? (xmchenzb)
cvGetCols()函数的工作原理与cvGetCols完全一致...
cvGetCols()函数的工作原理与cvGetCol()完全一致...
(by hbclc)
cvMaxs()函数缺少 const CvArr*
mask 参数,但在函数说明中有此参数介绍? (xmchenzb)
正文倒数第6行:cvMaxS(),应改为:cvMinS()(xmchenzb)
cvMinS()函数缺少 const CvArr*
mask 参数,但在函数说明中有此参数介绍? (xmchenzb)
cvMul()函数缺少 const CvArr*
mask 参数,但在函数说明中有此参数介绍? (xmchenzb)
表3-9中第三个公式(CV_L2),最后结果需要开算术平方根。(guoming0000)
表3-10中第三个公式(CV_L2),最后结果需要开算术平方根。(guoming0000)
倒数第二行的CvPoint(int x,int y)应该是cvPoint(int x,int y),后者是构造函数。(shanleo1986)
3行 “从磁盘中调出cfg.xml文件”
应是 “向磁盘中写入cfg.xml文件”
(chenxinqiu50)
倒数第14行:cvWrite()编写处色彩转换矩阵,应改为:cvWrite()编写出色彩转换矩阵 (xmchenzb)
倒数18行:“cvWritelnt()向结构中写数据” 应是“cvWriteInt()向结构中写入数据” (shanleo1986)
练习第2题 void cvCircle(CvArr* img, CvPoint center, intradius,CvScalar color,int thickness=1, int line_type=8, int shift=0)
没有 intradius
void cvCircle(CvArr* img, CvPoint center, int radius,CvScalar color,int thickness=1, int line_type=8, int shift=0) (by )
练习第5题第2句话,英文原版为:“Create a 210-by-210 single-channel byte image and zero it.”应该翻译为:“创建一个210X210的单通道字节图像并将其归0”,中文版中少了“字节”二字。(by FlyZhy)
第2段第2句话:“OpenCV中的HighGUI可以分为3部分:硬件相关部分、文件部分以及图形用户界面部分”以及这句的注释中都使用了“文件部分”的说法,应该为:“文件系统部分”。(by FlyZhy)
“创建窗口”小节第一行:“我们使用cvNameWindow()” 应为 “我们使用cvNamedWindow()”.(by )
115页 函数draw_box里面的CvRect rect应该改成CvRect box!(by xssh123)
此言差矣,box在外部已定义,其实形参rect形同虚设,按作者本意,应将函数draw_box中所有box改为rect!!
(by wxf198861)
第1行:“没有显示提供” 应为 “没有显式提供”。(by )
“两个取的滑动条”
应是 “两个状态的滑动条”(chenxinqiu50)
例4-2中,在回调函数switch_callback中调用了2个函数:switch_on_function,switch_off_function,但是根本没有定义过这2个函数。(Winking)
最后一段第2行:“图像必须是8位的单通过或者3个通道”应该为:“图像必须是8位的单通道或者3个通道”。(by FlyZhy)
第12行,"CvReleaseVideoWriter"应该为"cvReleaseVideoWriter" (by deltat)
图5-5“双边滤波结果”重复了前页高斯模糊的图,英文版是双边滤波的图。(by mayitbe)
6行 lim 应为 min(by Enjoysoft)
正文最后两行和141页第一行(图5-10和图5-11之间的正文)与前一段重复,应删除。(by chenxinqiu50 & xmchenzb)
倒数第一行,“例如常用与处理有机组织……”应改为“常用于处理有机组织”
倒数第二行:"大两倍的图像" 应为 “大两个像素的图像”。(by whwhyy@)
图5-19 标题中的
应是 "upDiff"
(chenxinqiu50)
倒2行 "lowDiff"
应是loDiff
(chenxinqiu50)
应是右上方
(chenxinqiu50)
表5-4后第一段第五行:很多z种计算像素值的方法。。。z是来打酱油的吗?(by wxf198861)
8行和16行,IPL_GAUSSIAN_5x5
CV_GAUSSIAN_5x5
(chenxinqiu50)
图5-21 9个图片中中间一行最右面图,旁边的箭头方向指反了(by Enjoysoft)
第二行“高度或宽度为80(2×2×2×5)”是否应该为“高度或宽度为40(2×2×2×5)”(blackhuman)
例5-2:cvThreshold()函数的用法,cvSplit(src,r,g,b,NULL),我认为应该改为cvSplit(src,b,g,r,NULL),在OpenCV中,彩色图像的通道顺序是 BGR (dian)
其“后” 应是其上 (chenxinqiu50)
例5-3上面一句话中"cvADD()"应为"cvAdd()" (guoming0000)
图5-24图中“二进制阈值”应是全局阈值二值图像 “ 自适应二进制阈值”应是
自适应阈值二值图像(chenxinqiu50)
脚注Either xorder or yorder must be nonzero.应译为:xorder和yorder两者中至少有一个非零。
(by mayitbe)
倒数第二段最后一行 “将低分类器识别的性能” 应为 “降低分类器识别的性能”(loop.k)
CV_SHARR,应为 CV_SCHARR(chenxinqiu50)
倒数第一段第二行,根据原作:a somewhat obscure use,“一些”应该是“一个”。 (by mayitbe)
倒数第3行“垂直于远点“,应为“垂直于原点“ (frwen)
图6-11标题中的 “参数1=”和“参数2=”
应分别是 “param1=”和“param2=”
(chenxinqiu50)
图6-11标题中的 "lowThresh = 50, lowThresh = 150",应修改为"lowThresh = 50, highThresh = 150" (shuilaiyu)
表6-1 标题应为cvRemap,cvWarpAffine此时还没出现过
(Enjoysoft)
中间的两个公式重复,上面一个应该去掉
(Enjoysoft)
倒1行“ 标志”应是参数(OpenCV教程是参数)(chenxinqiu50)
文字部分第六行 "分配2x3数组"
应为 "分配一个3x3数组" (loop.k)
例程6-3源代码最后一行的“}”是多余的,应去掉(pobenliu)
3行,图-17, “对数极变换”
对数极坐标变换
“例6-17”应是图6-17(chenxinqiu50)
图6-21标题中 “ 参数1=50,参数2=200”
应是 “ param1=50,param2=200”
(chenxinqiu50)
第14行,“即把它们看成长为……”应改为“即把它们看成为”
函数下方的第一行 “尽管这里下它们是被” 多了一个“下”字
(ailby0709)
倒数第四行 “可以使矩阵” 应为 “可以是矩阵”
应为 "拍摄"
(loop.k)
两图中表述不一致 图6-23右图? (chenxinqiu50)
图6-23左图标应改为:累积高斯分布函数,右图标应改为:高斯分布函数(xmchenzb)
原文:Now create two one-dimensional kernels with anchors in the center. 现译文:现在创建参考点在中心的两个核。应为:创建参考点在中心的两个一维核。
原文:one going “across” (1/4, 2/4, 1/4), and one going down (1/4, 2/4, 1/4). 现译文:一个“交叉”(1/4,2/4,1/4),另一个下降(1/4,2/4,1/4)。应为:一个横向(1/4,2/4,1/4),另一个纵向(1/4,2/4,1/4)。
练习6. “写代码找出眼镜的瞳孔” 应为 “写代码找出眼睛的瞳孔”
第1行:“看到的对字用直方图” 应为 “看到的物体用直方图”
(chenxinqiu50)
第3行,“以及表示目标位置的当前假设(目标当前位置的假设?)”,括号里的是什么意思?(ailby0709)
倒3,4行"rangs" 应是 "ranges" (有两处)
(chenxinqiu50)
1 行 布尔类型变量uniform应是整型变量(219页倒20行, OpenCV教程303页
归一化标志 ,整型变量)(chenxinqiu50)
2,3行 "rangs" 应是 "ranges"
(chenxinqiu50)
最后一个公式,多了两个逗号,并且公式分母中的H2平方的前面缺少了一个sigma.(by enjoysoft)
第2段,出现了“intersection方法”、“chi-square方法”,之前英语术语已经翻译,而且图7-4已有翻译,建议此处翻译为“直方图相交方法”和“卡方方法”.(by enjoysoft)
27行,cvNormalizeHist函数里面的hist[i]无法编译,应该为hist,经过原书实例,没有此行.(by xssh123)
"至于函数cvCalcBackProjectionPatch()"不通,应为"和函数cvCalcBackProjectionPatch()一样" (enjoysoft)
第1/2/3公式都少了T(by frankyhit, franky.)
第4公式第一个T应为T'(by enjoysoft)
倒数第二段不知道是不是原书的错误,“归一化的系数都如下”,实际上,CV_TM_CCOEFF_NORMED的分母并不是Z(x,y),应该另定义一个Z'(x,y)(by enjoysoft)
不知道T'(x',y')的表达式是不是错了,感觉应该是
T'(x',y')-E{T(x,y)}*1/(w*h)
E代表求和符号
"cvMatch Template" 应是 "cvMatchTemplate"
(chenxinqiu50)
第3段,"flags变量"应为"seq_flags变量",参看上面的函数声明
(enjoysoft)
最后一行,"cvClearMemStore"应该为"cvClearMemStorage" (by deltat)
注意部分 "通过图8-1" 应为 "通过图8-2" (loop.k)
正文第三行,漏译了一个分句:cvStartReadChainPoints plays the role of cvStartReadSeq。原文:In this respect, CvChainPtReader is similar to the more general CvSeqReader, and cvStartReadChainPoints plays the role of cvStartReadSeq.拙译:就这一点来说,CvChainPtReader类似于更为通用的CvSeqReader,同时cvStartReadChainPoints发挥了cvStartReadSeq的作用。 (by mayitbe)
正文倒数第10行 "它不像乍看上去" 应为 "它不像看上去" (loop.k)
倒数第8行"此变量可以被设置为便利轮廓的最大深度"中"便利"应该为"遍历" (by deltat)
倒数第12行 ,函数cvCreateTrackbar函数中少参数,应该加入bar中最大值,否则无法编译.(by xssh123)
第 9 行,"如果设置的max_depth" 应该为 max_level
例8_3 中 for 循环下的 cvCvtColor(img_8uc1, img_8uc3, CV_GRAY2BGR); 应移到for 循环上面,否则没效果 (by zcube)
代码块下方第一句,"数状序列"改为"树状序列";对不起,我看不懂这一句,仅仅在此做个标记。(kingkong)
倒数第11行 ,"如果想了这个参数..."应该为"如果想了解这个参数..."
第三行“同于计算轮廓面积”
应为 “用于计算轮廓面积”
(by zcube)
倒数第7行 ,"CvBoundingRech"应该为"CvBoundingRect" (by deltat)
正文第3行 ,"CvBoundingReck"应该为"CvBoundingRect" (by deltat)
倒数第10行 "图8-7: CvRect只能表示一个方正的正方形,但CvBox2D可以表示任何倾斜度的正方形"应为
"图8-7: CvRect只能表示一个方正的长方形,但CvBox2D可以表示任何倾斜度的长方形" (by deltat)
第6行"并且演示了如果..."应该为""并且演示了如何..." (by deltat)
第11行"比较两个个..."应该为""比较两个..."
(by deltat)
页底 cvContoursMoments并不存在,应为cvContourMoments,就是cvMoments的一个宏 (by enjoysoft)
第14行的公式
mp,q 中的p,q 应为下标
第11行、第20行cvContoursMoments都应为cvContourMoments (by deltat)
倒数第1行,cvDrawContour应该为"cvDrawContours"
(by deltat)
第一行,去掉"使用"。(kingkong)
倒数第七行 "中心距"应为"中心矩" (by deltat)
第1行、第5行 "中心距"应为"中心矩" (by deltat)
表8-1 第二行 '1' 应为 'I'(by mikeyang)
第16行 "cvMatchShape"应为"cvMatchShapes" (by deltat)
倒2行, "树节电"
"树节点" (chenxinqiu50)
倒数6行"returnpoints"
应是 "return_points" (chenxinqiu50)
b上图(chenxinqiu50)
2行 CV_NEXT_LINT_POINT()函数?(chenxinqiu50)
正文第3行“frameTeim1”应改为“frameTime1”。(by shanleo1986)
倒数第五行应为 cvSubRS(Imask, cvScalar(255), Imask); (by zcube)
void cvAcc(
const Cvrr *image, ...);的Cvrr应改为CvArr。(by shanleo1986)
倒数4行"blob"
(chenxinqiu50)
第4行“跟踪在mumEries”改为“跟踪在numEries”。(by shanleo1986)
正数18行-19行:
“对于每个通道,最大值和最小值就是codebook的分界线”中的最大值应改为“max”,最小值应为“min”
20行:“如果一个像素值的每个通道”==&“对于一个新的像素,如果各通道”
21-22行:“距离上次更新和陈旧的时间用于删除过程中学习的很少使用的码本条目”==》“时间参数t_last_update和stale用于删除在学习过程中创建但很少使用的码本元素”
倒数第一段,即“背景学习”标题下
该段第一句应为“我们为每个像素分配一个有一系列码元组成的码本”
第二句应为“我们需要一个与正在学习的图像像素数目等长的码本阵列”
题目“用于前景清除的连通部分”
把“清除”改成“清理”或“净化” 更恰当点 (by zcube)
第二段有个英文"mean-shoft"应为“mean-shift”(enjoysoft )
第二段有个英文"mean-shoft"应为“mean-shift”(enjoysoft )
cvInitSubdiv Delaunay2D()改为 cvInitSubdivDelaunay2D(),是一个函数,中间没有空格。(by shanleo1986)
cvCalcSubdiv Voronoi2D()改为 cvCalcSubdivVoronoi2D(),是一个函数,中间没有空格。(by shanleo1986)
倒数第5行,"CV_TERMCRIT_ITER"类型名重复,第二个参数类型应为"CV_TERMCRIT_EPS". ( Yishi Guo )
第1行,“点集”应为“点积”
14行“是由cvGoodFeatures()得到的位置坐标”改为“是由cvGoodFeaturesToTrack()得到的位置坐标”。(by shanleo1986)
图10-4的解释最后一句"(图由Michael Black[Black82]提供)",但在后面的参考文献中并无Black82,貌似应为Black92. ( Yishi Guo )
倒数第3行,"建立邻域像素的系统方程"应为"建立邻域像素的方程组"
此处英文原文为"...to set up a system of equations.",这里的"system of equation"应为"方程组"的意思.(Yishi Guo)
倒数7行"winSize"应是win_Size(chenxinqiu50)
倒数7行,"featuresA 与featuresB 相似"
"featuresB与featuresA 相似" (chenxinqiu50)
倒数第6行,"win_size"在此应为"winSize",和上文函数中的参数名对应.(Yishi Guo)
倒数第11行,“数组B”个人认为应该改为“数组featuresB”。因为参数中给的是featuresB。(bu shanleo1986,)
第一部分倒数第3行,"什么是Lagrange乘子?",其实翻译成"什么是拉格朗日乘子?"更好.( Yishi Guo )
倒数第5行,"camshaift"应为"camshift" (by deltat)
英文版中第340页的倒数第二段部分内容未翻译.( Yishi Guo )
正文倒数第5行最后一句:"这样就大大地简化了这个方法的计算机实现"翻译有误.
英文版原文为:"This greatly simplifies the computational implications of this method.",此处的"implications"并不是"实现"的意思而是"含义"的意思."实现"应为"implement". ( Yishi Guo )
注释1最后一句,改为“给复杂的方法”。(kingkong)
注释1最后一句,“则需要比单一的Kalman滤波器更有好的方法” 应为 “那需要比单一的Kalman滤波器更复杂的方法” (by )
最后一段第一句,bad translation。(kingkong)
此页少一个注释.即英文版第356页的第一个注释. ( Yishi Guo )
H矩阵应该为:
0](gawing)
例10-2代码的"#define phi2xy(mat)"部分中, "/"斜杠应为"\"反斜杠. ( Yishi Guo )
页面下方的代码第6行多空格,cvR andSetRange应该为 cvRandSetRange. ( frostmourn )
练习2题目中 “参考图10-12”应为 “参考图10-2”。
子题目中 a. “在图10-12中” 也应为 “在图10-2中”。
页图11-2后面的注释"q=(z,y,f)"是否错误?是否应为“q=(x,y,f)”?我看了下英文电子文档也是写"q=(z,y,f)"!(fenggee008)
正文最后一个公式,少等号“=”(pobenliu)
正文前两个公式,少等号“=”
(pobenliu)
最后一行,“R = Rz(θ), Ry(φ), Rx(ψ)”两个逗号“,”应为乘号“×”。参考P414正文最后一行“……总的旋转矩阵R是三个矩阵 Rz(θ), Ry(φ)和Rx(ψ)的乘积……”
(pobenliu)
正文第一行“ RRT=I”格式有误,T为第二个R的上标
(pobneliu)
正文第三行“第一个坐标是原点”应为“第一个坐标系原点”
(pobenliu)
图11-9下方正文第一行,“必须是8位灰度(单通道)”,应该改为“必须是8位的灰度或者彩色图像”
wiki出处:
正文第四行“通过乘以WQ,得到摄像机矩阵M”翻译有误,原文为“the action of the camera matrix M is multiplied by WQ”,翻译为“将摄像机的移动矩阵M与WQ相乘” (pobenliu)
正文第九行“(即R=[r1,r2,r3]”,少了一个匹配的“)”,也即“(即R=[r1,r2,r3])”(pobenliu)
中间的公式,由B11、B12、B22、B13、B23、B33组成的列向量不应再转置了,应该将转置去掉
倒数10,12行"jacobin","jabobian", 皆应是 "jacobian" (chenxinqiu50)
"cvFindExtrinsicCameraParameters2() " 应是 "cvFindExtrinsicCameraParams2()"
(chenxinqiu50)
438页 练习题第一题 :应该是y=fy*(Y/Z)+Cy; 原文将其写成了y-fy*(Y/Z)+Cy;( by
脚注2的最后一个公式,Z=fT... 其中T不应为下标,
(enjoysoft)
图12-4的说明中的O1,O2应该改为Ol和Or。 (Djnwpu)
最后一行,plel应该为plel,l为下标。(kingkong)
注释2行"H定义"?(chenxinqiu50)
倒数第二个公式,S=【...】缺少一个等号 (enjoysoft)
正文第13行,RT=R-1,T应为上标。 (enjoysoft)
第二个公式,将P改为p,应为。(kingkong)
"基础矩阵数学"第四句,bad translation。(kingkong)
正文第7行,“极线方程”应为“ax+by+c=0”。 (enjoysoft)
第1个公式,Rl括号外应有一个转置符号 (enjoysoft)
第1个公式中的T是转置符号
倒数9行 "distCoeffs5" 应是 "distCoeffs2" (chenxinqiu50)
正文第7行:“但是由于可选择的前向平行平面个数是有限的,...”中,‘有限的’一词翻译错误,英文原文是‘infinite’,是“无限”的意思。(goode)
图12-10下方第1项中有两个关系式,第二个改为。(kingkong)
第五行,下标错误,应为rl和rr。(kingkong)
倒数第2个公式缺少等号 (enjoysoft)
前2个公式缺少等号 (enjoysoft)
重投影矩阵Q与Learning OpenCV(435页)不同,最后一个元素应为 。(hslaiyaya)
第三行,下标错误,应为和、xl和xr。(kingkong)
“d=xl-xr(cxleft - cxright)” 应为 “d=xl-xr-(cxleft - cxright)” (enjoysoft)
倒数7,8,19,20,21,23行"cvRandint","cvRandReal",应是cvReadint,cvReadReal(chenxinqiu50)
倒数10行cvLine()7个参数,91页cvLine()6个参数(chenxinqiu50)
老版本OpenCV只有6个参数,新版本有7个参数 ( 15:31 日 (CST))
倒数第一段,两种有监督的学习。建议,在“回归”后面加上“(regression)”,与前面“分类(classification)”相对应。(kingkong)
倒数第2行,“左侧自节点”==》“左侧子节点”(by lymarzzh@OpenCV China)
EM,分科-&分割。(kingkong)
续表的第1行,“递归”==》“回归”(by lymarzzh@OpenCV China)
注释:N行 替换为 N列,K列 替换为 K行。(by YenTe)
1行:"返回的参数 responses 可以是类别标签" 应为 "参数 responses 可以是类别标签"
2行:"返回值常常是一个" ==& "responses 参数通常是一个"
4行: "返回值有两种类型" ==& "responses 参数有两种类型"
本段下面的部分翻译也比较混乱,建议参考英文原版。(by Adintr)
第9行,“特征之前相互独立”==》“特征之间相互独立”(by lymarzzh@OpenCV China)
“物体”==》“目标”似乎更合适(by lymarzzh@OpenCV China)
最后一段“训练决策树的方法如下,... 使用)。” 替换为:
“用来训练决策树的方法模板如下。有两种方法:第一个用于决策树的直接使用;第二个用于整体学习(如boosting)或随机森林(如随机树)。”
(by lymarzzh@OpenCV China)
正文第6行,
“有一个叫统计提升(meta-learning)的学习算法(由” 改为 “有一种称作统计提升(statistical boosting)的元学习(meta-learning)算法(由”
(by lymarzzh@OpenCV China)
脚注1第2句,boosting 和随机数可以获得很好的性能 ==& boosting 和随机树可以获得很好的性能(by adintr)
第6行,“外围数据”==》“异常数据”
(by lymarzzh@OpenCV China)
正文倒数 第4 行: “1/m,i=1,...,m ”中 的m,应该 为M
正文 倒数第1 行 中的 "m" 应为 "M"
(by lymarzzh@OpenCV China)
正文倒数第5行: “更加关注与前面的分类器...”中的'与‘,应该为’于‘
(by caotangwa)
正文第2行: “αt=log...”应该为“αt=1/2log..."
(by caotangwa)
正文倒数7行,"非物体",“物体”==&“非目标”,“目标”
(by lymarzzh@OpenCV China)
正文倒数第1行,文中”CvBooatTree的声明“应该为“CvBoostTree的声明”
(by caotangwa@OpenCV China)
正文倒数 第2 行, “OpenCV包含随机森林(random trees)类”==》“OpenCV包含随机树(random trees)类”
正 文 倒数第3行Random Trees 应该 翻译 为 “ 随机树 ”
(by lymarzzh@OpenCV China)
正文第2自然段的倒数第三行,“OOB错误”==&"OOB误差"
(by lymarzzh@OpenCV China)
正文第二段:第一句话“...每个节点中中...",‘中’重复了,应该删去。(by caotangwa@OpenCV China)
正文第二段:第一句话“如前所述....的物体"重复了,整句应该删去。
(by caotangwa@OpenCV China)
正文11行,“分等级的类脑网络的最新工作”==》“可进行概率传播的分层类脑网络的最新工作”
(by lymarzzh@OpenCV China)
[]586页~598页
望与中文版页码对应,将方便不少!有劳。(by mayitbe)
Personal tools
本网站由赞助主机和网络。OPENCV 中的 cvsubdiv的数据结构是怎么回事?_百度知道12650人阅读
数据挖掘和图像处理(26)
本博客转载自 &请其他同学转载时注明原始文章的出处!
Delaunay三角剖分是1934年发明的将空间点连接为三角形,使得所有三角形中最小角最大的一个技术。
如果你熟悉计算机图形学,你便会知道Delaunay三角剖分是变现三维形状的基础。如果我们在三维空间渲染一个,我们可以通过这个物体的投影来建立二维视觉图,并用二维Delaunay三角剖分来分析识别该物体,或者将它与实物相比较。Delaunay剖分是连接计算机视觉与计算机图形学的桥梁。然而使用OpenCV实现三角剖分的不足之处就是OpenCV只实现了二维的Delaunay剖分。如果我们能够对三维点进行三角剖分,也就是说构成立体视觉,那么我们可以在三维的计算机图形和计算机视觉进行无缝的转换。然而二维三角剖分通常用于计算机视觉中标记空间目标的特征或运动场景跟踪,目标识别,或两个不同的摄像机的场景匹配(如图从立体图像中获得深度信息)。
其实真正的文章来源是:
1.&三角剖分与Delaunay剖分的定义
&&&&&如何把一个散点集合剖分成不均匀的三角形网格,这就是散点集的三角剖分问题,散点集的三角剖分,对数值分析以及图形学来说,都是极为重要的一项预处理技术。该问题图示如下:
&1.1.三角剖分定义
&&&&【定义】三角剖分:假设V是二维实数域上的有限点集,边e是由点集中的点作为端点构成的封闭线段, E为e的集合。那么该点集V的一个三角剖分T=(V,E)是一个平面图G,该平面图满足条件:
&&&&1.除了端点,平面图中的边不包含点集中的任何点。
&&&&2.没有相交边。
&&&&3.平面图中所有的面都是三角面,且所有三角面的合集是散点集V的凸包。
1.2.& Delaunay三角剖分的定义
&&&&在实际中运用的最多的三角剖分是Delaunay三角剖分,它是一种特殊的三角剖分。先从Delaunay边说起:
&&&&【定义】Delaunay边:假设E中的一条边e(两个端点为a,b),e若满足下列条件,则称之为Delaunay边:存在一个圆经过a,b两点,圆内(注意是圆内,圆上最多三点共圆)不含点集V中任何其他的点,这一特性又称空圆特性。
&&&&【定义】Delaunay三角剖分:如果点集V的一个三角剖分T只包含Delaunay边,那么该三角剖分称为Delaunay三角剖分。
1.3.Delaunay三角剖分的准则
&&&&要满足Delaunay三角剖分的定义,必须符合两个重要的准则:
&&&&1、空圆特性:Delaunay三角网是唯一的(任意四点不能共圆),在Delaunay三角形网中任一三角形的外接圆范围内不会有其它点存在。如下图所示:
&&&&2、最大化最小角特性:在散点集可能形成的三角剖分中,Delaunay三角剖分所形成的三角形的最小角最大。从这个意义上讲,Delaunay三角网是“最接近于规则化的“的三角网。具体的说是指在两个相邻的三角形构成凸四边形的对角线,在相互交换后,六个内角的最小角不再增大。如下图所示:
1.4.Delaunay三角剖分的特性
&&&&以下是Delaunay剖分所具备的优异特性:
&&&&1.最接近:以最近临的三点形成三角形,且各线段(三角形的边)皆不相交。
&&&&2.唯一性:不论从区域何处开始构建,最终都将得到一致的结果。
&&&&3.最优性:任意两个相邻三角形形成的凸四边形的对角线如果可以互换的话,那么两个三角形六个内角中最小的角度不会变大。
&&&&4.最规则:如果将三角网中的每个三角形的最小角进行升序排列,则Delaunay三角网的排列得到的数值最大。
&&&&5.区域性:新增、删除、移动某一个顶点时只会影响临近的三角形。
&&&&6.具有凸多边形的外壳:三角网最外层的边界形成一个凸多边形的外壳。
1.5.局部最优化处理
&&&&理论上为了构造Delaunay三角网,Lawson提出的局部优化过程LOP(Local Optimization Procedure),一般三角网经过LOP处理,即可确保成为Delaunay三角网,其基本做法如下所示:
&&&&1.将两个具有共同边的三角形合成一个多边形。
&&&&2.以最大空圆准则作检查,看其第四个顶点是否在三角形的外接圆之内。
&&&&3.如果在,修正对角线即将对角线对调,即完成局部优化过程的处理。
&&&&LOP处理过程如下图所示:
2.Delaunay剖分的算法
&&&&Delaunay剖分是一种三角剖分的标准,实现它有多种算法。
2.1.Lawson算法
&&&&逐点插入的Lawson算法是Lawson在1977年提出的,该算法思路简单,易于编程实现。基本原理为:首先建立一个大的三角形或多边形,把所有数据点包围起来,向其中插入一点,该点与包含它的三角形三个顶点相连,形成三个新的三角形,然后逐个对它们进行空外接圆检测,同时用Lawson设计的局部优化过程LOP进行优化,即通过交换对角线的方法来保证所形成的三角网为Delaunay三角网。
&&&&上述基于散点的构网算法理论严密、唯一性好,网格满足空圆特性,较为理想。由其逐点插入的构网过程可知,遇到非Delaunay边时,通过删除调整,可以构造形成新的Delaunay边。在完成构网后,增加新点时,无需对所有的点进行重新构网,只需对新点的影响三角形范围进行局部联网,且局部联网的方法简单易行。同样,点的删除、移动也可快速动态地进行。但在实际应用当中,这种构网算法当点集较大时构网速度也较慢,如果点集范围是非凸区域或者存在内环,则会产生非法三角形。
2.2.Bowyer-Watson算法&
&&&&Lawson算法的基本步骤是:
&&&&1、构造一个超级三角形,包含所有散点,放入三角形链表。
&&&&2、将点集中的散点依次插入,在三角形链表中找出其外接圆包含插入点的三角形(称为该点的影响三角形),删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来,从而完成一个点在Delaunay三角形链表中的插入。
&&&&3、根据优化准则对局部新形成的三角形进行优化。将形成的三角形放入Delaunay三角形链表。
&&&&4、循环执行上述第2步,直到所有散点插入完毕。
&&&&这一算法的关键的第2步图示如下:
3、OpenCV中的Delaunay和Voronoi细分
学习这部分,也是一个头疼的问题,要理解需要较好的数据结构作为基础。由于自己对数据结构也是敬畏三分,所以下面理解不免有误。
OpenCV中现实的Delaunay三角剖分应该是Bowyer-Watson算法。
3.1创建一个Delaunay或Voronoi细分。
我们需要存储Delaunay的内存空间和一个外接矩形(该矩形盒子用来确定虚拟三角形)
// STORAGE AND STRUCTURE FOR DELAUNAY SUBDIVISION //存储和结构 for三角剖分
CvRect rect = { 0, 0, 600, 600 };
//Our outer bounding box //我们的外接边界盒子
CvMemStorage*
//Storage for the Delaunay subdivsion //用来存储三角剖分
storage = cvCreateMemStorage(0);
//Initialize the storage //初始化存储器
CvSubdiv2D* //The subdivision itself // 细分
subdiv = init_delaunay( storage, rect);
//See this function below //函数返回CvSubdiv类型指针
init_delaunay函数如下,它是一个OpenCV函数,是一个包含一些OpenCV函数的函数包。
//INITIALIZATION CONVENIENCE FUNCTION FOR DELAUNAY SUBDIVISION //为三角剖分初始化便利函数
CvSubdiv2D* init_delaunay(CvMemStorage* storage,CvRect rect) {
CvSubdiv2D*
subdiv = cvCreateSubdiv2D(CV_SEQ_KIND_SUBDIV2D,sizeof(*subdiv),sizeof(CvSubdiv2DPoint),sizeof(CvQuadEdge2D),storage);//为数据申请空间
cvInitSubdivDelaunay2D( subdiv, rect ); //rect sets the bounds
//返回申请空间的指针
我们知道三角剖分是对散点集进行处理的,我们知道了散点集就可以获得点集的三角剖分。如何传入(插入)散点集呢?
这些点必须是32位浮点型,并通过下面的方式插入点:
CvPoint2D32 //This is our point holder//这是我们点的持有者(容器)
for( i = 0; i & as_many_points_as_you_ i++ ) {
// However you want to set points //如果我们的点集不是32位的,在这里我们将其转为CvPoint2D32f,如下两种方法。
fp = your_32f_point_list[i];
cvSubdivDelaunay2DInsert( subdiv, fp );
转换为CvPoint2D32f的两种方法:
1)通过宏cvPoint2D32f(double x,double y)
2)通过cxtype.h下的cvPointTo32f(CvPoint point)函数将整形点方便的转换为32位浮点型。
当可以通过输入点(散点集)得到Delaunay三角剖分后,接下来,我们用一下两个函数设置和清除相关的Voronoi划分:
cvCalcSubdivVoronoi2D( subdiv ); // Fill out Voronoi data in subdiv //在subdiv中填充Vornoi的数据
cvClearSubdivVoronoi2D( subdiv ); // Clear the Voronoi from subdiv//从subdiv中清除Voronoi的数据
CvSubdiv2D结构如下:
#define CV_SUBDIV2D_FIELDS() \
CV_GRAPH_FIELDS() \
int quad_ \
int is_geometry_ \
CvSubdiv2DEdge recent_ \
CvPoint2D32 \
CvPoint2D32
typedef struct CvSubdiv2D
CV_SUBDIV2D_FIELDS()
CvSubdiv2D;
平面划分是将一个平面分割为一组不重叠的、能够覆盖整个平面的区域。结构CvSubdiv2D描述了建立在二维点集上的划分结构,其中点集互相连接且构成平面图形,该图形通过结合一些无线连接外部划分点(称为凸形点)的边缘,将一个平面用按照其边缘划分成很多小区域。
对于每一个划分操作,都有一个对偶划分与之对应。对偶的意思是小区域与点(划分的顶点)变换角色,即在对偶划分中,小区域被当做一个顶点(以下称为虚拟点)而原始的划分顶点被当做小区域。如下图所示,原始的划分用实线表示,而对偶划分用虚线表示。
OpenCV使用Delaunay算法将平面分割成小的三角形区域(该三角形确保包括所有的分割点)开始不断迭代完成。在这种情况下,对偶划分就是输入的二维点集的Voronoi图表。这种划分可以用于对一个平面进行三维分段变换、形态变换、平面点的快速 定位以及建立特定的图结构(如NNG,RNG)。
CvQuadEdge2D
CvQuadEdge2D结构包含了两个Delaunay点和两个Vorionoi点以及连接它们的边缘(假设Voronoi点和边缘已经由函数计算出来,通过上面的函数:cvCalSubdivVoronoi2D(subdiv))。
CvQuadEdge2D定义平面划分中的Quad-edge(四方边缘结构),其结构如下:
one of edges within quad-edge, lower 2 bits is index (0..3)
and upper bits are quad-edge pointer
typedef long CvSubdiv2DE
//四方边缘结构中的一条边缘,低两位表示该边缘的索引号,其他高位表示边缘指针。
/*quad-edge structure fields*/四方边缘的结构场
#define CV_QUADEDGE2D_FIELDS() \
struct CvSubdiv2DPoint*pt[4]; \
CvSubdiv2DEdge next[4];
typedef struct CvQuadEdge2D
CV_QUADEDGE2D_FIELDS()
CvQuadEdge2D;
四方边缘结构是平面划分的基元,其中包括四条边缘(e,eRot,以及他们的反方向),如下图所示:
CvSubdiv2DPoint
CvSubdiv2DPoint结构包含Delaunay边缘及其相连的顶点。
其结构定义如下:
#define CV_SUBDIV2D_POINT_FIELDS()\
CvSubdiv2DE \
CvPoint2D32 \
//This integer can be used to index auxillary data asscoiated with each vertex of the planar subdivison.
#define CV_SUBDIV2D_VIRTUAL_POINT_FLAG (1 && 30)
typedef struct CvSubdiv2DPoint
CV_SUBDIV2D_POINT_FIELDS()
CvSubdiv2DP
边缘的遍历
Subdiv2DRotateEdge函数
功能:函数Subdiv2DRotateEdge根据输入的边缘返回四方边缘结构中的一条边缘。
格式:
CvSubdiv2DEdge cvSubdiv2DRotateEdge(CvSubdiv2DEdge edge,int rotate );
edge划分的边缘(不是四方边缘结构,即不是CvQuadEdge2D)
rotate 确定函数根据输入的边缘返回同一四方边缘结构中的哪条边缘,为下列值之一:
1)0 输入边缘(如果e是输入边缘,则为e)。
2)1 旋转比那样(eRot)
3)2 逆边缘(e的反向边缘)。
4)3旋转比那样的反向边缘(eRot的反向边缘)。
cvSubdiv2DGetEdge函数
使用该函数我们可以遍历Delaunay图。该函数返回与输入边缘相关的边缘。
格式:
CvSubdiv2DEdge cvSubdiv2DGetEdge( CvSubdiv2DEdge edge, CvNextEdgeType type );
edge 划分的边缘(不是四方边缘结构)
type 确定函数返回哪条相关边缘,为下列值之一:
CV_NEXT_AROUND_ORG &边缘原点的下一条(eOnext,如果e是输入边)。
CV_NEXT_AROUND_DST 边缘顶点的下一条(eDnext)
CV_PREV_AROUND_ORG 边缘原点的前一条(eRnext的反向)
CV_PREV_AROUND_DST边缘终点的前一条(eLnext的反向)
CV_NEXT_AROUND_LEFT 左区域的下一条(eLnext) &或下一个左平面
CV_NEXT_AROUND_RIGHT 右区域的下一条(eRnext) 或下一个右平面
CV_PREV_AROUND_LEFT 左区域的前一条(eOnext的反向)或前一个左平面
CV_PREV_AROUND_RIGHT 右区域的前一条(eDnext的反向)或前一个右平面
来至边缘的点
我们可以通过下面的两个函数获得Delaunay或者Voronoi边缘的两个实际点:
CvSubdiv2DPoint* cvSubdiv2DEdgeOrg( CvSubdiv2DEdge edge );
CvSubdiv2DPoint* cvSubdiv2DEdgeDst( CvSubdiv2DEdge edge );
下面是将CvSubdiv2DPoint点转换为更熟悉的点CvPoint2D32f 或者CvPoint:
CvSubdiv2DPoint ptS
//Subdivision vertex point
CvPoint2D32f pt32f = ptSub-&
// to 32f point
CvPoint pt = cvPointFrom32f(pt32f);
// to an integer point
如何从Delaunay/Voronoi细分得到第一条边或点?
有两种方法:1)使用一个外部点定位边缘或顶点 2)遍历一系列点或边缘
方法一:使用外部点定位边缘或顶点
第一种方法是任取一点,然后在细分中定位该点。该点不一定是三角剖分中点,而可以为任意点。
cvSubdiv2DLocate()函数填充三角形的边缘和顶点(如果必要)或者填充该点所在的Voronoi面,函数的声明如下:
CvSubdiv2DPointLocation cvSubdiv2DLocate(
CvSubdiv2D*
CvPoint2D32f
CvSubdiv2DEdge* edge,//要填充的边缘
CvSubdiv2DPoint** vertex = NULL//如果需要,则填充顶点
请注意,这些不必是最接近的边缘或顶点,它们只需要在三角形上。此函数的返回值按下列的方式说明点的位置:
1)CV_PTLOC_INSIDE 点落入某些面;*edge将包含该面的一个边缘。
2)CV_PTLOC_ON_ENCODE 点落于边缘;*edge含有这个边缘。
3)CV_PTLOC_VERTEX 该点与一个细分顶点重合;*vertex将包含该顶点的指针。
4)CV_PTLOC_OUTSIDE_RECT 该点处于细分参考矩形之外;该函数返回后不填充指针。
5)CV_PTLOC_ERROR 输入变量无效。
方法二:遍历一系列点或边缘
外接三角形(虚拟)的三个顶点和三个边的获取:
1)首先我们要有一个建立的Delaunay细分。
2)我们还需要调用cvCalcSubdivVoronoi2D(subdiv)函数计算相关的Voronoi划分。
3)然后我们就能用CvSubdiv2DPoint *outer_vtx[3]和CvQuadEdge2D*outer_gedges[3]来存储三个顶点和三条边。
CvSubdiv2DPoint* outer_vtx[3];
for( i = 0; i & 3; i++ ) {
outer_vtx[i] =
(CvSubdiv2DPoint*)cvGetSeqElem( (CvSeq*)subdiv, I );
CvQuadEdge2D* outer_ qedges[3];
for( i = 0; i & 3; i++ ) {
outer_qedges[i] =
(CvQuadEdge2D*)cvGetSeqElem( (CvSeq*)(my_subdiv-&edges), I );
确定凸包的外接三角形(Bounding triangle)或边缘并遍历凸包
我们回想一下,我们通过调用cvInitSubdivDelaunay2D(subdiv,rect)来初始化Delaunay三角剖分。在这种情况下,下面的论述成立:
1、如果边缘的起点和终点都在矩形之外,那么此边缘也在细分的外接三角形上。(即如果一个边缘的两个点出了矩形边界,那么该边缘为虚拟三角形的边缘)
2、如果边缘的一端在矩形内,一段在矩形外,那么矩形边界内的点落在凸集上,凸集上的每个点与虚拟外接三角形的两顶点相连,这两边相继出现。
(英语原文:If you are on an edge with one point inside and one point outside the rect bounds,then the point in bounds is on the convex hull of the set。。。)
(注释:Learning OpenCV中第343页,将In bounds翻译为在边界上,这里个人感觉应该是在翻译为在边界内,为了方便理解,个人猜测如下图,可能不对:)
从上述第二个条件可知,我们可以使用cvSubdiv2DNextEdge(),移动到第一条边上,这条边的dst端点在边界内。
两个端点都在边界内的这条边在点集的凸包上,因此记下那个点和边。一旦它在凸包上,那么你可以如下遍历所有顶点。(切记参考英文原版,我感觉下面的步骤应该参考具体操作理解。)
1·、将凸包遍历一周后,通过cvSubdiv2DRotateEdge(CvSubdiv2DEdge edge,0)函数移动到凸包的下一条边。
2、接着,两次调用宏cvSubdiv2DNextEdge()就到了凸包的下一条边。跳转到第一步。
使用实例:
1、使用函数cvSubdiv2DLoacate()遍历Delaunay三角剖分的边。
2、使用函数cvFindNearestPoint2D()函数找到距离输入最近的点。
3、使用函数draw_subdiv_facet()函数逐步遍历Voronoi面,这个函数是组合函数,实现参考OpenCV的344页。
4、使用CvSeqReader逐步遍历Delaunay或者Voronoi边。
5、找到了三角剖分的所有顶点,我们就可以利用内敛宏cvTriangleArea()函数来计算剖分的面积。
三角剖分例程序:
在OpenCV的sample的c目录下:delaunay.c文件
代码理解参考图:
#include &opencv2/imgproc/imgproc_c.h&
#include &opencv2/legacy/legacy.hpp&
#include &opencv2/highgui/highgui.hpp&
#include&opencv2\opencv.hpp&
#include&iostream&
#include &stdio.h&
static void help( void )
printf(&\nThis program demostrates iterative construction of\n&//这个程序阐述了delaunay剖分和voronoi细分的迭代构造
&delaunay triangulation and voronoi tesselation.\n&
&It draws a random set of points in an image and then delaunay triangulates them.\n&//在图像上画出一些随机点,然后进行delaunay三角剖分
&Usage: \n&
&./delaunay \n&
&\nThis program builds the traingulation interactively, you may stop this process by\n&
&hitting any key.\n&);//迭代构造三角剖分,如果像停止,则按任意键
static CvSubdiv2D* init_delaunay( CvMemStorage* storage,//初始化三角剖分结构,为其分配单元
CvRect rect )
CvSubdiv2D*//三角剖分的数据单元
subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*subdiv),
sizeof(CvSubdiv2DPoint),
sizeof(CvQuadEdge2D),
storage );
cvInitSubdivDelaunay2D( subdiv, rect );
static void draw_subdiv_point( IplImage* img, CvPoint2D32f fp, CvScalar color )//画出三角剖分的顶点
cvCircle( img, cvPoint(cvRound(fp.x), cvRound(fp.y)),5, color, CV_FILLED, 8, 0 );
static void draw_subdiv_edge( IplImage* img, CvSubdiv2DEdge edge, CvScalar color )//画出三角剖分的边
CvSubdiv2DPoint* org_//源顶点
CvSubdiv2DPoint* dst_//目地顶点
CvPoint2D32
CvPoint2D32
CvPoint iorg,
org_pt = cvSubdiv2DEdgeOrg(edge);//通过边获取顶点
dst_pt = cvSubdiv2DEdgeDst(edge);
if( org_pt && dst_pt )//如果两个端点不为空
org = org_pt-&
dst = dst_pt-&
iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
cvLine( img, iorg, idst, color, 1, CV_AA, 0 );
static void draw_subdiv( IplImage* img, CvSubdiv2D* subdiv,
CvScalar delaunay_color, CvScalar voronoi_color
)//画出剖分和细分
int i, total = subdiv-&edges-&//边的数量
int elem_size = subdiv-&edges-&elem_//边的大小
cout&&typeid(subdiv-&edges).name()&&
cvStartReadSeq( (CvSeq*)(subdiv-&edges), &reader, 0 );//使用CvSeqReader遍历Delaunay或者Voronoi边
for( i = 0; i & i++ )
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
if( CV_IS_SET_ELEM( edge ))
// draw_subdiv_edge( img, (CvSubdiv2DEdge)edge + 1, voronoi_color );
draw_subdiv_edge( img, (CvSubdiv2DEdge)edge, delaunay_color );
CV_NEXT_SEQ_ELEM( elem_size, reader );
static void locate_point( CvSubdiv2D* subdiv, CvPoint2D32f fp, IplImage* img,//遍历三角剖分的边
CvScalar active_color )
CvSubdiv2DE
CvSubdiv2DEdge e0 = 0;
CvSubdiv2DPoint* p = 0;
cvSubdiv2DLocate( subdiv, fp, &e0, &p );
draw_subdiv_edge( img, e, active_color );
e = cvSubdiv2DGetEdge(e,CV_NEXT_AROUND_LEFT);
while( e != e0 );
draw_subdiv_point( img, fp, active_color );
//@author andme-单目视觉
void dashLine(Mat &img, Point2d& pt1, Point2d& pt2, int n)//n为虚线段数
Point sub = pt2 - pt1;
for (int i = 0; i & 2*n; i += 2)
line(img, Point(pt1.x + sub.x * i / (2 * n - 1), pt1.y + sub.y * i / (2 * n - 1)), Point(pt1.x + sub.x * (i+1) / (2 * n - 1), pt1.y + sub.y * (i+1) / (2 * n - 1)), Scalar(0,255,0), 2);
//调用形式draw_subdiv_facet( img, cvSubdiv2DRotateEdge( e, 1 ));
static void draw_subdiv_facet( IplImage* img, CvSubdiv2DEdge edge )//画出voronoi面
//cout&&edge&&//edge低两位表示表示索引,高位表示四方边缘指针。
//cout&&(edge&3)&&
CvSubdiv2DEdge t =//当我们按上面的调用形式时,edge为eRot。
int i, count = 0;
CvPoint* buf = 0;
Point2d *buf1=0;
// count number of edges in facet //面内边的计数
t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );
} while (t != edge );//我们绕着一个voronoi单元一周,遍历该vornonoi边缘所拥有的边缘数。
buf = (CvPoint*)malloc( count * sizeof(buf[0]));
buf1=(Point2d*)malloc(count*sizeof(buf1[0]));
// gather points
for( i = 0; i & i++ )
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeOrg( t );//第一次获取eRot边缘的起始点
if( !pt )//如果得不到该源点,则退出循环
buf[i] = cvPoint( cvRound(pt-&pt.x), cvRound(pt-&pt.y));//将该点转换为cvPoint类型点,存储在buf中
t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );//然后绕着vornonoi单元,左旋转。
if( i == count )//如果所有的点都存储起来了。
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeDst( cvSubdiv2DRotateEdge( edge, 1 ));//这里eRot的旋转边缘应该是reversed e,那么目的点,就是e的源点。
// cvFillConvexPoly( img, buf, count, CV_RGB(rand()&255,rand()&255,rand()&255), CV_AA, 0 );//填充凸多边形
for(i=0;i&i++)
buf1[i].x=buf[i].x;
buf1[i].y=buf[i].y;
Mat mat_img(img);
cvPolyLine( img, &buf, &count, 1, 1, CV_RGB(0,200,0), 1, CV_AA, 0);//画出线。
//for(int i=0;i&count-1;i++)
//dashLine(mat_img,buf1[i],buf1[i+1],100);
//dashLine(mat_img,buf1[i],buf1[0],100);
draw_subdiv_point( img, pt-&pt, CV_RGB(255,0,0));//用黑色画出画出剖分顶点。
free( buf );
static void paint_voronoi( CvSubdiv2D* subdiv, IplImage* img )//画出voronoi面
int i, total = subdiv-&edges-&//边缘总数
int elem_size = subdiv-&edges-&elem_//边缘的大小
cvCalcSubdivVoronoi2D( subdiv );
cvStartReadSeq( (CvSeq*)(subdiv-&edges), &reader, 0 );
for( i = 0; i & i++ )
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);//获取四方边缘
if( CV_IS_SET_ELEM( edge ))//判断边缘是否在边缘集中
CvSubdiv2DEdge e = (CvSubdiv2DEdge)//edge是四方边缘的指针,而CvSubdiv2DEdge高位表示四方边缘的指针。
//cout&&(e&3)&&//通过测试e低2位即索引值应该设置为0了,即输入边缘
draw_subdiv_facet( img, cvSubdiv2DRotateEdge( e, 1 ));//e为Delaunay边,获得Delaunay边对应的voronoi边,即e的旋转边缘
draw_subdiv_facet( img, cvSubdiv2DRotateEdge( e, 3 ));//反向的旋转边缘
CV_NEXT_SEQ_ELEM( elem_size, reader );//移动到下一个位置
static void run(void)
char win[] = &source&;
CvRect rect = { 0, 0, 600, 600 };
CvMemStorage*
CvSubdiv2D*
CvScalar active_facet_color, delaunay_color, voronoi_color, bkgnd_
active_facet_color = CV_RGB( 255, 0, 0 );//红色
delaunay_color
= CV_RGB( 0,0,0);//黑色
voronoi_color = CV_RGB(0, 180, 0);//绿色
bkgnd_color = CV_RGB(255,255,255);//白色
img = cvCreateImage( cvSize(rect.width,rect.height), 8, 3 );
cvSet( img, bkgnd_color, 0 );
cvNamedWindow( win, 1 );
storage = cvCreateMemStorage(0);
subdiv = init_delaunay( storage, rect );
printf(&Delaunay triangulation will be build now interactively.\n&
&To stop the process, press any key\n\n&);
vector&CvPoint2D32f&
for( i = 0; i & 5; i++ )
CvPoint2D32f fp = cvPoint2D32f( (float)(rand()%(rect.width-10)),//使点约束在距离边框10像素之内。
(float)(rand()%(rect.height-10)));
points.push_back(fp);
locate_point( subdiv, fp, img, active_facet_color );//定位点的位置,并画出点所在voronoi面的边。
cvShowImage( win, img );//刷新显示
if( cvWaitKey( 100 ) &= 0 )
cvSubdivDelaunay2DInsert( subdiv, fp );//向三角剖分中插入该点,即对该点进行三角剖分
cvCalcSubdivVoronoi2D( subdiv );//计算Voronoi细分,有时候我们不需要
cvSet( img, bkgnd_color, 0 );//设置图像的背景颜色为白色
draw_subdiv( img, subdiv, delaunay_color, voronoi_color);
cvShowImage( win, img );
//cvWaitKey();
if( cvWaitKey( 100 ) &= 0 )
for(int i=0;i&points.size();i++)
draw_subdiv_point( img, points[i], active_facet_color );
cvShowImage(win,img);
cvWaitKey();
cvSet( img, bkgnd_color, 0 );//重新刷新画布,即设置背景颜色为白色
paint_voronoi( subdiv, img );//画出细分
cvShowImage( win, img );//
cvWaitKey(0);
cvReleaseMemStorage( &storage );
cvReleaseImage(&img);
cvDestroyWindow( win );
int main( int argc, char** argv )
(void) (void)
#ifdef _EiC
main( 1, &delaunay.c& );
和程序一类似,只是加了根据一个点,然后获取该点在所在的剖分平面(即包含该点的三角形),然后遍历该三角形:
#include &opencv2/imgproc/imgproc_c.h&
#include &opencv2/legacy/legacy.hpp&
#include &opencv2/highgui/highgui.hpp&
#include&opencv2\opencv.hpp&
#include&iostream&
#include &stdio.h&
static void help( void )
printf(&\nThis program demostrates iterative construction of\n&//这个程序阐述了delaunay剖分和voronoi细分的迭代构造
&delaunay triangulation and voronoi tesselation.\n&
&It draws a random set of points in an image and then delaunay triangulates them.\n&//在图像上画出一些随机点,然后进行delaunay三角剖分
&Usage: \n&
&./delaunay \n&
&\nThis program builds the traingulation interactively, you may stop this process by\n&
&hitting any key.\n&);//迭代构造三角剖分,如果像停止,则按任意键
static CvSubdiv2D* init_delaunay( CvMemStorage* storage,//初始化三角剖分结构,为其分配单元
CvRect rect )
CvSubdiv2D*//三角剖分的数据单元
subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*subdiv),
sizeof(CvSubdiv2DPoint),
sizeof(CvQuadEdge2D),
storage );
cvInitSubdivDelaunay2D( subdiv, rect );
static void draw_subdiv_point( IplImage* img, CvPoint2D32f fp, CvScalar color )//画出三角剖分的顶点
cvCircle( img, cvPoint(cvRound(fp.x), cvRound(fp.y)),5, color, CV_FILLED, 8, 0 );
static void draw_subdiv_edge( IplImage* img, CvSubdiv2DEdge edge, CvScalar color )//画出三角剖分的边
CvSubdiv2DPoint* org_//源顶点
CvSubdiv2DPoint* dst_//目地顶点
CvPoint2D32
CvPoint2D32
CvPoint iorg,
org_pt = cvSubdiv2DEdgeOrg(edge);//通过边获取顶点
dst_pt = cvSubdiv2DEdgeDst(edge);
if( org_pt && dst_pt )//如果两个端点不为空
org = org_pt-&
dst = dst_pt-&
iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
cvLine( img, iorg, idst, color, 1, CV_AA, 0 );
static void draw_subdiv( IplImage* img, CvSubdiv2D* subdiv,
CvScalar delaunay_color, CvScalar voronoi_color
)//画出剖分和细分
int i, total = subdiv-&edges-&//边的数量
int elem_size = subdiv-&edges-&elem_//边的大小
cout&&typeid(subdiv-&edges).name()&&
cvStartReadSeq( (CvSeq*)(subdiv-&edges), &reader, 0 );//使用CvSeqReader遍历Delaunay或者Voronoi边
for( i = 0; i & i++ )
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
if( CV_IS_SET_ELEM( edge ))
// draw_subdiv_edge( img, (CvSubdiv2DEdge)edge + 1, voronoi_color );
draw_subdiv_edge( img, (CvSubdiv2DEdge)edge, delaunay_color );
CV_NEXT_SEQ_ELEM( elem_size, reader );
static void locate_point( CvSubdiv2D* subdiv, CvPoint2D32f fp, IplImage* img,//遍历三角剖分的边
CvScalar active_color )
CvSubdiv2DE
CvSubdiv2DEdge e0 = 0;
CvSubdiv2DPoint* p = 0;
cvSubdiv2DLocate( subdiv, fp, &e0, &p );
draw_subdiv_edge( img, e, active_color );
e = cvSubdiv2DGetEdge(e,CV_NEXT_AROUND_LEFT);
while( e != e0 );
draw_subdiv_point( img, fp, active_color );
//@author andme-单目视觉
void dashLine(Mat &img, Point2d& pt1, Point2d& pt2, int n)//n为虚线段数
Point sub = pt2 - pt1;
for (int i = 0; i & 2*n; i += 2)
line(img, Point(pt1.x + sub.x * i / (2 * n - 1), pt1.y + sub.y * i / (2 * n - 1)), Point(pt1.x + sub.x * (i+1) / (2 * n - 1), pt1.y + sub.y * (i+1) / (2 * n - 1)), Scalar(0,255,0), 2);
//调用形式draw_subdiv_facet( img, cvSubdiv2DRotateEdge( e, 1 ));
static void draw_subdiv_facet( IplImage* img, CvSubdiv2DEdge edge )//画出voronoi面
//cout&&edge&&//edge低两位表示表示索引,高位表示四方边缘指针。
//cout&&(edge&3)&&
CvSubdiv2DEdge t =//当我们按上面的调用形式时,edge为eRot。
int i, count = 0;
CvPoint* buf = 0;
Point2d *buf1=0;
// count number of edges in facet //面内边的计数
t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );
} while (t != edge );//我们绕着一个voronoi单元一周,遍历该vornonoi边缘所拥有的边缘数。
buf = (CvPoint*)malloc( count * sizeof(buf[0]));
buf1=(Point2d*)malloc(count*sizeof(buf1[0]));
// gather points
for( i = 0; i & i++ )
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeOrg( t );//第一次获取eRot边缘的起始点
if( !pt )//如果得不到该源点,则退出循环
buf[i] = cvPoint( cvRound(pt-&pt.x), cvRound(pt-&pt.y));//将该点转换为cvPoint类型点,存储在buf中
t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );//然后绕着vornonoi单元,左旋转。
if( i == count )//如果所有的点都存储起来了。
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeDst( cvSubdiv2DRotateEdge( edge, 1 ));//这里eRot的旋转边缘应该是reversed e,那么目的点,就是e的源点。
// cvFillConvexPoly( img, buf, count, CV_RGB(rand()&255,rand()&255,rand()&255), CV_AA, 0 );//填充凸多边形
for(i=0;i&i++)
buf1[i].x=buf[i].x;
buf1[i].y=buf[i].y;
Mat mat_img(img);
cvPolyLine( img, &buf, &count, 1, 1, CV_RGB(0,200,0), 1, CV_AA, 0);//画出线。
//for(int i=0;i&count-1;i++)
//dashLine(mat_img,buf1[i],buf1[i+1],100);
//dashLine(mat_img,buf1[i],buf1[0],100);
draw_subdiv_point( img, pt-&pt, CV_RGB(255,0,0));//用黑色画出画出剖分顶点。
free( buf );
/**********************************************重点部分:如何实现变量所有的Delauany或者Voronoi边*****************************/
static void paint_voronoi( CvSubdiv2D* subdiv, IplImage* img )//画出voronoi面
int i, total = subdiv-&edges-&//边缘总数
int elem_size = subdiv-&edges-&elem_//边缘的大小
cvCalcSubdivVoronoi2D( subdiv );
cvStartReadSeq( (CvSeq*)(subdiv-&edges), &reader, 0 );
for( i = 0; i & i++ )
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);//获取四方边缘
if( CV_IS_SET_ELEM( edge ))//判断边缘是否在边缘集中
CvSubdiv2DEdge e = (CvSubdiv2DEdge)//edge是四方边缘的指针,而CvSubdiv2DEdge高位表示四方边缘的指针。
//cout&&(e&3)&&//通过测试e低2位即索引值应该设置为0了,即输入边缘
draw_subdiv_facet( img, cvSubdiv2DRotateEdge( e, 1 ));//e为Delaunay边,获得Delaunay边对应的voronoi边,即e的旋转边缘
draw_subdiv_facet( img, cvSubdiv2DRotateEdge( e, 3 ));//反向的旋转边缘
CV_NEXT_SEQ_ELEM( elem_size, reader );//移动到下一个位置
/*************************************************************************************/
draw_edge(CvSubdiv2DEdge e0,IplImage *img,const char *cp )
CvSubdiv2DPoint *point1_org=cvSubdiv2DEdgeOrg(e0);
CvSubdiv2DPoint *point1_dst=cvSubdiv2DEdgeDst(e0);
CvPoint pt_org=cvPointFrom32f(point1_org-&pt);
CvPoint pt_dst=cvPointFrom32f(point1_dst-&pt);
pt.x=(pt_org.x+pt_dst.x)/2;
pt.y=(pt_org.y+pt_dst.y)/2;
cvInitFont(&font,CV_FONT_ITALIC,1,1,0,2,8);
cvLine(img,pt_org,pt_dst,cvScalar(255,0,0),2,8);
cvCircle(img,pt,6,cvScalar(0,0,0),2,CV_AA);
cout&&&点位于&&&pt.x&&& &&&pt.y&&
cvPutText(img,cp,pt,&font,cvScalar(0,255,0));
if(pt_dst.y-pt_org.y&0)
cout&&&箭头朝下&&&
cout&&&箭头朝上&&&
static void run(void)
char win[] = &source&;
CvRect rect = { 0, 0, 600, 600 };
CvMemStorage*
CvSubdiv2D*
CvScalar active_facet_color, delaunay_color, voronoi_color, bkgnd_
active_facet_color = CV_RGB( 255, 0, 0 );//红色
delaunay_color
= CV_RGB( 0,0,0);//黑色
voronoi_color = CV_RGB(0, 180, 0);//绿色
bkgnd_color = CV_RGB(255,255,255);//白色
img = cvCreateImage( cvSize(rect.width,rect.height), 8, 3 );
cvSet( img, bkgnd_color, 0 );
cvNamedWindow( win, 1 );
storage = cvCreateMemStorage(0);
subdiv = init_delaunay( storage, rect );
printf(&Delaunay triangulation will be build now interactively.\n&
&To stop the process, press any key\n\n&);
vector&CvPoint2D32f&
for( i = 0; i & 5; i++ )
CvPoint2D32f fp = cvPoint2D32f( (float)(rand()%(rect.width-10)),//使点约束在距离边框10像素之内。
(float)(rand()%(rect.height-10)));
points.push_back(fp);
locate_point( subdiv, fp, img, active_facet_color );//定位点的位置,并画出点所在voronoi面的边。
cvShowImage( win, img );//刷新显示
if( cvWaitKey( 100 ) &= 0 )
cvSubdivDelaunay2DInsert( subdiv, fp );//向三角剖分中插入该点,即对该点进行三角剖分
cvCalcSubdivVoronoi2D( subdiv );//计算Voronoi细分,有时候我们不需要
cvSet( img, bkgnd_color, 0 );//设置图像的背景颜色为白色
draw_subdiv( img, subdiv, delaunay_color, voronoi_color);
cvShowImage( win, img );
//cvWaitKey();
if( cvWaitKey( 100 ) &= 0 )
for(int i=0;i&points.size();i++)
draw_subdiv_point( img, points[i], active_facet_color );
cvShowImage(win,img);
cvWaitKey();
cvSet( img, bkgnd_color, 0 );//重新刷新画布,即设置背景颜色为白色
paint_voronoi( subdiv, img );//画出细分
CvPoint2D32f point1=cvPoint2D32f(300,300);//图像中心选择一点。
CvSubdiv2DE
CvSubdiv2DEdge e0 = 0;
CvSubdiv2DPoint* p = 0;
CvSubdiv2DPointLocation loc=cvSubdiv2DLocate( subdiv, point1, &e0, &p );
if(loc==CV_PTLOC_INSIDE)
cout&&&落入某些面,箭头的方向用来说明图的方向&&&
draw_edge(e0,img,&fisrt&);
CvSubdiv2DEdge e_lnext=cvSubdiv2DGetEdge(e0,CV_NEXT_AROUND_LEFT);
draw_edge(e_lnext,img,&second&);
CvSubdiv2DEdge e_Onext=cvSubdiv2DGetEdge(e_lnext,CV_NEXT_AROUND_LEFT);
draw_edge(e_Onext,img,&third&);
}else if(loc==CV_PTLOC_ON_EDGE)
cout&&&点落在边缘上&&&
}else if(loc==CV_PTLOC_VERTEX)
cout&&&和顶点重合&&&
cout&&&输入变量无效&&&
cvShowImage( win, img );//
cvWaitKey(0);
cvReleaseMemStorage( &storage );
cvReleaseImage(&img);
cvDestroyWindow( win );
int main( int argc, char** argv )
(void) (void)
#ifdef _EiC
main( 1, &delaunay.c& );
执行效果:
上图中我们选择了图像的中心点,图像大小为600×600,故中心点为300×300,我们通过函数cvSubdiv2DLocate()得知该点落在了面内,然后我们该函数得到该面的一个边缘,我们有了边缘信息,如果我们帮它当做程序1上的参考图中的e,那么我就可以获得参考图中的所有边的信息,我们也可以根据这些边的信息获取的这些边的端点。
并注意一下控制台的输出信息:即first边的方向向下,second为方向向上,third是方向向下。
#include &opencv2/legacy/legacy.hpp&
#include &opencv2/opencv.hpp&
#include &opencv2/nonfree/nonfree.hpp&
#include &opencv2/nonfree/features2d.hpp&
#include &atlstr.h& // use STL string instead, although not as convenient...
#include &atltrace.h&
#include &iostream&
#include &fstream&
#include &string&
#include&time.h&
pts,要剖分的散点集,in
img,剖分的画布,in
tri,存储三个表示顶点变换的正数,out
// used for doing delaunay trianglation with opencv function
//该函数用来防止多次重画并消去虚拟三角形的 顶点
bool isGoodTri( Vec3i &v, vector&Vec3i& & tri )
int a = v[0], b = v[1], c = v[2];
v[0] = min(a,min(b,c));//v[0]找到点插入的先后顺序(0....N-1,N为点的个数)的最小值
v[2] = max(a,max(b,c));//v[2]存储最大值.
v[1] = a+b+c-v[0]-v[2];//v[1]为中间值
if (v[0] == -1)
vector&Vec3i&::iterator iter = tri.begin();//开始时为空
for(;iter!=tri.end();iter++)
Vec3i &check = *//如果当前待压入的和存储的重复了,则停止返回false。
if (check[0]==v[0] &&
check[1]==v[1] &&
check[2]==v[2])
if (iter == tri.end())
tri.push_back(v);
pts,要剖分的散点集,in
img,剖分的画布,in
tri,存储三个表示顶点变换的正数,out
void TriSubDiv( vector&Point2f& &pts, Mat &img, vector&Vec3i& &tri )
CvSubdiv2D*
CvMemStorage* storage = cvCreateMemStorage(0); //创建存储器
Rect rc = Rect(0,0, img.cols, img.rows);//矩形是图像的大小
subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*subdiv),
sizeof(CvSubdiv2DPoint),
sizeof(CvQuadEdge2D),
storage );//为剖分数据分配空间
cvInitSubdivDelaunay2D( subdiv, rc );
for (size_t i = 0; i & pts.size(); i++)
CvSubdiv2DPoint *pt = cvSubdivDelaunay2DInsert( subdiv, pts[i] );//利用插入法进行剖分
pt-&id =//为每一个顶点分配一个id
CvSeqR//利用CvSeqReader遍历
int total = subdiv-&edges-&//边的总数
int elem_size = subdiv-&edges-&elem_//边的大小
cvStartReadSeq( (CvSeq*)(subdiv-&edges), &reader, 0 );
Point buf[3];
const Point *pBuf =
Vec3i verticesI
Mat imgShow = img.clone();
srand( (unsigned)time( NULL ) );
for( int i = 0; i & i++ )
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
if( CV_IS_SET_ELEM( edge ))
CvSubdiv2DEdge t = (CvSubdiv2DEdge)
int iPointNum = 3;
Scalar color = CV_RGB(rand()&255,rand()&255,rand()&255);
//Scalar color=CV_RGB(255,0,0);
//bool isNeg =
for(j = 0; j & iPointN j++ )
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeOrg( t );//获取t边的源点
buf[j] = pt-&//将点存储起来
//if (pt-&id == -1) isNeg =
verticesIdx[j] = pt-&//获取顶点的Id号,将三个点的id存储到verticesIdx中
t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );//获取下一条边
if (j != iPointNum)
if (isGoodTri(verticesIdx, tri))
//tri.push_back(verticesIdx);
polylines( imgShow, &pBuf, &iPointNum,
1, true, color,
1, CV_AA, 0);//画出三条边
//printf(&(%d, %d)-(%d, %d)-(%d, %d)\n&, buf[0].x, buf[0].y, buf[1].x, buf[1].y, buf[2].x, buf[2].y);
//printf(&%d\t%d\t%d\n&, verticesIdx[0], verticesIdx[1], verticesIdx[2]);
//imshow(&Delaunay&, imgShow);
//waitKey();
t = (CvSubdiv2DEdge)edge+2;//相反边缘 reversed e
for(j = 0; j & iPointN j++ )
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeOrg( t );
buf[j] = pt-&
verticesIdx[j] = pt-&
t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );
if (j != iPointNum)
if (isGoodTri(verticesIdx, tri))
//tri.push_back(verticesIdx);
polylines( imgShow, &pBuf, &iPointNum,
1, true, color,
1, CV_AA, 0);
//printf(&(%d, %d)-(%d, %d)-(%d, %d)\n&, buf[0].x, buf[0].y, buf[1].x, buf[1].y, buf[2].x, buf[2].y);
//printf(&%d\t%d\t%d\n&, verticesIdx[0], verticesIdx[1], verticesIdx[2]);
//imshow(&Delaunay&, imgShow);
//waitKey();
CV_NEXT_SEQ_ELEM( elem_size, reader );
//RemoveDuplicate(tri);
char title[100];
sprintf_s(title, 100, &Delaunay: %d Triangles&, tri.size());//tri存储的为3个顶点为一个vec3i,故tri.size()表示三角形的个数。
imshow(title, imgShow);
waitKey();
void main(int argc, char* argv[])
Mat imgL(600,600,CV_8UC3);
/************************************************************************/
/* Delaunay triangulation
/************************************************************************/
cout&&&doing triangulation...&&&
vector&Vec3i&
vector&Point2f& vec_
i = 0; i & 60; i++ )
Point2f fp = cvPoint2D32f( (float)(rand()%(imgL.cols-10)),//使点约束在距离边框10像素之内。
(float)(rand()%(imgL.rows-10)));
vec_points.push_back(fp);
TriSubDiv(vec_points, imgL, tri);
执行结果:
上程序中,通过isGoodTri()函数可以有效地防止重画并且消除虚拟三角形的顶点。
我要说的:
1、程序的关键是通过上面**号强调的利用CvSeqReader遍历所有的Delaunay或者Voronoi.
2、Delaunay或者Voronoi之间的桥梁就是通过函数,cvSubdiv2DRotateEdge()实现边的旋转(切换)。
3、这个Delaunay是个有向图,还是双向的。具体怎么实现的,没有较深的数据结构为基础是很难理解的,至少现在我还不是很懂具体的实现,关于它的使用还是摸索阶段。OpenCV水很深,如果这东西只是为我们用,或者了解,而不是研究,我们没有必要纠结的太深。
4、文章中的错误,还请指正,一起学习。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:663007次
积分:7241
积分:7241
排名:第2143名
原创:137篇
转载:18篇
评论:199条
文章:25篇
阅读:23737
文章:21篇
阅读:33904
(1)(2)(4)(4)(1)(1)(5)(5)(6)(10)(6)(4)(24)(6)(1)(1)(21)(25)(7)(3)(8)(8)(2)

我要回帖

更多关于 opencv subdiv2d 的文章

 

随机推荐