调用OpenCV的cvFindContours方法获取图像边界

//得到图像的外边框 

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;

 

原图调用OpenCV的cvFindContours方法获取图像边界_第1张图片二值化图像多边形逼近绘图调用OpenCV的cvFindContours方法获取图像边界_第2张图片

原图

多边形逼近绘图

你可能感兴趣的:(算法,image,String,list,tree,Integer)