开发环境:Ubuntu 18.04 LTS + ROS Melodic + ViSP 3.3.1
文章内容主要参考ViSP官方教学文档:https://visp-doc.inria.fr/doxygen/visp-daily/tutorial_mainpage.html

  本文主要介绍了如何使用ViSP实现泛洪算法,所谓泛洪算法就是在一个封闭图形中从一个像素点出发,以此向周边的像素点扩充着色,直到图形的边界。本文主要参考了imgprco中的tutorial-flood-fill.cpp例程。首先要获取这个例程文件并编译它

svn export https://github.com/lagadic/visp.git/trunk/tutorial/imgproc
cd imgproc/flood-fill
mkdir build
cd build 
cmake .. -DCMAKE_BUILD_TYPE=Release -DVISP_DIR=$VISP_WS/visp-build
make 

  执行例程,查看效果

./tutorial-flood-fill

  使用鼠标左键在图像中随意点一些点,作为多边形的顶点
在这里插入图片描述
  点击鼠标右键,连接各个顶点,构成多边形
在这里插入图片描述
  再次点击鼠标左键绘制新的多边形,一共需要绘制三次。最后在任意闭合区域内点击左键进行填充
在这里插入图片描述
  下面介绍一下代码实现过程

#include <cstdlib>
#include <iostream>
#include <visp3/core/vpImage.h>
#include <visp3/gui/vpDisplayGDI.h>
#include <visp3/gui/vpDisplayOpenCV.h>
#include <visp3/gui/vpDisplayX.h>

#if defined(VISP_HAVE_MODULE_IMGPROC) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV))
//! [Include]
#include <visp3/imgproc/vpImgproc.h>
//! [Include]

namespace
{
//! [Bresenham's line algorithm]
//定义Bresenham's line算法函数,用于绘制多边形连线
vpImagePoint switchToOctantZeroFrom(const int octant, const vpImagePoint &imPt)
{
  vpImagePoint imPt_switched = imPt;

  switch (octant) {
  case 0: // return (x, y)
    imPt_switched.set_uv(imPt.get_u(), imPt.get_v());
    break;

  case 1: // return (y, x)
    imPt_switched.set_uv(imPt.get_v(), imPt.get_u());
    break;

  case 2: // return (y, -x)
    imPt_switched.set_uv(imPt.get_v(), -imPt.get_u());
    break;

  case 3: // return (-x, y)
    imPt_switched.set_uv(-imPt.get_u(), imPt.get_v());
    break;

  case 4: // return (-x, -y)
    imPt_switched.set_uv(-imPt.get_u(), -imPt.get_v());
    break;

  case 5: // return (-y, -x)
    imPt_switched.set_uv(-imPt.get_v(), -imPt.get_u());
    break;

  case 6: // return (-y, x)
    imPt_switched.set_uv(-imPt.get_v(), imPt.get_u());
    break;

  case 7: // return (x, -y)
    imPt_switched.set_uv(imPt.get_u(), -imPt.get_v());
    break;

  default:
    break;
  }

  return imPt_switched;
}

vpImagePoint switchFromOctantZeroTo(const int octant, const vpImagePoint &imPt)
{
  vpImagePoint imPt_switched = imPt;

  switch (octant) {
  case 0: // return (x, y)
    imPt_switched.set_uv(imPt.get_u(), imPt.get_v());
    break;

  case 1: // return (y, x)
    imPt_switched.set_uv(imPt.get_v(), imPt.get_u());
    break;

  case 2: // return (-y, x)
    imPt_switched.set_uv(-imPt.get_v(), imPt.get_u());
    break;

  case 3: // return (-x, y)
    imPt_switched.set_uv(-imPt.get_u(), imPt.get_v());
    break;

  case 4: // return (-x, -y)
    imPt_switched.set_uv(-imPt.get_u(), -imPt.get_v());
    break;

  case 5: // return (-y, -x)
    imPt_switched.set_uv(-imPt.get_v(), -imPt.get_u());
    break;

  case 6: // return (y, -x)
    imPt_switched.set_uv(imPt.get_v(), -imPt.get_u());
    break;

  case 7: // return (x, -y)
    imPt_switched.set_uv(imPt.get_u(), -imPt.get_v());
    break;

  default:
    break;
  }

  return imPt_switched;
}

int getOctant(const vpImagePoint &imPt1, const vpImagePoint &imPt2)
{
  double dx = imPt2.get_u() - imPt1.get_u();
  double dy = imPt2.get_v() - imPt1.get_v();

  if (dx >= 0 && dy >= 0) {
    if (dy >= dx) {
      return 1;
    } else {
      return 0;
    }
  } else if (dx < 0 && dy >= 0) {
    if (-dx >= dy) {
      return 3;
    } else {
      return 2;
    }
  } else if (dx < 0 && dy < 0) {
    if (dy <= dx) {
      return 5;
    } else {
      return 4;
    }
  } else {
    if (dx >= -dy) {
      return 7;
    } else {
      return 6;
    }
  }
}

void drawLine(vpImage<unsigned char> &I, const unsigned char value, const vpImagePoint &imPt1_,
              const vpImagePoint &imPt2_)
{
  vpImagePoint imPt1((int)imPt1_.get_v(), (int)imPt1_.get_u());
  vpImagePoint imPt2((int)imPt2_.get_v(), (int)imPt2_.get_u());

  int octant = getOctant(imPt1, imPt2);
  imPt1 = switchToOctantZeroFrom(octant, imPt1);
  imPt2 = switchToOctantZeroFrom(octant, imPt2);

  double dx = imPt2.get_u() - imPt1.get_u();
  double dy = imPt2.get_v() - imPt1.get_v();
  double D = 2 * dy - dx;
  double y = imPt1.get_v();

  for (int x = (int)imPt1.get_u(); x <= (int)imPt2.get_u(); x++) {
    vpImagePoint currentPt(y, x);
    currentPt = switchFromOctantZeroTo(octant, currentPt);

    unsigned int i = std::min(I.getHeight() - 1, (unsigned int)std::max(0.0, currentPt.get_i()));
    unsigned int j = std::min(I.getWidth() - 1, (unsigned int)std::max(0.0, currentPt.get_j()));
    I[i][j] = value;

    if (D >= 0) {
      y++;
      D -= dx;
    }

    D += dy;
  }
}
//! [Bresenham's line algorithm]
} // namespace

