C++ Opencv imfill孔洞填充函数的实现思路与代码

目录
  • 函数实现的中心思想
    • 二值图
    • 寻找连通域的关键
    • 种子点的确定
    • 连通域的寻找过程
    • 条件设定
    • 最后赋值
  • 话不多说 直接上函数代码
    • 主函数代码
  • 代码框截图
  • 实例图片
  • 总结

函数实现的中心思想

二值图

此程序针对于二值图,寻找二值图中 像素值为0的连通域,将所有连通域的像素点分别保存下来,将符合条件的连通域的像素值 置为255;

寻找连通域的关键

针对填洞功能的实现,也就是0置为255过程,我们需要以四连通为基本点进行寻找。

种子点的确定

寻找种子点,其实就是寻找二值图中像素值为0的点,我们可以直接采取 遍历 二值图 中的像素,将第一个遇见的像素值为0的点确定为 第一个连通域的种子点。这时候,有一些朋友可能会疑惑,因为按照我的说法,在遍历 的过程中,遇见的第n个像素值为0的点 就是第n个连通域的种子点,进一步说,在整个遍历过程中,遇见像素值为0的像素点的个数,就是连通域的个数。
是的!

当然,如果要实现这一点,那我们就需要在各个连通域的寻找的过程中,将找到的点全部立即置为255,(此处不一定非得是255,只要不是0即可)这样在寻找结束后,我们再遍历二值图时,已经找到的连通域中的所有像素点的值均为255,当再次找到像素值为0 的像素点时,此像素点必是下一个待寻找的连通域的种子点

连通域的寻找过程

首先创建四连通的向量,vector<Point> upp;用来存储上下前后四个点,创建vector<vector<vector<Point>>> lenm;用来存储所有的连通域,至于为什么要创建三维Point数组,大家可以先看看关于这个三维数组的注释,(下面的公式就是,程序中也有相应的注释),了解清楚每一维代表的意义,再结合一下程序,我感觉大家应该可以明白,再简要赘述一下,lenm.size()为连通域的个数。

如图所示;函数为第i个连通域像素点个数的求和。

条件设定

在经过以上的寻找过程后,得到的结果必然是全白的图像,而我们只想要填充孔洞,所以我们需要去除不符合的连通域。所谓孔洞,其实就是周围被像素值为255的点包围起来的连通域,但是,有一些连通域,直接和图像的边界相连,而这并不是我们想要的, 至少不是我想要的,(如果大家有不同的需求,程序也是很容易改过去的)。所以,我需要一个标志位,当这个连通域中的像素点接触到边界后,给这个连通域一个标记。在下面的程序中,我用vector<vector<int>> Flag;来存储标记点,其中Flag[i]表示第i个连通域的标记点。在程序中,找到种子点后,首先将第i个连通域的Flag[i][0] = 1;,如果在此连通域中出现边界点,再Flag[i][0] = 0;(在程序中,此处貌似有一个小BUG,我就先不改了[😏])

最后赋值

在寻找到的所有连通域中,Flag[i][0] == 1; {其中 i 属于 [0,Flag.size()) }的连通域为符合要求的连通域,因此将lenm[i];中的所有像素点赋值255即可。

话不多说 直接上函数代码

输入二值图;

返回二值图;

