##图像轮廓生成Freeman码##
- 摘要
Freeman码是描述轮廓的另外一种形式,这篇博客我将会用matlab和OpenCV两个版本生成Freeman码。纵观网上很多介绍边缘轮廓的,都提到过Freeman码,但是实现却比较少。
-
准备工作
2.1 matlab版
matlab自身是不带边界轮廓跟踪生成Freeman码算法函数的。起初我做的时候,网上有介绍matlab轮廓生成Freeman码函数,给个链接matlab生成Freeman码介绍,但是在matlab编辑器上写这个函数时,发现报错"未定义boundaries函数"。其实这个函数是书本《数字图像处理MATLAB版》的库函数,matlab不自带,故需要在matlab中添加库函数,我有资源冈萨雷斯数字图像MATLAB处理库函数,具体添加工作可以百度,也可以问我。
2.2 OpenCV版
OpenCV不需要准备工作。 -
Freeman码的生成
3.1 matlab版
用matlab生成Freeman码的代码可以在我给出的那篇博客中复制过来,我把生成Freeman码的代码写进了一个function中,在以前基础上,我将每个轮廓点的和它的链码(即它下一个点在它的那个方向上)存起来以更好的下一步工作,代码如下:
function contours=freeman(im_edge)% 函数定义
c=boundaries(im_edge,8);%生成Freeman码
contours=cell(size(c,1),1);
index=1;
% [L,W]=size(im_edge);%观察轮廓
% im_edge(c{1}(:,1)+(c{1}(:,2)-1)*L)=1;
% imshow(im_edge);
while index<=size(c,1)
if length(c{index})<40
c(index)=[];
else
h=fchcode(c{index,:},8);
contour=c{index,1}(1:length(h.fcc),:);
contour=[contour,h.fcc'];%提取Freeman并和轮廓点组合
contours{index,1}=contour;
index=index+1;
end
end
contours(index:end,:)=[];
end
调用上述函数代码:
contours=freeman(im_edge);
contours生成的Freeman就在元胞数组里,可以进行下一步工作了。另外,matlab语言我还不是很熟练,很多用法都是业余水平,望指教!
---------------------------------------------------------2019-03-27更新---------------------------------------------------------------
这两天做论文时发现,matlab中还有个提取轮廓链码的函数bwboundaries,具体的用法大家可以自行百度,我在这里想说的是,这个函数bwboundaries要比boundaries函数快三倍时间。bwboundaries好像是外部库函数,我用的是matlab2014b。
[c,l]= bwboundaries(im_edge,'noholes'); %c是基于八邻域提取的轮廓,
[cc]=chaincode(c{index,1}); %提取对应的Freeman链码
这个chaincode是在mathworks上搜的提取轮廓Freeman链码的一个function
我这里直接贴上来代码,有点多,好多注释,我直接放链接吧,看了下,好像作者更新了,应该也是没问题,如果想用我的,私信我即可。
[https://ww2.mathworks.cn/matlabcentral/fileexchange/29518-freeman-chain-code?s_tid=srchtitle]
---------------------------------------------------------end-----------------------------------------------------------------------------
3.2 OpenCV版
OpenCV自带轮廓提取的库函数是findContours(),这是C++接口函数,目前我还没有用findContours()函数生成Freeman码,这里我是使用C语言接口的函数cvFindContours(),但是这并不影响我们使用C++调用OpenCV的库函数。在这里感谢原帖如何取得Freeman链码,帖子上代码是没什么问题的,但是美中不足的是在提取轮廓点坐标时有错误,我自己摸索出解决方法,同样的,我也将提取Freeman码写进一个函数供调用,OpenCV生成Freeman码的代码如下:
void Freemans(Mat srcimage,vector<vector<FM>>&FMS)
{
CvMat _srcimage = srcimage;
CvMemStorage* storage = cvCreateMemStorage();//采用默认大小,即:0.
CvSeq* first_contour = NULL;
int Nc = cvFindContours(&_srcimage,storage,&first_contour,sizeof(CvContour),
CV_RETR_CCOMP,
CV_CHAIN_CODE,///*这个是关键参数*/
cvPoint(0, 0)
);
CvChain* chain = 0;
vector<CvSeq*>c1;
CvSeq* h;
for( CvSeq* c = first_contour; c != NULL;c=c->h_next)
{
vector<FM>fms;
int total=c->total;
if(total<20)
c=c->h_next;
else
{
CvSeqReader reader;
cvStartReadSeq( (CvSeq*)c, &reader, 0 );
CvChainPtReader reader1;
cvStartReadChainPoints((CvChain*)c, &reader1);
FM fm;
for( int i = 0; i < total; i++ )
{
char code;
CV_READ_SEQ_ELEM( code, reader ); //printf(" %d,",code); //得到轮廓的Freeman链码序列
fm.direction=code;
CvPoint pt;
CV_READ_CHAIN_POINT(pt,reader1);
fm.x=pt.x;
fm.y=pt.y;
fms.push_back(fm);
}
FMS.push_back(fms);
}
}//for
}
调用上述函数代码:
Freemans(dstimage,FMSS);
dstimage是用边缘检测算法检测出的边缘,FMSS是存放轮廓点及其Freeman码的容器,包含三个元素,你懂的。
至此,两个版本的轮廓生成Freeman码都写完了,无论是matlab还是OpenCV,两个版本的算法共同的缺点是对于非闭合轮廓会生成两遍,闭合轮廓是没问题的。我的另外一篇博客OpenCV八邻域轮廓跟踪算法同样的也可以生成Freeman码,但个人认为首选OpenCV自带的库函数还是比较好。
5. 总结
在生成Freeman码过程中,付出很多,查找很多资料,有时候看见网上一些关于轮廓生成Freeman的帖子蜻蜓点水就来气,没有实用的方法生成Freeman码,所以自己将自己得成果整理出来奉献出来相互学习交流,不足的地方望批评指正,谢谢大家!