OpenCV:第八章、图像轮廓与图像分割修复

  • 目录

    第八章、图像轮廓与图像分割修复

    8.1、查找并绘制轮廓

    8.1.1、寻找轮廓:findContours()函数

    8.1.2、绘制轮廓:drawContours()函数

    8.2、寻找物体的凸包

    8.2.1、凸包

    8.2.2、寻找凸包

    8.2.4、寻找和绘制物体的凸包

    8.3、使用多边形将轮廓包围

    8.3.1、返回外部矩形边界:boundingRect

    8.3.2、寻找最小包围矩形:minAreaRect

    8.3.3、寻找最小包围圆形minEnclosingCircle()函数

    8.3.4、用椭圆拟合二维点集:fitEllipse

    8.3.5、逼近多边形曲线:approxPolyDP()函数

    8.3.6、创建包围轮廓的矩形边界

    8.3.7、创建包围轮廓的圆形边界

    8.3.8、使用多边形包围轮廓

    8.4、图像的矩

    8.4.1、矩的计算:moments()函数

    8.4.2、计算轮廓面积:contourArea()函数

    8.4.3、计算轮廓长度:arcLengeh()函数

    8.5、分水岭算法

    8.5.1、实现分水岭算法:watershed()算法

    8.5.2、分水岭算法

    8.6、图像修补

    8.6.1、实现图像修补:inpaint()函数

    8.6.2、图像修补示例

    8.7、本章小结


    第八章、图像轮廓与图像分割修复

    • 8.1、查找并绘制轮廓

      • 8.1.1、寻找轮廓:findContours()函数

        • void findContours(InputOutArray image, OutputArrayOfArrays
           contours,OutputArray hierarchy, int mode,
           int method,Point offset=Point())

          1:输入图像且需为 8位单通道图像。 图像的非零像素被视为 1, 0像素值被 保留为 0,所以图像为二进制。 我们可以使用 compareO、 inrangeO、 thresholdO、 adaptivethresholdO、 cannyO等函数由灰度图或彩色图创建二进 制图像。 此函数会在提取图像轮廓的同时修改图像的内容;2:检测到的轮廓、函数 调用后的运算结果存在这里。每个轮廓存储为一个点向量,即用 point类型 的 vector 表示;3:可选的输出向量,包含图像的 拓扑信息。其作为轮廓数量的表示,包含了许多元素。每个轮廓contours[ i ] 对应4个hierarchy 元素 hierarchy[ i ][ 0 ]~hierarchy[ i ][ 3 ], 分别表示后一 个轮廓、前一个轮廓、父轮廓、内嵌轮廊的索引编号。 如果没有对应项, 对应的 hierarchy[i]值设置为负数。4:轮廓检索模式5:轮廓的近似办法6:每个轮廓点的可选偏移量,默认值Point()。对 ROI 图像中找出的轮廓, 并要在整个图像中进行分析时, 这个 参数便可排上用场。

        • findContours 经常与 drawContours 配合使用一使用用 findContoursO函数检测 到图像的轮廓后,便可以用 drawContoursO函数将检测到的轮廓绘制出来。

      • 8.1.2、绘制轮廓:drawContours()函数

        • void drawContours(InputOutputArray image,
          InputArrayOfArrays contours,int contourTdx,
          const Scalar& color, int thickness=1,
          int lineType=8, InputArray hierarchy=noArray(),
          int maxLevel=INT_MAX,Point offset=Point())

          1:目标图像;2:所有的输入轮廓,每个轮廓存储为一个点向量;3:轮廓绘制的指示变量,如果为负值,则绘制所有轮廓;4:轮廓的颜色;5:轮廓线条的粗细度,有默认值1。若为负值,便会绘制在轮廓的内部;6:线条的类型,默认值为8,7:可选的层次结构信息,默认noArray();8:用于绘制轮廓的最大等级,默认INT_MAX;9:可选的轮廓偏移参数,用指定的偏移量offset =(dx,dy)偏移需要绘制的轮廓,有默认值Point()

      • 8.1.3、基础示例程序:轮廓查找

        • int main(int argc, char **argv)
          {
              Mat srcImage = imread("1.jpg");
              imshow("原始图",srcImage);
          
              Mat dstImage = Mat::zeros(srcImage.rows, srcImage.cols,CV_8UC3);
          
              //取阈值大于199的部分
              srcImage=srcImage>199;
              imshow("取阈值后的图像",srcImage);
          
              //定义轮廓和层次结构
              vector<vector<Point>> contours;
              vector<Vec4i> hierarchy;
          
              //查找轮廓
              findContours(srcImage,contours;hierarchy,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
              //遍历所有顶层的轮廓,以随机颜色绘制出每个连接组件颜色
              int index=0;
              for(;index>=0;index=hierarchy[index[0]])
              {
                  Scalar color(rand()&255,rand()&255,rand()&255);
                  drawContours(dstImage,contours,index,color,FILLED,8,hierarchy);
              }
          
              imshow("轮廓图",dstImage);
              waitKey(0);
              return 0;
          
          }
          
          

      • 8.2、寻找物体的凸包

        • 8.2.1、凸包

          • 给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。理解物体形状或轮廓的一种比较有用的方法便是计算凸包,然后计算凸缺陷。很多复杂物体的特性能很好的被这种缺陷表现出来。
        • 8.2.2、寻找凸包

          • void convexHull(InputArray points, OutputArray hull,
            bool clockwise=false, bool returnPoints=true)

            1:输入的二维点集,可以是Mat或vector;2:输出参数,函数调用后找到的凸包;3:操作方向标识符,当为真时,输出的凸包为顺时针方向,反之为逆时针。4:操作标识符,默认为真,返回各凸包的各个点,否则返回凸包各点的指数。

            • #include <opencv2/opencv.hpp> 
              #include<opencv2/ highgui/highgui .hpp> 
              #include<opencv2/imgproc/imgproc.hpp> 
              using namespace cv; 
              using namespace std;
              
              
              
              int main()
              {
                  Mat image(600,600,CV_8UC3);
                  RNG& rng = theRng();
              
                  while(1)
                  {
                      char key;//键值
                      int count = (unsigned)rng%100+3;//随机生成点的数量
                      vector<Point> points;
              
                      //随机生成点坐标
                      for(int i = 0;i<count;i++)
                      {
                          Point point;
                          point.x = rng.uniform(image.cols/4, image.cols*3/4);
                          point.y = rng.uniform(image.rows/4, image.rows*3/4);
                          points.push_back(point);
                      }
                      //检测凸包
                      vector<int> hull;
                      convexHull (Mat(points),hull,true);
                      //绘制出随机颜色的点
                      image = Scalar::all(0);
                      for(int i=0;i<count;i++)
                      {
                          circle(image,points[i],3,Scalar(rng.uniform(0,255),
                          rng.uniform(0,255),rng.uniform(0,255)),FILLED,LINE_AA);
                          
                      }
                      //准备参数
                      int hullcount = (int)hull.size();//凸包的边数
                      Point point()=points[hull[hullcount-1]];//连接凸包边的坐标点
                      //绘制凸包的边
                      for(int i=0;i<hullcount;i++)
                      {
                          Point point = points[hull[i]];
                          line(image,point0,point,Scalar(255,255,255),2,LINE_AA);
                          point0= point;
                      }
                      imshow("凸包检测示例",image);
              
                      key=(char)waitKey();
                      if(key==27 || key=='q' || key == 'Q')
                          break;
                  }    
                  return 0;
              }
              

          • 8.2.4、寻找和绘制物体的凸包

          • #include <opencv2/opencv.hpp> 
            #include<opencv2/ highgui/highgui .hpp> 
            #include<opencv2/imgproc/imgproc.hpp> 
            using namespace cv; 
            using namespace std;
            
            #define WINDOW_NAME1 "【原始图窗口】"
            #define WINDOW_NAME2 "【效果图窗口】"
            
            Mat g_srcImage;Mat g_grayImage;
            int g_nThresh=50;
            int g_maxThresh=255;
            RNG g_rng(12345);
            Mat srcImage_copy = g_secImage.clone();
            Mat g_thresholdImage_output;
            vector<vector<Point>> g_vContours;
            vector<Vec4i> g_vHierarchy;
            
            static void ShowHelpText();
            void on_ThreshChange(int,void *);
            
            int main()
            {
                g_srcImage = imread("1.jpg");
            
                cvtColor( g_srcImage, g_grayImage,COLOR_BGR2GRAY);
                blur(g_grayImage,g_grayImage,Size(3,3));
            
                namedWindow(WINDOW_NAME1,WINDOW_AUTOSIZE);
                imshow(WINDOW_NAME1,g_srcImage);
            
                createTrackbar("阈值:",WINDOW_NAME1,&g_nThresh,g_maxThresh,
                on_ThreshChange);
                on_ThreshChange(0,0);
                waitKey(0);
                return 0;
            
            }
            
            void on_ThreshChange(int, void *)
            {
                threshold(g_grayImage,g_thresholdImage_output,g_nThresh,
                255,THRESH_BINARY);
            
                //遍历每个轮廓,寻找其凸包
                vector<vector<Point>> hull (g_vContours.size());
                for(unsigned int i = 0; i<g_vContours.size();i++)
                {
                    convexHull(Mat (g_vContours[i]),hull[i],false);
                }
                //绘出轮廓及其凸包
                Mat drawing = Mat::zeros(g_thresholdImage_output.size(),CV_8UC3);
                for(unsigned int i = 0; i<g_vContours.size();i++)
                {
                    Scalar color = Scalar(g_rng.uniform(0,255),
                    g_rng.uniform(0,255),g_rng.uniform(0,255));
                    
                    drawContours(drawing, g_vContours, i, color,1,8,
                    vector<Vec4i>(),0,Point());
            
                    drawContours(drawing, hull, i, color,1,8,
                    vector<Vec4i>(),0,Point());
            
                }
                imshow(WINDOW_NAME2,drawing);
            }
            
            
            

      • 8.3、使用多边形将轮廓包围

        • 8.3.1、返回外部矩形边界:boundingRect

          • Rect boundingRect(InputArray points)

            唯一的一个参数是输入的二维点集

        • 8.3.2、寻找最小包围矩形:minAreaRect

          • RotateRect minAreaRect(InputArray points)

            唯一的一个参数是输入的二维点集

        • 8.3.3、寻找最小包围圆形minEnclosingCircle()函数

          • void minEnclosingCircle(InputArray points, 
            Point2f& center,float& radius)

            1:输入的二维点集;2:圆的输出圆心;3:圆的输出半径

        • 8.3.4、用椭圆拟合二维点集:fitEllipse

          • RotatedRect fitEllipse(InputArray points)

            唯一的一个参数是输入的二维点集

        • 8.3.5、逼近多边形曲线:approxPolyDP()函数

          • void approxPolyDP(InputArray curve,OutputArray approxCurve,
            double epsilon, bool closed)

            1:输入的二维点集;2:多边形逼近的结果;3:逼近的精度,为原始曲线和即近似曲线间的最大值;4:为真则近似的曲线为封闭曲线

        • 8.3.6、创建包围轮廓的矩形边界

        • #include <opencv2/opencv.hpp> 
          #include<opencv2/ highgui/highgui .hpp> 
          #include<opencv2/imgproc/imgproc.hpp> 
          using namespace cv; 
          using namespace std;
          
          int main()
          {
              Mat image(600,600,CV_8U3C);
              RNG& rng = theRNG();
          
              while (1)
              {
                  //参数初始化
                  int count = rng.uniform(3,103);
                  vector<Point> points;
                  //随机生成点坐标
                  for(int i=0;i<count;i++)
                  {
                      Point point;
                      point.x = rng.uniform(image.cols/4,image.cols*3/4);
                      point.y = rng.uniform(image.rows/4,image.rows*3/4);
          
                      points.push_back(point);
                  }
          
                  //对给定的2D点集,寻找最小面积的包围矩形
                  RotatedRect box = minAreaRect(Mat(points));
                  Point2f vertex[4];
                  box.point(vertex);
          
                  //绘制出随机颜色的点
                  image = Scalar::all(0);
                  for(int i=0;i<count;i++)
                  {
                      circle(image,points[i],3,Scalar(rng.uniform(0,255),
                      rng.uniform(0,255),rng.uniform(0,255)),FILLED,LINE_AA);
                      
                  }
          
                  //绘制出最小面积的包围矩形
                  for(int i=0;i<4;i++)
                  {
                      line(image,vertex[i],vertex[(i+1)%4],
                      Scalar(100,200,211),2,LINE_AA);
                  }
          
                  imshow("矩形包围示例",image);
                  key=(char)waitKey();
                  if(key==27 || key=='q' || key == 'Q')
                      break;
              }
              return 0;
          
          
          }

        • 8.3.7、创建包围轮廓的圆形边界

        • #include <opencv2/opencv.hpp> 
          #include<opencv2/ highgui/highgui .hpp> 
          #include<opencv2/imgproc/imgproc.hpp> 
          using namespace cv; 
          using namespace std;
          
          int main()
          {
              Mat image(600,600,CV_8U3C);
              RNG& rng = theRNG();
          
              while (1)
              {
                  //参数初始化
                  int count = rng.uniform(3,103);
                  vector<Point> points;
                  //随机生成点坐标
                  for(int i=0;i<count;i++)
                  {
                      Point point;
                      point.x = rng.uniform(image.cols/4,image.cols*3/4);
                      point.y = rng.uniform(image.rows/4,image.rows*3/4);
          
                      points.push_back(point);
                  }
          
                  //对给定的2D点集,寻找最小面积的包围圆
                  Point2f center;
                  float radius = 0;
                  minEnclosingCircle(Mat(points),center,radius);
          
                  //绘制出随机颜色的点
                  image = Scalar::all(0);
                  for(int i=0;i<count;i++)
                  {
                      circle(image,points[i],3,Scalar(rng.uniform(0,255),
                      rng.uniform(0,255),rng.uniform(0,255)),FILLED,LINE_AA);
                      
                  }
          
                  //绘制出最小面积的包围圆
                  circle(image,center,cvRound(radius),Scalar(rng.uniform(0,
                  255),rng.uniform(0,255),rng.uniform(0,255)),2,LINE_AA);
            
                  imshow("圆形包围示例",image);
                  key=(char)waitKey();
                  if(key==27 || key=='q' || key == 'Q')
                      break;
              }
              return 0;
          
          
          }

        • 8.3.8、使用多边形包围轮廓

        • #include <opencv2/opencv.hpp> 
          #include<opencv2/ highgui/highgui .hpp> 
          #include<opencv2/imgproc/imgproc.hpp> 
          using namespace cv; 
          using namespace std;
          
          #define WINDOW_NAME1 "【原始图窗口】"
          #define WINDOW_NAME2 "【效果图窗口】"
          
          Mat g_srcImage;Mat g_grayImage;
          int g_nThresh=50;
          int g_maxThresh=255;
          RNG g_rng(12345);
          
          void on_ContoursChange(int , void*);
          static void ShowHelpText();
          
          int main()
          {
              system("color 1A");
          
              g_srcImage = imread("1.jpg");
          
              cvtColor( g_srcImage, g_grayImage,COLOR_BGR2GRAY);
              blur(g_grayImage,g_grayImage,Size(3,3));
          
              namedWindow(WINDOW_NAME1,WINDOW_AUTOSIZE);
              imshow(WINDOW_NAME1,g_srcImage);
              createTrackbar("阈值:",WINDOW_NAME1,&g_nThresh,g_maxThresh,
              on_ContoursChange);
              on_ContoursChange(0,0);
              waitKey(0);
              return 0;    
          
          }
          
          void on_ContoursChange(int, void *)
          {
              Mat threshold_output;
              vector<vector<Point>> contours;
              vector<Vec4i> hierarchy;
          
              //检测边缘
              threshold(g_grayImage,threshold_output,
              g_nThresh,255,THRESH_BINARY);
          
              //找出轮廓
              findContours(threshold_output, contours,hierarchy,
              RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
          
              //多边形逼近轮廓+获取矩形和圆形边界框
              vector<vector<Point>> contours_poly(contours.size());
              vector<Rect> boundRect(contours.size());
              vector<Point2f>center(contours.size());
              vector<float> radius(contours.size());
          
              for(unsigned int i=0;i<contours.size();i++)
              {
                  //指定精度逼近多边形曲线
                  approxPolyDP(Mat(contours[i]),contours_poly[i],
                  3,true);
                  //计算点集最外面的矩形边界
                  boundRect[i]=boundingRect(Mat(contours_poly[i]));
                  //对给定的2D点集,寻找最小面积的包围圆形
                  minEnclosingCircle(contours_poly[i],center[i],
                  radius[i]);
          
              }
              //绘制多边形轮廓+包围的矩形框+圆形框
              Mat drawing = Mat::zeros(threshold_output.size(),CV_8UC3);
              for(int unsigned i = 0; i<contours.size(),i++)
              {   
                  Scalar color= Scalar(g_rng.uniform(0,255),
                  g_rng.uniform(0,255),g_rng.uniform(0,255));//设置随机颜色
          
                  drawContours(drawing, contours_poly,i,color,1,8,
                  vector<Cec4i>(),0,Point());//绘制轮廓
          
                  rectangle(drawing,boundRect[i].t1(),boundRect[i].br(),
                  color,2,8,0);// 绘制矩形
                  
                  circle(drawing,center[i],(int)radius[i],color,2,8,0);//绘制圆
          
                  namedWindow(WINDOW_NAME1,Window_AUTOSIZE);
                  imshow(WINDOW_NAME2,drawing);
                  
              }
          }

      • 8.4、图像的矩

      • 矩函数在模式识别、目标分类、目标识别和方位估计、图像编码与重构应用。一个从数字图形中计算出来的矩集,描述了图像形状的全局特征,并提供了大量点的关于该图像不同类型的几何特性信息(大小、位置、方向、形状)。一阶矩与形状有关,二阶矩显示曲线围绕直线平均值的扩展程度,三阶矩则是关于平均值的对称性的测量。由二阶和三阶矩可以导出7组不变矩。
        • 8.4.1、矩的计算:moments()函数

        • Moments moments(InputArray array, bool binaryImage=false)

          1:输入参数;2:默认为false,为true时所有非零像素为1

        • 8.4.2、计算轮廓面积:contourArea()函数

        • double contourArea(InputArray contour,bool oriented=false)

          1:输入参数;2:面向区域标识符,true时返回一个带符号的面积值,正负值取决于轮廓的方向,根据这个特性可以根据面积的符号来确定轮廓的位置。这个参数有默认值false, 表示以绝对值返回,不带符号。

        • 8.4.3、计算轮廓长度:arcLengeh()函数

        • double arcLength(InputArray curve,bool closed)

           1:输入数据;2:用于指示曲线是否封闭的标识符,默认值closed曲线关闭

        • 8.4.4、查找和绘制图像轮廓矩

        • #include <opencv2/opencv.hpp> 
          #include<opencv2/ highgui/highgui .hpp> 
          #include<opencv2/imgproc/imgproc.hpp> 
          using namespace cv; 
          using namespace std;
          
          #define WINDOW_NAME1 "【原始图窗口】"
          #define WINDOW_NAME2 "【图像轮廓】"
          
          Mat g_srcImage;Mat g_grayImage;
          int g_nThresh=50;
          int g_maxThresh=255;
          RNG g_rng(12345);
          Mat g_cannyMat_output;
          vector<vector<Point>> g_vContours;
          vector<Vec4i> g_vHierarchy;
          
          void on_ContoursChange(int , void*);
          static void ShowHelpText();
          
          int main()
          {
              system("color 1A");
          
              g_srcImage = imread("1.jpg",1);
          
              cvtColor( g_srcImage, g_grayImage,COLOR_BGR2GRAY);
              blur(g_grayImage,g_grayImage,Size(3,3));
          
              namedWindow(WINDOW_NAME1,WINDOW_AUTOSIZE);
              imshow(WINDOW_NAME1,g_srcImage);
              createTrackbar("阈值:",WINDOW_NAME1,&g_nThresh,g_maxThresh,
              on_ContoursChange);
              on_ContoursChange(0,0);
              waitKey(0);
              return 0;    
          
          }
          
          void on_ContoursChange(int, void *)
          {
          
          
              //检测边缘
              Canny(g_grayImage,g_cannyMat_output,
              g_nThresh,g_nThresh*2,3);
          
              //找出轮廓
              findContours(threshold_output, contours,hierarchy,
              RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
          
              //计算矩
              vector<Moments>mu(g_vContours.size());
              for(unsigned int i=0;i<g_vContours.size();i++);
              {
                  mu[i]=moments(g_vContours[i],false);
              }
              //计算中心矩
              vector<Point2f> mc(g_vContours.size());
              for(unsigned int i=0;i<g_vContours.size();i++)
              {
                  mc[i] =Point2f(static_cast<float>
                  (mu[i].m10/mu[i].m00),static_cast<float>
                  (mu[i].m01/mu[i].m00))
              }
              //绘制轮廓
              Mat drawing = Mat::zeros(g_cannyMat_output.size(),CV_8UC3);
              for(unsigned int i=0;i<g_vContours.size();i++)
              {
                  Scalar color= Scalar(g_rng.uniform(0,255),
                  g_rng.uniform(0,255),g_rng.uniform(0,255));//设置随机颜色
                  drawContours(drawing, g_vContours0,i,color,2,8,
                  g_vHierarchy,0,Point());//绘制外层和内层轮廓
                  circle(drawing,mc[i],4,color,-1,8,0);//绘制圆+
          
                  namedWindow(WINDOW_NAME2,Window_AUTOSIZE);
                  imshow(WINDOW_NAME2,drawing);
          
                  //通过m00计算轮廓面积并且和OpenCV函数比较
                  printf("\t 输出内容:面积和轮廓长度\n");
                  for(unsigned int i =0;i<g_vContours.size();i++)
                  {
                      printf(">通过m00计算出轮廓【%d】的面积:(m_00)=%.2f \n 
                      OpenCV 函数计算的面积=%0.2f,长度: %0.2f, \n\n",i,mu[i].m00,
                      contourArea(g_vContours[i]),arcLength(g_vContours[i],true));
          
                      Sclar color = Scalar(g_rng.uniform(0,255),g_rng.uniform(0,255),
                      g_rng.uniform(0,255));
                      drawContours(drawing,g_vContours,i,color,2,8,g_vHierarchy,
                      0,Point());
                      circle(drawing.mc[i],4,color,-1,8,0);
                  }
          
              }
              

      • 8.5、分水岭算法

        • 此算法可以将图像中的边缘转化为”山脉“,将均匀区域转化为”山谷“,有助于分割目标。分水岭算法,是一种基于拓扑理论的数学形态学的分割方法,基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,形成分水岭。

        • 分水岭的计算过程是一个迭代标注过程。分为两个步骤:排序和淹没。首先对每个像素的灰度级进行从低到高的排序然后在从低到高实现淹没的过程中对每一个局部极小值在h阶高度的影响域采用先进先出结构进行判断和标注。分水岭得到的是输入图像的集水盆图像,表示的是输入图像的极大值点。

        • 分水岭算法首先计算灰度图像的梯度;然后从指定点开始持续灌注盆地直到这些区域连成一片。基于这样产生的标记就可以把区域合并到一起,合并后的区域又通过聚集的方式进行分割。

        • 8.5.1、实现分水岭算法:watershed()算法

          • 此函数实现的算法是基于标记的分割算法的一部分;在把图像传给函数之前,需要大致勾画标记出图像中的期望进行分割的区域,被标记为正指数。所以每一个预取都会被标记为像素值1、2、3等,表示成为一个或多个连接组件。这些标记的值可以使用findContours函数和drawContours函数由二进制的掩码检索出来。这些标记就是即将绘制出来的分割区域的”种子“,而没有标记清楚的区域被置0。
          • void watershed(InputArray image, InputOutputArray markers)

            1:输入图像;2:函数运算后的结果。

        • 8.5.2、分水岭算法

        • #include <opencv2/opencv.hpp> 
          #include<opencv2/ highgui/highgui .hpp> 
          #include<opencv2/imgproc/imgproc.hpp> 
          using namespace cv; 
          using namespace std;
          
          #define WINDOW_NAME "【程序窗口】"
          
          
          Mat g_maskImage,g_srcImage;
          Point prevPt(-1,-1);
          
          static void on_Mouse(int event, int x, int y,
          int flags,void *);
          static void ShowHelpText()
          
          int main(int argc, char**argv)
          {
             
          
              g_srcImage = imread("1.jpg",1);
              imshow(WINDOW_NAME,g_srcImage); 
              Mat srcImage,grayImage;
              g_srcImage.copyTo(srcImage);
          
              cvtColor( g_srcImage, g_maskImage,COLOR_BGR2GRAY);
              cvtColor( g_maskImage, grayImage,COLOR_BGR2GRAY);
              g_maskImage = Scalar::all(0);
              //设置鼠标回调函数
              setMouseCallback(WINDOW_NAME,on_Mouse,0);
          
          
              while(1)
              {
                  int c= waitKey(0);
                  if((char) c ==27)
                  {
                      cout<<"程序退出。。。。。。。\n";
                      break;
                  }
                  //恢复原图
                  if((char) c =='2')
                  {
                      g_maskImage = Sclar::all(0);
                      srcImage.copyTo(g_srcImage);
                      imshow("image",g_srcImage);
          
                  }
                  //进行处理
                  if((char)c == '1' || (char)c == ' ')
                  {
                      int i,j,compCount=0;
                      vector<vector<Point>> contours;
                      vector<Vec4i> hierarchy;
          
                      //寻找轮廓
                      findContours(g_maskImage,contours,hierarchy,
                      CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
          
                      //轮廓为空时处理
                      if(contours.empty())
                          continue;
          
                      //复制掩膜
                      Mat maskImage(g_maskImage.size(),CV_32S);
                      maskImage = Scalar::all(0);
          
                      //循环绘制出轮廓
                      for(int index = 0; index>=0;index=hierarchy[index][0],
                      compCount++)
                          drawContours(maskImage,contours,index,
                          Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX);
          
                      //compCount为0时的处理
                      if(compCount ==0)
                          continue;
                      
                      //生成随机颜色
                      vector<Vec3b> colorTab;
                      for(i=0;i<compCount;i++)
                      {
                          int b = theRNG().uniform(0,255);
                          int q = theRNG().uniform(0,255);
                          int r = theRNG().uniform(0,255);
                          colorTab.push_back(Vec3b((uchar)b,
                          (uchar)g,(uchar)r));
                      }
          
                      //计算处理时间并输出到窗口
                      double dTime = (double) getTickCount();
                      watershed(srcImage,maskImage);
                      dTime = (double )getTickCount()-dTime;
                      printf("\t处理时间= %gms\n",
                      dTime*1000./getTickFrequency());
          
                      //双层循环,将分水岭图像遍历存入watershedImage
                      Mat watershedImage(maskImage.size(),CV_8UC3);
                      for(int i=0;i<maskImage.rows;i++)
                      {
                          for(int j=0;j<maskImage.cols;j++)
                          {
                              int index = maskImage.at<int>(i,j);
                              if(index==-1)
                                  watershedImage.at<Vec3b>(i,j)=
                                  Vec3b(255,255,255);
                              else if(index<=0 || index>compCount)
                                  watershedImage.at<Vec3b>(i,j)=
                                  Vec3b(0,0,0);
                              else 
                              watershedImage.at<Vec3b>(i,j)=
                                  colorTab[index-1];
                          }
                          //混合灰度图和分水岭效果图并显示
                          watershedImage = watershedImage*0.5+grayImage*0.5;
                          imshow("watershed transform",watershedImage);
                      }
                  }
          
                  return 0;
          
              }
          
          
              return 0;    
          
          }
          
           static void on_Mouse(int event, int x,int y ,int flags, void*)
           {
              //处理鼠标不在窗口中的情况
              if(x<0 || x>=g_srcImage.cols ||y<0||y>=g_srcImage.rows)
                  return;
              //处理鼠标左键相关信息
              if(event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
                  prevPt = Point(-1,-1);
              else if(event == EVENT_LBUTTONDOWN)
                  prevPt = Point(x,y);
              
              //鼠标左键按下并移动,绘制出白色线条
              else if(event == EVENT_MOUSEMOVE && (flags &EVENT_FLAG_LBUTTON))
              {
                  Point pt(x,y);
                  if(prevPt.x<0)
                      prevPt = pt;
                  line(g_maskImage,prevPt,pt,Scalar::all(255),5,8,0);
                  line(g_srcImage,prevPt,pt,Scalar::all(255),5,8,0);
                  prevPt = pt;
                  imshow(WINDOW_NAME,g_srcImage);
              }
           }
          
          
          
          static void ShowHelpText()
          {
              printf("\n\n\n\t欢迎来到分水岭示例程序···\n\n");
              printf("\t当前opencv版本:"CV_VERSION);
              printf("\n\n\t按键操作说明:\n\n"
             // "\t\t 【esc】---退出程序"
              "\t\t 【1】or 【space】 ---运行的分水岭分割算法"
              "\t\t 【2】 ---恢复原图"
             // "\t\t 【3】 ---第3种映射"
              "\t\t 【esc】 ---退出程序")
          }

      • 8.6、图像修补

        • 利用那些已经被破坏区域的地缘,即边缘的颜色和结构,繁殖和混合到 损坏的图像中,以达到图像修补的问的。
        • 8.6.1、实现图像修补:inpaint()函数

        • void inpaint(InputArray src,InputArray inpaintMask,
          OutputArray dst,double inpaintRadius,int flags)

          1:输入图像;2:修复掩膜,其中的非零像素表示要修补的区域;3:函数调用后的运算结果存在这里;4:需要修补的每个点的圆形邻域, 为修复算法的参考半径;5:修补方法的标识符

        • 8.6.2、图像修补示例

        • #include <opencv2/opencv.hpp> 
          #include<opencv2/ highgui/highgui .hpp> 
          #include<opencv2/imgproc/imgproc.hpp> 
          using namespace cv; 
          using namespace std;
          
          #define WINDOW_NAME1 "【原始图】"
          
          #define WINDOW_NAME2 "【修补后的图】"
          
          
          Mat srcImage1,inpaintMask;
          Point previousPoint(-1,-1);
          
          static void on_Mouse(int event, int x, int y,
          int flags,void *);
          static void ShowHelpText()
          
          int main(int argc, char**argv)
          {
             
          
              Mat srcImage = imread("1.jpg",-1);
              srcImage1 = srcImage.clone();
              inpaintMask = Mat::zeros(srcImage1.size(),CV_8U);
          
              imshow(WINDOW_NAME1,srcImage1); 
            
              //设置鼠标回调函数
              setMouseCallback(WINDOW_NAME1,on_Mouse,0);
          
          
              while(1)
              {
                  int c= (char) waitKey();
                  if((char) c ==27)
                  {
                      cout<<"程序退出。。。。。。。\n";
                      break;
                  }
                  //恢复原图
                  if((char) c =='2')
                  {
                      inpaintMask = Scalar::all(0);
                      srcImage.copyTo(srcImage1);
                      imshow(WINDOW_NAME1, srcImage1);
          
                  }
                  //进行处理
                  if((char)c == '1' || (char)c == ' ')
                  {
                      Mat inpaintedImage;
                      inpaint(srcImage1,inpaintMask,inpaintedImage,3
                      INPAINT_TELEA);
                      imshow(WINDOW_NAME2, inpaintedImage);
                  }
          
                  return 0;
          
              }
          
          
              return 0;    
          
          }
          
           static void on_Mouse(int event, int x,int y ,int flags, void*)
           {
            
              //处理鼠标左键相关信息
              if(event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
                  previousPoint = Point(-1,-1);
              else if(event == EVENT_LBUTTONDOWN)
                  previousPoint = Point(x,y);
              
              //鼠标左键按下并移动,绘制出白色线条
              else if(event == EVENT_MOUSEMOVE && (flags &EVENT_FLAG_LBUTTON))
              {
                  Point pt(x,y);
                  if(previousPoint.x<0)
                      previousPoint = pt;
                  line(inpaintMask,previousPoint,pt,Scalar::all(255),5,8,0);
                  line(srcImage1,previousPoint,pt,Scalar::all(255),5,8,0);
                  previousPoint = pt;
                  imshow(WINDOW_NAME1,srcImage1);
              }
           }
          
          
          
          static void ShowHelpText()
          {
              printf("\n\n\n\t欢迎来到图像修补示例程序···\n\n");
              printf("\t当前opencv版本:"CV_VERSION);
              printf("\n\n\t按键操作说明:\n\n"
             "\t\t 【esc】---退出程序"
              "\t\t 【1】or 【space】 ---进行图像修复操作"
              "\t\t 【鼠标左键】 ---在图像上绘制白色线条"
             // "\t\t 【3】 ---第3种映射"
              //"\t\t 【esc】 ---退出程序")
          }

      • 8.7、本章小结

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值