OpenCV Skeletonization - eiichiromomma/CVMLAB GitHub Wiki
(OpenCV) Skeletonization
細線化より単純にスケルトンを抽出
cvFilter2Dによる2次元フィルタ処理の練習も兼用
- cvDistTransformで距離変換(尾根=スケルトン になる)
- Kasvandの反復型線検出オペレータを適用した画像S0,S45,S90,S135を求める
- Smax=max(S0,S45,S90,S135)を求める
- g(x,y)=Smax(Smax>=0のとき), 0(Smax<0のとき)を求める
ペイントで適当に作成
閾値は適当に決めた。対象物の予見される中心までの距離を基準に考えれば大体の数値は決められるかも? 低めに設定してノイズや枝の除去をするか、高めに設定して膨張収縮させるかするとスケルトンだけ抽出できるかも?
//Kasvandの反復型線検出オペレータ 作成:Eiichiro Momma
//2008.1.21 ざっくり実装
#include <cv.h>
#include <highgui.h>
void main(void)
{
IplImage *src;
IplImage *distsrc;
IplImage *out;
IplImage *S00;
IplImage *S45;
IplImage *S90;
IplImage *S135;
CvMat kern00, kern45, kern90, kern135;
float Smax=0;
//Kasvandの反復型線検出オペレータ
float L0[]={
-1,-1,-1,-1,-1,
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
0, 0, 0, 0, 0,
-1,-1,-1,-1,-1
};
float L45[]={
0,-1,-1, 0, 2,
-1,-1, 0, 2, 0,
-1, 0, 2, 0,-1,
0, 2, 0,-1,-1,
2, 0,-1,-1, 0
};
float L90[]={
-1, 0, 2, 0,-1,
-1, 0, 2, 0,-1,
-1, 0, 2, 0,-1,
-1, 0, 2, 0,-1,
-1, 0, 2, 0,-1
};
float L135[]={
2, 0,-1,-1, 0,
0, 2, 0,-1,-1,
-1, 0, 2, 0,-1,
-1,-1, 0, 2, 0,
0,-1,-1, 0, 2
};
src=cvLoadImage("OpenCVKasvandTest.png)",0);
distsrc=cvCreateImage(cvGetSize(src),IPL_DEPTH_32F,1);
S00=cvCreateImage(cvGetSize(src),IPL_DEPTH_32F,1);
S45=cvCreateImage(cvGetSize(src),IPL_DEPTH_32F,1);
S90=cvCreateImage(cvGetSize(src),IPL_DEPTH_32F,1);
S135=cvCreateImage(cvGetSize(src),IPL_DEPTH_32F,1);
out=cvCreateImage(cvGetSize(src),IPL_DEPTH_32F,1);
//kernelの作成
cvInitMatHeader(&kern00,5,5,CV_32FC1,L0);
cvInitMatHeader(&kern45,5,5,CV_32FC1,L45);
cvInitMatHeader(&kern90,5,5,CV_32FC1,L90);
cvInitMatHeader(&kern135,5,5,CV_32FC1,L135);
//距離変換
cvDistTransform(src,distsrc,CV_DIST_L2,5);
//フィルタ処理
cvFilter2D(distsrc,S00,&kern00);
cvFilter2D(distsrc,S45,&kern45);
cvFilter2D(distsrc,S90,&kern90);
cvFilter2D(distsrc,S135,&kern135);
//
//Smax = MAX(S00,S45,S90,S135)
// / Smax, Smax >= 0
// g = |
// \ 0 , others
//
for (int y=0; y < out->height; y++){
for (int x=0; x< out->width; x++){
Smax = MAX(MAX(((float*)(S00->imageData + y* S00->widthStep))[x], ((float*)(S45->imageData + y* S45->widthStep))[x]),
MAX(((float*)(S90->imageData + y* S90->widthStep))[x], ((float*)(S135->imageData + y* S135->widthStep))[x]));
((float*)(out->imageData + y* out->widthStep))[x] = Smax > 0 ? Smax: 0.0;
}
}
cvThreshold(out,out,7,1,CV_THRESH_BINARY);
cvNamedWindow("S00",1);
cvNamedWindow("S45",1);
cvNamedWindow("S90",1);
cvNamedWindow("S135",1);
cvNamedWindow("out",1);
cvShowImage("S00",S00);
cvShowImage("S45",S45);
cvShowImage("S90",S90);
cvShowImage("S135",S135);
cvShowImage("out",out);
cvWaitKey(0);
//画像出力のため8Uのsrcを再利用
//Sxxは出力の勾配がわかるよう32倍(適当)
cvCvtScaleAbs(S00,src,32,0);
cvSaveImage("S00.png)",src);
cvCvtScaleAbs(S45,src,32,0);
cvSaveImage("S45.png)",src);
cvCvtScaleAbs(S90,src,32,0);
cvSaveImage("S90.png)",src);
cvCvtScaleAbs(S135,src,32,0);
cvSaveImage("S135.png)",src);
cvCvtScaleAbs(out,src,255,0);
cvSaveImage("out.png)",src);
}