Mat imfill(Mat cop)
{
	Mat fcop;
	cop.copyTo(fcop);
	vector<Point> upp;//定义四连通点集,有必要可以是八连通
	upp.push_back(Point(-1, 0));
	upp.push_back(Point(0, -1));
	upp.push_back(Point(0, 1));
	upp.push_back(Point(1, 0));
	//upp.push_back(Point(1, 1));
	//upp.push_back(Point(-1,-1));
	//upp.push_back(Point(-1, 1));
	//upp.push_back(Point(1, -1));

	vector<vector<vector<Point>>> lenm;//三维point向量 lenm.size()是连通域的个数
	/*
	int impixel_sum = 0;
	for (int j = 0,j<lenm[i].size();j++)
	{
	    impixel_sum+= lenm[i][j].size();
	}
	//这段循环 表示第i个连通域中 像素点的个数。
	*/
	vector<vector<Point>> numim;
	vector<Point> ssinum;
	vector<vector<int>> Flag;
	vector<int> ce;

	int nmss = 0;//连通域的个数;
	int nums = 0;//中间变量 用来存储 lenm.size();即 在程序运行过程中 nums始终等于 lenm[i][j][k] 中的j 的 值的大小;
	int s1 = 0;
	//标志位 ,每次区域生长后 符合条件的像素个数,当第i个连通域,在经过第j次生长后,s1=lenm[i][j].size(),
	//若s1==0,表示生长结束,不再有符合条件的点,第i连通域中的所有点都已经找到。

	for (int row = 0; row < fcop.rows; row++)
	{
		for (int col = 0; col < fcop.cols; col++)
		{
			if (fcop.at<uchar>(row, col) == 0)
			{
				ce.push_back(1);
				Flag.push_back(ce);
				//vector<vector<Point>> numim;
				//vector<Point> ssinum;
				ssinum.push_back(Point(col, row));
				numim.push_back(ssinum);
				fcop.at<uchar>(row, col) = 255;
				ssinum.clear();
				s1 = 1;
				while (s1 > 0)
				{
					//ce.push_back(1);
					//Flag.push_back(ce);
					//vector<Point> ssinum;
					for (int i = 0; i < numim[nums].size(); i++)
					{
						for (int j = 0; j < upp.size(); j++)
						{
							int X = numim[nums][i].x + upp[j].x;
							int Y = numim[nums][i].y + upp[j].y;
							if (X >= 0 && Y >= 0 && X < fcop.cols && Y < fcop.rows)
							{
								if (fcop.at<uchar>(Y, X) == 0)
								{
									ssinum.push_back(Point(X, Y));
									fcop.at<uchar>(Y, X) = 255;
								}
							}

							if (X == 0 || Y == 0 || X == fcop.cols - 1 || Y == fcop.rows - 1)
							{
								Flag[nmss][0] = 0;

							}
						}
					}
					//Flag.push_back(ce);
					numim.push_back(ssinum);
					s1 = ssinum.size();
					nums++;
					ssinum.clear();
					/*ce.clear();*/
				}
				nums = 0;
				lenm.push_back(numim);
				numim.clear();
				nmss++;
				ce.clear();
			}

		}
	}
	//imshow("1",fcop);
	Mat ffcop;
	cop.copyTo(ffcop);
	//ffcop = Mat::zeros(cop.size(),cop.type());
	for (int i = 0; i < Flag.size(); i++)
	{
		if (Flag[i][0] == 1)
		{
			for (int j = 0; j < lenm[i].size(); j++)
			{
				for (int k = 0; k < lenm[i][j].size(); k++)
				{
					int X = lenm[i][j][k].x;
					int Y = lenm[i][j][k].y;
					ffcop.at<char>(Y, X) = 255;
				}
			}
		}
	}
	return ffcop;
}

主函数代码

#include<opencv2/opencv.hpp>
#include<iostream>

#include"imfill.h"

using namespace std;
using namespace cv;

Mat src;
vector<vector<Point>>  lunk;
vector<Vec4i> level;

//RNG rn;
int main()
{
	src = imread("5.jpg");
	//imshow("万丈高楼第一步",src);

	Mat dst, gray, erzhi;
	blur(src, dst, Size(3, 3), Point(-1, -1));
	//imshow("均值滤波",dst);

	cvtColor(dst, gray, COLOR_BGR2GRAY);
	//imshow("灰度图",gray);

	Canny(gray, erzhi, 100, 200, 3, false);
	//imshow("边缘检测",erzhi);

	Mat holef;
	holef = imfill(erzhi);
	imshow("填洞", holef);

	waitKey(0);
	return 0;
}

代码框截图

实例图片

运行结果

总结

此程序会填充所有的孔洞,如果想只填充指定阈值范围内的孔洞,需要再多计算每个连通域像素的个数即可,因为所有数据都已经存了下来,所以计算会比较简单。

