开发环境: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;
}
如果大家对于深度学习与计算机视觉领域感兴趣,希望获得更多的知识分享与最新的论文解读,欢迎关注我的个人公众号“深视”。
评论(0)
您还未登录,请登录后发表或查看评论