//得到图像的外边框
procedure TFrmMain.Button3Click(Sender: TObject);
var
oImg, oImg2: PIplImage;
contours: PCvSeq;
p: Pchar;
pSeq: PCvSeq;
oMem: TCvMemStorage;
I, nCnt, area: Integer;
stor: pCvMemStorage;
cont: pCvSeq;
rr:CvRect;
sFileName: string;
begin
OpenDialog1.InitialDir := ExtractFilePath(ParamStr(0)) + 'pic';
if OpenDialog1.Execute then
sFileName := OpenDialog1.FileName
else
Exit;
stor := cvCreateMemStorage(0);
cont := cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), stor);
oImg := cvLoadImage(PChar(sFileName), CV_LOAD_IMAGE_GRAYSCALE);
oImg2 := cvCreateImage(cvSize_(oImg.Width, oImg.Height), IPL_DEPTH_8U, 1);
cvSmooth(oImg, oImg, CV_MEDIAN, 5, 5);
cvAdaptiveThreshold(oImg, oImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, StrToInt(Edit1.Text), StrToInt(Edit2.Text));
//cvSaveImage('C:\1.bmp', oImg);
cvNamedWindow('Example1', CV_WINDOW_AUTOSIZE);
cvNamedWindow('Example2', CV_WINDOW_AUTOSIZE);
//侵蚀
cvErode(oImg, oImg, 0, 2);
//查找轮廓
(*mode 检索模式,可取值如下:
CV_RETR_EXTERNAL:只检索最外面的轮廓;
CV_RETR_LIST:检索所有的轮廓,并将其放入list中;
CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。
method 边缘近似方法(除了CV_RETR_RUNS使用内置的近似,其他模式均使用此设定的近似算法)。可取值如下:
CV_CHAIN_CODE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
CV_CHAIN_APPROX_NONE:将所有的连码点,转换成点。
CV_CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法的一种。
CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。
*)
//nCnt := cvFindContours(oImg, stor, @cont, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint_(0,0) ); //找到所有轮廓
nCnt := cvFindContours(oImg, stor, @cont, SizeOf(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint_(0, 0));
while cont <> nil do
begin
rr := pCvContour(cont).Rect;
area:=cont.total;
cvRectangle(oImg2, cvPoint_(rr.x, rr.y), cvPoint_(rr.x + rr.width, rr.y + rr.height), CV_RGB(255,0,0), 2, 8, 0);
//count := count + 1;
cont := cont.h_next;
end;
cvShowImage('Example1', oImg);
cvShowImage('Example2', oImg2);
cvWaitKey(0);
cvDestroyWindow('Example1');
cvDestroyWindow('Example2');
cvReleaseImage(oImg);
end;
//使用Freeman链码进行边界绘制.
//设置cvFindContours函数的method为CV_CHAIN_CODE,可以获取到freeman链码,从而可得到所有的边界点,尝试使用cvStartReadChainPoints和cvReadChainPoint读取其中的点总是报错,最后找到一种替代的方案,先进行多边形逼近,在将Seq转换为数组,读取到逼近的多边形点.
procedure TFrmMain.BtnFreeManClick(Sender: TObject);
var
oImg, oImg2, oImg3: PIplImage;
contours: PCvSeq;
p: Pchar;
pSeq: PCvSeq;
oMem: TCvMemStorage;
I, nCnt, nMaxArea, total: Integer;
stor: pCvMemStorage;
cont: pCvSeq;
pnt:CvPoint;
sFileName: string;
pChain: PCvChain;
nDistance: Integer;
nDelay: Integer;
IsDeflection: Boolean;
seqReader: CvSeqReader;
pChainReader: PCvChainPtReader;
chainReader: CvChainPtReader;
c: PCvChain;
arr: TIntegerArr;
begin
nMaxArea := 0;
OpenDialog1.InitialDir := ExtractFilePath(ParamStr(0)) + 'pic';
if OpenDialog1.Execute then
sFileName := OpenDialog1.FileName
else
Exit;
nDelay := THelp.GetSysTickCount64;
stor := cvCreateMemStorage(0);
cont := cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), stor);
oImg := cvLoadImage(PChar(sFileName), CV_LOAD_IMAGE_GRAYSCALE);
oImg3 := cvCreateImage(cvSize_(oImg.Width, oImg.Height), oImg.Depth, oImg.NChannels);
//oImg2 := cvCreateImage(cvSize_(oImg.Width, oImg.Height), IPL_DEPTH_8U, 1);
oImg2 := cvCloneImage(oImg);
//cvSmooth(oImg, oImg, CV_MEDIAN, 5, 5);
cvAdaptiveThreshold(oImg, oImg3, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 11, 5);
cvCanny(oImg, oImg, StrToInt(EdtCanny_P1.Text), StrToInt(EdtCanny_P2.Text));
cvSaveImage('C:\1.bmp', oImg3);
//侵蚀
//cvErode(oImg, oImg, 0, 5);
//cvSmooth(oImg, oImg, CV_MEDIAN, 15, 15);
cvSaveImage('C:\2.bmp', oImg);
cvNamedWindow('Example1', CV_WINDOW_AUTOSIZE);
cvNamedWindow('Example2', CV_WINDOW_AUTOSIZE);
pChain := nil;
nCnt := cvFindContours(oImg, stor, @pChain, SizeOf(CvChain), CV_RETR_LIST, CV_CHAIN_CODE, cvPoint_(0,0));
cont := cvApproxChains(PCvSeq(pChain), stor, CV_CHAIN_APPROX_SIMPLE, 0, 0, 1); //多边形逼近
cvZero(oImg);
//可以调用cvDrawContours或下面的代码手动绘边界图
//cvDrawContours(oImg, cont, cvScalar_(255), cvScalar_(255), MaxInt, 1, 8, cvPoint_(0, 0));
//使用多边形逼近得到线段进行绘制图像
while cont <> nil do
begin
if cont.total > 2 then
begin
SetLength(arr, cont.total * 2);
cvCvtSeqToArray(cont, @arr[0], CvSlice_(0, CV_WHOLE_SEQ_END_INDEX));
for i := Low(arr) to High(arr) div 2 do
begin
if (i + 1) * 2 < High(arr) then
cvLine(oImg, cvPoint_(arr[i * 2], arr[i * 2 + 1]), cvPoint_(arr[(i + 1) * 2], arr[(i + 1) * 2 + 1]), CV_RGB(255, 255, 255), 1);
end;
end;
cont := cont.h_next;
end;
cvSaveImage('C:\2.bmp', oImg3);
cvShowImage('Example1', oImg);
cvShowImage('Example2', oImg3);
cvWaitKey(0);
cvReleaseImage(oImg);
cvReleaseImage(oImg2);
cvDestroyWindow('Example1');
cvDestroyWindow('Example2');
end;
原图
多边形逼近绘图