到此这篇关于C++ Opencv imfill孔洞填充函数的文章就介绍到这了,更多相关C++ Opencv imfill孔洞填充函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ Opencv imfill孔洞填充函数的实现思路与代码

    目录 函数实现的中心思想 二值图 寻找连通域的关键 种子点的确定 连通域的寻找过程 条件设定 最后赋值 话不多说 直接上函数代码 主函数代码 代码框截图 实例图片 总结 函数实现的中心思想 二值图 此程序针对于二值图,寻找二值图中 像素值为0的连通域,将所有连通域的像素点分别保存下来,将符合条件的连通域的像素值 置为255: 寻找连通域的关键 针对填洞功能的实现,也就是0置为255过程,我们需要以四连通为基本点进行寻找. 种子点的确定 寻找种子点,其实就是寻找二值图中像素值为0的点,我们可以直接

  • opencv形态学中的孔洞填充详细图解

    目录 1. 原理 2. 漫水填充算法 1. 原理 孔洞指的是被前景像素点或者说感兴趣的像素点包围起来的区域,这个区域是我们不感兴趣的背景区域. 数字图像处理的孔洞填充的公式为: I 为前景像素 ,c 为补集 其实孔洞填充的步骤就是一个迭代的过程: 先设置一个填充的起始点,需要在孔洞的内部.如d图所示,然后被结构元B(图c)膨胀. 然后,为了将膨胀的结果限制在孔洞内部,需要和原图(图a)的补集(图b)相交(图e),因为如果不控制膨胀的结果的话,那么膨胀会填充整个区域,而膨胀结果和原图的补集相交,会

  • python opencv 图像边框(填充)添加及图像混合的实现方法(末尾实现类似幻灯片渐变的效果)

    图像边框的实现 图像边框设计的主要函数 cv.copyMakeBorder()--实现边框填充 主要参数如下: 参数一:源图像--如:读取的img 参数二--参数五分别是:上下左右边的宽度--单位:像素 参数六:边框类型: cv.BORDER_CONSTANT--cv.BORDER_REPLICATE--cv.BORDER_REFLECT--cv.BORDER_WRAP--cv.BORDER_REFLECT_101--cv.BORDER_TRANSPARENT--cv.BORDER_REFLEC

  • OpenCV 基本图形绘制函数详解

    用于绘制直线的line函数: 用于绘制椭圆的ellipse函数: 用于绘制矩形的rectangle函数: 用于绘制圆的circle函数: 用于绘制填充的多边形的fillPoly函数. #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; using namespac

  • Python OpenCV简单的绘图函数使用教程

    目录 1.画直线的函数是cv2.line 2.画矩形的函数是cv2.rectangle 3.画圆函数是cv2.circle 4.画椭圆的函数是cv2.elipes 5.画多边形的函数是cv2.polylines 6.添加文字的函数是cv2.putText 1.画直线的函数是cv2.line cv2.line函数语法: cv2.line(img,start_point,end_point,color,thickness=0) cv2.line函数参数解释: img:需要画的图像 start_poi

  • python+opencv边缘提取与各函数参数解析

    前情提要:作为刚入门机器视觉的小伙伴,第一节课学到机器视觉语法时觉得很难理解, 很多人家的经验,我发现都千篇一律,功能函数没解析,参数不讲解,就一个代码,所以在此将搜集的解析和案例拿出来汇总!!! 一.opencv+python环境搭建 其实能写python的就能写opencv,但是工具很总要,代码提示也很重要,你可能会用submit     vs等工具,submit编码个人觉得不够智能,vs的话过完年我学的方向不一致,所以没用 推荐 pycharm ,在项目setting中的项目解释器中安装

  • 浅谈OpenCV中的新函数connectedComponentsWithStats用法

    主要内容:对比新旧函数,用于过滤原始图像中轮廓分析后较小的区域,留下较大区域. 关键字:connectedComponentsWithStats 在以前,常用的方法是"是先调用 cv::findContours() 函数(传入cv::RETR_CCOMP 标志),随后在得到的连通区域上循环调用 cv::drawContours() " 比如,我在GOCVHelper中这样进行了实现 //寻找最大的轮廓 VP FindBigestContour(Mat src){ int imax =

  • pytorch中的nn.ZeroPad2d()零填充函数实例详解

    在卷积神经网络中,有使用设置padding的参数,配合卷积步长,可以使得卷积后的特征图尺寸大小不发生改变,那么在手动实现图片或特征图的边界零填充时,常用的函数是nn.ZeroPad2d(),可以指定tensor的四个方向上的填充,比如左边添加1dim.右边添加2dim.上边添加3dim.下边添加4dim,即指定paddin参数为(1,2,3,4),本文中代码设置的是(3,4,5,6)如下: import torch.nn as nn import cv2 import torchvision f

  • opencv中颜色空间转换函数cv2.cvtColor()使用

    opencv中有多种色彩空间,包括 RGB.HSI.HSL.HSV.HSB.YCrCb.CIE XYZ.CIE Lab8种,使用中经常要遇到色彩空间的转化,以便生成mask图等操作. 可以使用下面的色彩空间转化函数 cv2.cvtColor( )进行色彩空间的转换: HSV 表示hue.saturation.value image_hsv = cv2.cvtColor(image,cv2.COLOR_BGR2HSV) 用这个函数把图像从RGB转到HSV夜色空间,注意是BGR2HSV,因为在ope

  • 使用OpenCV对车道进行实时检测的实现示例代码

    项目介绍 下图中的两条线即为车道: 我们的任务就是通过 OpenCV 在一段视频(或摄像头)中实时检测出车道并将其标记出来.其效果如下图所示: 这里使用的代码来源于磐怼怼大神,此文章旨在对其代码进行解释. 实现步骤 1.将视频的所有帧读取为图片: 2.创建掩码并应用到这些图片上: 3.图像阈值化: 4.用霍夫线变换检测车道: 5.将车道画到每张图片上: 6.将所有图片合并为视频. 代码实现 1.导入需要的库 import os import re import cv2 import numpy

随机推荐