#endif

int main()
{
//! [Macro defined]
#if defined(VISP_HAVE_MODULE_IMGPROC) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV))
  //! [Macro defined]

  //! [Create bitmap]
  vpImage<vpRGBa> I(480, 640, vpRGBa());
//! [Create bitmap]

#ifdef VISP_HAVE_X11
  vpDisplayX d;
#elif defined(VISP_HAVE_GDI)
  vpDisplayGDI d;
#elif defined(VISP_HAVE_OPENCV)
  vpDisplayOpenCV d;
#endif
  d.init(I, 0, 0, "Paint");

  //! [Draw polygons]
  std::vector<vpPolygon> polygons;
  //绘制多边形
  for (int i = 0; i < 3; i++) {
    vpDisplay::display(I);
    std::stringstream ss;
    ss << "Left click to draw polygon " << i + 1 << "/3"
       << ", right click to close the shape.";
    vpDisplay::displayText(I, 20, 20, ss.str(), vpColor::red);
    vpDisplay::flush(I);

    vpPolygon polygon;
    polygon.initClick(I);//获取多边形顶点
    polygons.push_back(polygon);

    vpDisplay::display(I);
    vpDisplay::displayLine(I, polygon.getCorners(), true, vpColor::red);
    vpDisplay::flush(I);

    // Update the lines draw internally in the current image
    vpDisplay::getImage(I, I);
  }
  //! [Draw polygons]

  //! [Draw polygon lines]
  vpImage<unsigned char> mask(I.getHeight(), I.getWidth(), 0);
  for (size_t i = 0; i < polygons.size(); i++) {
    if (polygons[i].getCorners().size() <= 1)
      continue;

    for (size_t j = 0; j < polygons[i].getCorners().size() - 1; j++)
      drawLine(mask, 255, polygons[i].getCorners()[j], polygons[i].getCorners()[j + 1]);//绘制多边形连线

    drawLine(mask, 255, polygons[i].getCorners().front(), polygons[i].getCorners().back());
  }
  //! [Draw polygon lines]

  bool quit = false;
  while (!quit) {
    vpDisplay::display(I);
    vpDisplay::displayText(I, 20, 20,
                           "Left click on a pixel location to fill the "
                           "shape, right click to quit.",
                           vpColor::red);
    vpDisplay::flush(I);

    //! [Seed point click]
    vpImagePoint ip;
    vpMouseButton::vpMouseButtonType button;
    if (vpDisplay::getClick(I, ip, button, false))//鼠标点击获取种子像素点位置
    //! [Seed point click]
    {
      switch (button) {
      case vpMouseButton::button1:
        //! [Flood fill]
        vp::floodFill(mask, ip, 0, 255, vpImageMorphology::CONNEXITY_4);//根据种子像素点位置执行泛洪算法
        //! [Flood fill]

        //! [Bucket fill]
        for (unsigned int cpt = 0; cpt < mask.getSize(); cpt++) {
          if (mask.bitmap[cpt])
            I.bitmap[cpt] = vpColor::red;//为执行泛洪算法的区域涂上红色
        }
        //! [Bucket fill]
        break;

      case vpMouseButton::button3:
        quit = true;
        break;

      default:
        break;
      }
    }
  }
#endif

  return EXIT_SUCCESS;
}

如果大家对于深度学习与计算机视觉领域感兴趣,希望获得更多的知识分享与最新的论文解读,欢迎关注我的个人公众号“深视”。在这里插入图片描述