项目需要看了种子填充算法,改进了算法主要去除面积小的部分。种子填充算法分为两种,简单的和基于扫描线的方法,简单的算法如下描述(笔者针对的是二值图像):

(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++代码实现的更多相关文章

  1. openGL实现图形学扫描线种子填充算法

    title: "openGL实现图形学扫描线种子填充算法" date: 2018-06-11T19:41:30+08:00 tags: ["图形学"] cate ...

  2. CGA填充算法之种子填充算法

    CGA填充算法之种子填充算法 平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界 (也可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜 ...

  3. JAVA实现种子填充算法

    种子填充算法原理在网上很多地方都能找到,这篇是继上篇扫描线算法后另一种填充算法,直接上实现代码啦0.0 我的实现只是实现了种子填充算法,但是运行效率不快,如果大佬有改进方法,欢迎和我交流,谢谢! 最后 ...

  4. Atitit Seed-Filling种子填充算法attilax总结

    Atitit Seed-Filling种子填充算法attilax总结 种子填充的原理,4联通与8联通区域的选择.. 三个队列 waitProcessPixList tempPixList Proces ...

  5. UVA 572 -- Oil Deposits(DFS求连通块+种子填充算法)

    UVA 572 -- Oil Deposits(DFS求连通块) 图也有DFS和BFS遍历,由于DFS更好写,所以一般用DFS寻找连通块. 下述代码用一个二重循环来找到当前格子的相邻8个格子,也可用常 ...

  6. [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)

    上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...

  7. Open gl 的不规则图形的4联通种子递归填充和扫描线种子递归填充算法实现

    实验题目:不规则区域的填充算法 实验目的:验证不规则区域的填充算法 实验内容:利用VC与OpenGL,实现不规则区域的填充算法. 1.必做:实现简单递归的不规则区域填充算法. 2.选做:针对简单递归算 ...

  8. [OpenGL] 不规则区域的填充算法

    不规则区域的填充算法 一.简单递归 利用Dfs实现简单递归填充. 核心代码: // 简单深度搜索填充 (四连通) void DfsFill(int x, int y) { || y < || x ...

  9. 强化学习中REIINFORCE算法和AC算法在算法理论和实际代码设计中的区别

    背景就不介绍了,REINFORCE算法和AC算法是强化学习中基于策略这类的基础算法,这两个算法的算法描述(伪代码)参见Sutton的reinforcement introduction(2nd). A ...

随机推荐

  1. jQuery Datepicker日期控件

    datepicker可以为bootstrap添加一个事件选择控件,适用于任何需要调用的场合,支持多种事件格式输出(比如:dd, d, mm, m, yyyy, yy等),是制作网页不可缺失的插件. R ...

  2. css3图片模糊过滤效果

    css3图片过滤效果,鼠标放上后其它图片模糊,鼠标所在位置的图片是清淅的,有效索引出当前的图片,对图片的模糊处理是本特效的亮点,你完全可以将模糊的效果应用于其它的图片特效中,你同样也可借此代码研究一下 ...

  3. 【小贴士】关于transitionEnd/animate的一个有趣故事

    前言 在很久之前,我们项目有一个动画功能,功能本身很简单,便是典型的右进左出,并且带动画功能 以当时来说,虽然很简单,但是受限于框架本身的难度,就直接使用了CSS3的方式完成了功能 当时主要使用tra ...

  4. 一步一步HTML5粒子编辑器

    写在前面 大家阅读此文之前,可以先看一篇MiloYip的文章:用JavaScript玩转游戏物理(一)运动学模拟与粒子系统,看完之后再看此文,更加容易理解. MiloYip使用的粒子是canvas中绘 ...

  5. PresentViewController切换界面

    视图切换,没有NavigationController的情况下,一般会使用presentViewController来切换视图并携带切换时的动画, 其中切换方法如下: – presentViewCon ...

  6. Atitit.常见软件 数据 交换格式 标准

    Atitit.常见软件 数据 交换格式 标准 1. 常见的数据格式txt ,doc ,pic,music ,vodio1 2. 通用格式json yaml phpstr1 3. 专用格式1 4. 用户 ...

  7. How To Collect ULS Log from SharePoint Farm

    We can use below command to collect SharePoint ULS log from all servers in the Farm in PowerShell. M ...

  8. Sharepoint学习笔记—习题系列--70-573习题解析 -(Q142-Q143)

    Question 142You have a Feature that contains an image named ImageV1.png.You plan to create a new ver ...

  9. IOS开发基础知识--碎片13

    1:运行程序报the file couldn't be opened because you don't have permission to view it 解决办法:项目—>targets- ...

  10. iPhone 丢失

    http://www.appvv.com/zhuanti/iccid/ 第一步:iPhone丢失后,及时报警,在最短时间内补办SIM卡或把卡停掉. 第二步:输入查询网址:sn.appvv.com 输入 ...