种子填充算法描述及C++代码实现
项目需要看了种子填充算法,改进了算法主要去除面积小的部分。种子填充算法分为两种,简单的和基于扫描线的方法,简单的算法如下描述(笔者针对的是二值图像):
(1)从上到下,从左到有,依次扫描每个像素;
(2)遇到一个非零数值压栈,并置原图像像素点值为0,面积初始化为1;否则,处理完毕。
(3)对栈非空查找,如果非空弹出栈顶,检测4领域或8领域,如果非空压栈,并置原图像像素点为0,标示不在处理此点,面积加1;如果为空,停止;
(4)判断面积是否大于给定阈值,小于的删掉,大于的把得到的所有像素点保存到目标图像上去,继续扫描像素,转2。
这里我用c++实现,开始用的stl栈,运行一段时间会有中断,之后换成链表可以了,代码共享如下,可以运行,图片使用二值,有需要的可以留下邮箱,一起研究:
//视频处理测试算法,种子填充算法,扫描线算法,二值图像
//用栈会保存,这里把栈换成链表了,下面有栈注释掉代码
//
#include <iostream>
#include "cv.h"
#include "highgui.h"
#include <stack>
#include <list>
#include <string> using namespace std;
int ScanLine_SeedFillingAlgo(IplImage *src,IplImage *dst,int MinCutNumb);//原图像和目标图像不要是同一副图像
int main()
{
IplImage *ipl_origin;
IplImage *ipl_target;
string fname = "D:/无腐蚀膨胀/Fight1save";
cvNamedWindow("原始图片");
cvNamedWindow("处理后图片");
for (int k=;k<;k++)
{
string filename="";
char tmp[];
_itoa_s(k,tmp,,);
filename+=tmp;
filename+=".bmp";
filename=fname+filename;
ipl_origin=cvLoadImage(filename.c_str(),-);
ipl_target=cvCreateImage(cvGetSize(ipl_origin),,);//cvCloneImage(ipl_origin); cvZero(ipl_target);
cvShowImage("原始图片",ipl_origin);
int s=clock();
ScanLine_SeedFillingAlgo(ipl_origin,ipl_target,);
int e=clock();
std::cout<<"\n"<<e-s;
cvShowImage("处理后图片",ipl_target);
cvWaitKey();
cvReleaseImage(&ipl_origin);
cvReleaseImage(&ipl_target);
} cvWaitKey(); cvDestroyWindow("原始图片");
cvDestroyWindow("处理后图片"); }
//MinCutNumb代表剔除面积小于MinCutNumb的值;
//返回找到目标数
int ScanLine_SeedFillingAlgo(IplImage *src,IplImage *dst,int MinCutNumb)
{
int i, j, k;
for ( i = ; i < ; i++ ) //上下两行
{
unsigned char * t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep ] ); for ( j = ; j < src->widthStep; j++ )
{
* t_pPos = (unsigned char);
t_pPos++;
}
} for ( i = ( src->height - ); i < src->height; i++ ) //上下两行
{
unsigned char * t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep ] ); for ( j = ; j < src->widthStep; j++ )
{
* t_pPos = (unsigned char);
t_pPos++;
}
} for ( i = ; i < src->height; i++ ) //左右两边
{
unsigned char * t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep ] ); for ( j = ; j < ; j++ )
{
* t_pPos = (unsigned char);
t_pPos++;
} t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep + src->widthStep - ] ); for ( j = ( src->widthStep - ); j < src->widthStep; j++ )
{
* t_pPos = (unsigned char);
t_pPos++;
}
}
int width = src->width;
int height = src->height;
int targetSumNumb=;
int area;
CvPoint direction_4[]={{-, }, {, }, {, }, {, -}};//上右下左
//CvPoint direction_8[] = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1} };//顺时针
int n_Count=sizeof(direction_4)/sizeof(CvPoint);//遍历方向个数
std::list<CvPoint> stk;//stl栈
std::list<CvPoint> lst;//stl链表
cvZero(dst);
//IplImage *tempimage=cvCreateImage(cvGetSize(src),8,1);//创建一个临时数据,保存源图像数据到目标过度数据
int t_i;//每次种子的位置
int t_j;
//cvZero(tempimage);//临时数据初始化,清0
for (int i=;i<height-;i++)
{
for (int j=;j<width-;j++)
{
//int s=clock(); //
if (src->imageData[i*width+j])
{
targetSumNumb++;
stk.push_back(cvPoint(i,j));//栈换成链表
lst.push_back(cvPoint(i,j));
src->imageData[i*width+j]=;//二值图像
//tempimage->imageData[i*width+j]=255;
area=;
while (!stk.empty())
{
CvPoint seed=stk.back();//弹出头部
stk.pop_back();
t_i=seed.x;
t_j=seed.y;
if (t_i<=||t_i>=height||t_j<=||t_j>=width)
continue;
for (int ii=;ii<n_Count;ii++)//扫描各个方向
{
if (src->imageData[(t_i+direction_4[ii].x)*width+t_j+direction_4[ii].y])
{
area++;
stk.push_back(cvPoint(t_i+direction_4[ii].x,t_j+direction_4[ii].y));
lst.push_back(cvPoint(t_i+direction_4[ii].x,t_j+direction_4[ii].y));
src->imageData[(t_i+direction_4[ii].x)*width+t_j+direction_4[ii].y]=;//二值图像
//tempimage->imageData[(t_i+direction_4[ii].x)*width+t_j+direction_4[ii].y]=255;
}
}
}
//int e=clock();
//std::cout<<e-s;
if (area>MinCutNumb)
{
//cvOr(dst,tempimage,dst);
while (!lst.empty())
{
CvPoint tmpPt=lst.front();
lst.pop_front();
dst->imageData[tmpPt.x*width+tmpPt.y]=;
}
}
else
{
//std::list<CvPoint>().swap(lst);
//while (!lst.empty()) lst.pop_back();
//lst.resize(0);
//lst.
lst.clear();
} }//判断是否入栈
//CvPoint }
}
//cvReleaseImage(&tempimage);
return targetSumNumb;
}
图片处理效果:
基于扫描线的算法,描述如下(也是针对二值图像编程的):
(1) 初始化一个空的栈用于存放种子点,将种子点(x, y)入栈;
(2) 判断栈是否为空,如果栈为空则结束算法,否则取出栈顶元素作为当前扫描线的种子点(x, y),y是当前的扫描线;
(3) 从种子点(x, y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xLeft和xRight;
(4) 分别检查与当前扫描线相邻的y - 1和y + 1两条扫描线在区间[xLeft, xRight]中的像素,从xLeft开始向xRight方向搜索,若存在非边界且未填充的像素点,则找出这些相邻的像素点中最右边的一个,并将其作为种子点压入栈中,然后返回第(2)步。
也是用的c++实现,代码如下:
//视频处理测试算法,种子填充算法,扫描线算法,二值图像
#include <iostream>
#include "cv.h"
#include "highgui.h"
#include <stack>
#include <list>
#include <string> using namespace std;
int ScanLine_SeedFillingAlgoE(IplImage *src,IplImage *dst,int MinCutNumb);//原图像和目标图像不要是同一副图像
int main()
{
IplImage *ipl_origin;
IplImage *ipl_target;
string fname = "C:/Users/zcx/Desktop/打架斗殴测试图片/第四次无腐蚀膨胀/Fight1save";
cvNamedWindow("原始图片");
cvNamedWindow("处理后图片");
for (int k=;k<;k++)
{
string filename="";
char tmp[];
_itoa_s(k,tmp,,);
filename+=tmp;
filename+=".bmp";
filename=fname+filename;
ipl_origin=cvLoadImage(filename.c_str(),-);
//ipl_target=cvCreateImage(cvGetSize(ipl_origin),8,1);//cvCloneImage(ipl_origin); //cvZero(ipl_target);
cvShowImage("原始图片",ipl_origin);
int s=clock();
ScanLine_SeedFillingAlgoE(ipl_origin,ipl_origin,);
int e=clock();
std::cout<<"\n"<<e-s;
cvShowImage("处理后图片",ipl_origin);
cvWaitKey(); } cvWaitKey();
cvReleaseImage(&ipl_origin);
//cvReleaseImage(&ipl_target);
cvDestroyWindow("原始图片");
cvDestroyWindow("处理后图片"); }
//MinCutNumb代表剔除面积小于MinCutNumb的值;
//返回找到目标数
int ScanLine_SeedFillingAlgoE(IplImage *src,IplImage *dst,int MinCutNumb)
{
int width = src->width;
int height = src->height;
int targetSumNumb=;//目标数
int area;//区域面积
int rcount=,lcount=;//向左向右计算像素个数
int yLeft,yRight;//左右像素坐标
//IplImage *src=cvCreateImage(cvGetSize(p_src),8,1);//cvCloneImage(p_src);
//cvCopy(p_src,src);
CvPoint direction_4[]={{-, }, {, }, {, }, {, -}};//上右下左
//CvPoint direction_8[] = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1} };//顺时针
int n_Count=sizeof(direction_4)/sizeof(CvPoint);//遍历方向个数
std::list<CvPoint> stk;//stl栈
std::list<CvPoint> lst;//stl链表 IplImage *tempimage=cvCreateImage(cvGetSize(src),,);//创建一个临时数据,保存源图像数据到目标过度数据
int t_i,t_j;//每次种子的位置
int rt_j,lt_j;//左右搜索
cvZero(tempimage);//临时数据初始化,清0
for (int i=;i<height-;i++)
{
for (int j=;j<width-;j++)
{
//int s=clock(); //
if (src->imageData[i*width+j])
{
targetSumNumb++;
stk.push_back(cvPoint(i,j));//栈换成链表
lst.push_back(cvPoint(i,j));
src->imageData[i*width+j]=;//二值图像
//tempimage->imageData[i*width+j]=255;
area=;
while (!stk.empty())
{
CvPoint seed=stk.back();//弹出头部
stk.pop_back();
t_i=seed.x;
rt_j=lt_j=t_j=seed.y;
if (t_i<=||t_i>=height)//上下扫描界限
continue;
//向右扫描
rcount=,lcount=;
while (rt_j<width)
{
//++t_j;
if (src->imageData[t_i*width+(++rt_j)])
{
rcount++;
lst.push_back(cvPoint(t_i,rt_j));
src->imageData[t_i*width+rt_j]=;//二值图像
}
else
{
break;
}
}
area+=rcount;
yRight=t_j+rcount;//右边坐标
//向左扫描
while (lt_j>)
{
//++t_j;
if (src->imageData[t_i*width+(--lt_j)])
{
lcount++;
lst.push_back(cvPoint(t_i,lt_j)); src->imageData[t_i*width+lt_j]=;//二值图像
}
else
{
break;
}
}
area+=lcount;
yLeft=t_j-lcount;//左边坐标
//上一行搜索入栈点
int up_yLeft=yLeft,up_yRight=yRight;
bool up_findNewSeed = false;//判断是否找到种子点
while(up_yLeft<=up_yRight)
{
up_findNewSeed = false;
while(src->imageData[(t_i-)*width+up_yLeft]&&up_yLeft<width)
{
up_findNewSeed=true;
up_yLeft++;
} if (up_findNewSeed)
{
if (up_yLeft==up_yRight)
{
stk.push_back(cvPoint(t_i-,up_yLeft));
lst.push_back(cvPoint(t_i-,up_yLeft));
src->imageData[(t_i-)*width+up_yLeft]=;//二值图像
}
else
{
stk.push_back(cvPoint(t_i-,up_yLeft-));
lst.push_back(cvPoint(t_i-,up_yLeft-));
src->imageData[(t_i-)*width+up_yLeft-]=;//二值图像
}
up_findNewSeed=false;
}
int itemp=up_yLeft;
while (!src->imageData[(t_i-)*width+up_yLeft]&&up_yLeft<up_yRight)
{
up_yLeft++;
}
if (itemp==up_yLeft)
{
up_yLeft++;
}
} //下一行搜索入栈点
int down_yLeft=yLeft,down_yRight=yRight;
bool down_findNewSeed = false;//判断是否找到种子点
while(down_yLeft<=down_yRight)
{
down_findNewSeed = false;
while(src->imageData[(t_i+)*width+down_yLeft]&&down_yLeft<width)
{
down_findNewSeed=true;
down_yLeft++;
} if (down_findNewSeed)
{
if (down_yLeft==down_yRight)
{
++area;
stk.push_back(cvPoint(t_i+,down_yLeft));
lst.push_back(cvPoint(t_i+,down_yLeft));
src->imageData[(t_i+)*width+down_yLeft]=;//二值图像
}
else
{
++area;
stk.push_back(cvPoint(t_i+,down_yLeft-));
lst.push_back(cvPoint(t_i+,down_yLeft-));
src->imageData[(t_i+)*width+down_yLeft-]=;//二值图像
}
down_findNewSeed=false;
}
int itemp=down_yLeft;
while (!src->imageData[(t_i+)*width+down_yLeft]&&down_yLeft<down_yRight)
{
down_yLeft++;
}
if (itemp==down_yLeft)
{
down_yLeft++;
} } }
//int e=clock();
//std::cout<<e-s;
if (area>MinCutNumb)
{
//cvOr(dst,tempimage,dst);
while (!lst.empty())
{
CvPoint tmpPt=lst.front();
lst.pop_front();
tempimage->imageData[tmpPt.x*width+tmpPt.y]=;
}
}
else
{
//std::list<CvPoint>().swap(lst);
//while (!lst.empty()) lst.pop_back();
//lst.resize(0);
//lst.
lst.clear();
} }//判断是否入栈
//CvPoint }
}
//cvZero(dst);
cvCopy(tempimage,dst);
cvReleaseImage(&tempimage);
return targetSumNumb;
}
效果如下图:
小结:去除小面积效果还好,这里实现两种算法的时间优化并不是很明显,自己编程实现效率并不是很高,仅供参考,有园友写的比较好的代码可以分享一下,大家互相学习。
种子填充算法描述及C++代码实现的更多相关文章
- openGL实现图形学扫描线种子填充算法
title: "openGL实现图形学扫描线种子填充算法" date: 2018-06-11T19:41:30+08:00 tags: ["图形学"] cate ...
- CGA填充算法之种子填充算法
CGA填充算法之种子填充算法 平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界 (也可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜 ...
- JAVA实现种子填充算法
种子填充算法原理在网上很多地方都能找到,这篇是继上篇扫描线算法后另一种填充算法,直接上实现代码啦0.0 我的实现只是实现了种子填充算法,但是运行效率不快,如果大佬有改进方法,欢迎和我交流,谢谢! 最后 ...
- Atitit Seed-Filling种子填充算法attilax总结
Atitit Seed-Filling种子填充算法attilax总结 种子填充的原理,4联通与8联通区域的选择.. 三个队列 waitProcessPixList tempPixList Proces ...
- UVA 572 -- Oil Deposits(DFS求连通块+种子填充算法)
UVA 572 -- Oil Deposits(DFS求连通块) 图也有DFS和BFS遍历,由于DFS更好写,所以一般用DFS寻找连通块. 下述代码用一个二重循环来找到当前格子的相邻8个格子,也可用常 ...
- [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)
上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...
- Open gl 的不规则图形的4联通种子递归填充和扫描线种子递归填充算法实现
实验题目:不规则区域的填充算法 实验目的:验证不规则区域的填充算法 实验内容:利用VC与OpenGL,实现不规则区域的填充算法. 1.必做:实现简单递归的不规则区域填充算法. 2.选做:针对简单递归算 ...
- [OpenGL] 不规则区域的填充算法
不规则区域的填充算法 一.简单递归 利用Dfs实现简单递归填充. 核心代码: // 简单深度搜索填充 (四连通) void DfsFill(int x, int y) { || y < || x ...
- 强化学习中REIINFORCE算法和AC算法在算法理论和实际代码设计中的区别
背景就不介绍了,REINFORCE算法和AC算法是强化学习中基于策略这类的基础算法,这两个算法的算法描述(伪代码)参见Sutton的reinforcement introduction(2nd). A ...
随机推荐
- CSS3关于background-size属性
bachground-size属性就是定义背景图片的大小,其值有:auto , 像素值 , 百分比 , cover , contain . background-size: auto,默认值,以图 ...
- highCharts提示框不显示的问题
使用HighCharts插件进行数据展示的时候,鼠标放在数据处没有提示框,或者只有头尾2个提示框,其他提示框不显示,为什么会这样? 1.查看是否使用了tooltip属性,该属性的enabled默认为t ...
- jquery叠加页片自动切换特效
查看效果:http://keleyi.com/keleyi/phtml/jqtexiao/34.htm 下面是HTML代码: <!DOCTYPE html> <html xmlns= ...
- Struts2(1) —— 概述
1.Struts2框架介绍 Struts2框架是MVC流程框架,适合分层开发,框架应用实现不依赖于Servlet,使用大量的拦截器来处理用户请求,属于无侵入式的设计. 2.Struts2框架的流程原理 ...
- Android Studio导入修改
- 解决xcode8模拟器不能删除应用的问题
问题描述:想删除模拟器上的测试项目,但是长按之后主界面模糊一下,并没有出现小叉叉来删除. 原因:这是因为xcode8中模拟器自带Touch3D,我们控制不好触摸板的力度. 解决方法: 1. ...
- 长链接转换成短链接(iOS版本)
首先需要将字符串使用md5加密,添加NSString的md5的类别方法如下 .h文件 #import <CommonCrypto/CommonDigest.h> @interface NS ...
- iOS json解析的几种方法 NSJSONSerialization,JSONKit,SBJson ,TouchJson
相关的第三方类库大家可以去github上下载 1.NSJSONSerialization 具体代码如下 : - (void)viewDidLoad { [super viewDidLoad]; NSD ...
- 用户故事驱动的敏捷开发 – 2. 创建backlog
本系列的第一篇[用户故事驱动的敏捷开发 – 1. 规划篇]跟大家分享了如何使用用户故事来帮助团队创建需求的过程,在这一篇中,我们来看看如何使用这些用户故事和功能点形成产品backlog.产品backl ...
- .net 已知图片的网络路径,通过浏览器下载图片
没什么技术含量,主要留给自己查找方便: 如题,知道图片的完整网络路径的情况下,在浏览器中下载图片的实现: 下面这个方法实现的是把图片读取为byte数组: private byte[] GetImage ...