项目需要看了种子填充算法,改进了算法主要去除面积小的部分。种子填充算法分为两种,简单的和基于扫描线的方法,简单的算法如下描述(笔者针对的是二值图像):
(1)从上到下,从左到有,依次扫描每个像素;
(2)遇到一个非零数值压栈,并置原图像像素点值为0,面积初始化为1;否则,处理完毕。
(3)对栈非空查找,如果非空弹出栈顶,检测4领域或8领域,如果非空压栈,并置原图像像素点为0,标示不在处理此点,面积加1;如果为空,停止;
(4)判断面积是否大于给定阈值,小于的删掉,大于的把得到的所有像素点保存到目标图像上去,继续扫描像素,转2。
这里我用c++实现,开始用的stl栈,运行一段时间会有中断,之后换成链表可以了,代码共享如下,可以运行,图片使用二值,有需要的可以留下邮箱,一起研究:
1 //视频处理测试算法,种子填充算法,扫描线算法,二值图像 2 //用栈会保存,这里把栈换成链表了,下面有栈注释掉代码 3 //20140911 4 #include5 #include "cv.h" 6 #include "highgui.h" 7 #include 8 #include 9 #include
10 11 using namespace std; 12 int ScanLine_SeedFillingAlgo(IplImage *src,IplImage *dst,int MinCutNumb);//原图像和目标图像不要是同一副图像 13 int main() 14 { 15 IplImage *ipl_origin; 16 IplImage *ipl_target; 17 string fname = "D:/无腐蚀膨胀/Fight1save"; 18 cvNamedWindow("原始图片"); 19 cvNamedWindow("处理后图片"); 20 for (int k=0;k<110;k++) 21 { 22 string filename=""; 23 char tmp[20]; 24 _itoa_s(k,tmp,20,10); 25 filename+=tmp; 26 filename+=".bmp"; 27 filename=fname+filename; 28 ipl_origin=cvLoadImage(filename.c_str(),-1); 29 ipl_target=cvCreateImage(cvGetSize(ipl_origin),8,1);//cvCloneImage(ipl_origin); 30 31 cvZero(ipl_target); 32 cvShowImage("原始图片",ipl_origin); 33 int s=clock(); 34 ScanLine_SeedFillingAlgo(ipl_origin,ipl_target,125); 35 int e=clock(); 36 std::cout<<"\n"< imageData[ i * src->widthStep ] ); 58 59 for ( j = 0; j < src->widthStep; j++ ) 60 { 61 * t_pPos = (unsigned char)0; 62 t_pPos++; 63 } 64 } 65 66 for ( i = ( src->height - 3 ); i < src->height; i++ ) //上下两行 67 { 68 unsigned char * t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep ] ); 69 70 for ( j = 0; j < src->widthStep; j++ ) 71 { 72 * t_pPos = (unsigned char)0; 73 t_pPos++; 74 } 75 } 76 77 for ( i = 0; i < src->height; i++ ) //左右两边 78 { 79 unsigned char * t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep ] ); 80 81 for ( j = 0; j < 3; j++ ) 82 { 83 * t_pPos = (unsigned char)0; 84 t_pPos++; 85 } 86 87 t_pPos = (unsigned char *) ( &src->imageData[ i * src->widthStep + src->widthStep - 3 ] ); 88 89 for ( j = ( src->widthStep - 3 ); j < src->widthStep; j++ ) 90 { 91 * t_pPos = (unsigned char)0; 92 t_pPos++; 93 } 94 } 95 int width = src->width; 96 int height = src->height; 97 int targetSumNumb=0; 98 int area; 99 CvPoint direction_4[]={ {-1, 0}, { 0, 1}, { 1, 0}, { 0, -1}};//上右下左100 //CvPoint direction_8[] = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1} };//顺时针101 int n_Count=sizeof(direction_4)/sizeof(CvPoint);//遍历方向个数102 std::list stk;//stl栈103 std::list lst;//stl链表104 cvZero(dst);105 //IplImage *tempimage=cvCreateImage(cvGetSize(src),8,1);//创建一个临时数据,保存源图像数据到目标过度数据106 int t_i;//每次种子的位置107 int t_j;108 //cvZero(tempimage);//临时数据初始化,清0109 for (int i=1;i imageData[i*width+j])117 {118 targetSumNumb++;119 stk.push_back(cvPoint(i,j));//栈换成链表120 lst.push_back(cvPoint(i,j));121 src->imageData[i*width+j]=0;//二值图像122 //tempimage->imageData[i*width+j]=255;123 area=1; 124 while (!stk.empty())125 {126 CvPoint seed=stk.back();//弹出头部127 stk.pop_back();128 t_i=seed.x;129 t_j=seed.y;130 if (t_i<=0||t_i>=height||t_j<=0||t_j>=width)131 continue;132 for (int ii=0;ii imageData[(t_i+direction_4[ii].x)*width+t_j+direction_4[ii].y])135 {136 area++;137 stk.push_back(cvPoint(t_i+direction_4[ii].x,t_j+direction_4[ii].y));138 lst.push_back(cvPoint(t_i+direction_4[ii].x,t_j+direction_4[ii].y));139 src->imageData[(t_i+direction_4[ii].x)*width+t_j+direction_4[ii].y]=0;//二值图像140 //tempimage->imageData[(t_i+direction_4[ii].x)*width+t_j+direction_4[ii].y]=255;141 }142 }143 }144 //int e=clock();145 //std::cout< MinCutNumb)147 {148 //cvOr(dst,tempimage,dst);149 while (!lst.empty())150 {151 CvPoint tmpPt=lst.front();152 lst.pop_front();153 dst->imageData[tmpPt.x*width+tmpPt.y]=255;154 }155 }156 else157 {158 //std::list ().swap(lst);159 //while (!lst.empty()) lst.pop_back();160 //lst.resize(0);161 //lst.162 lst.clear();163 }164 165 }//判断是否入栈166 //CvPoint 167 168 }169 }170 //cvReleaseImage(&tempimage);171 return targetSumNumb;172 }
图片处理效果:
基于扫描线的算法,描述如下(也是针对二值图像编程的):
(1) 初始化一个空的栈用于存放种子点,将种子点(x, y)入栈;
(2) 判断栈是否为空,如果栈为空则结束算法,否则取出栈顶元素作为当前扫描线的种子点(x, y),y是当前的扫描线;
(3) 从种子点(x, y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xLeft和xRight;
(4) 分别检查与当前扫描线相邻的y - 1和y + 1两条扫描线在区间[xLeft, xRight]中的像素,从xLeft开始向xRight方向搜索,若存在非边界且未填充的像素点,则找出这些相邻的像素点中最右边的一个,并将其作为种子点压入栈中,然后返回第(2)步。
也是用的c++实现,代码如下:
1 //视频处理测试算法,种子填充算法,扫描线算法,二值图像 2 #include3 #include "cv.h" 4 #include "highgui.h" 5 #include 6 #include 7 #include
8 9 using namespace std; 10 int ScanLine_SeedFillingAlgoE(IplImage *src,IplImage *dst,int MinCutNumb);//原图像和目标图像不要是同一副图像 11 int main() 12 { 13 IplImage *ipl_origin; 14 IplImage *ipl_target; 15 string fname = "C:/Users/zcx/Desktop/打架斗殴测试图片/第四次无腐蚀膨胀/Fight1save"; 16 cvNamedWindow("原始图片"); 17 cvNamedWindow("处理后图片"); 18 for (int k=0;k<246;k++) 19 { 20 string filename=""; 21 char tmp[20]; 22 _itoa_s(k,tmp,20,10); 23 filename+=tmp; 24 filename+=".bmp"; 25 filename=fname+filename; 26 ipl_origin=cvLoadImage(filename.c_str(),-1); 27 //ipl_target=cvCreateImage(cvGetSize(ipl_origin),8,1);//cvCloneImage(ipl_origin); 28 29 //cvZero(ipl_target); 30 cvShowImage("原始图片",ipl_origin); 31 int s=clock(); 32 ScanLine_SeedFillingAlgoE(ipl_origin,ipl_origin,125); 33 int e=clock(); 34 std::cout<<"\n"< width; 57 int height = src->height; 58 int targetSumNumb=0;//目标数 59 int area;//区域面积 60 int rcount=0,lcount=0;//向左向右计算像素个数 61 int yLeft,yRight;//左右像素坐标 62 //IplImage *src=cvCreateImage(cvGetSize(p_src),8,1);//cvCloneImage(p_src); 63 //cvCopy(p_src,src); 64 CvPoint direction_4[]={ {-1, 0}, { 0, 1}, { 1, 0}, { 0, -1}};//上右下左 65 //CvPoint direction_8[] = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1} };//顺时针 66 int n_Count=sizeof(direction_4)/sizeof(CvPoint);//遍历方向个数 67 std::list stk;//stl栈 68 std::list lst;//stl链表 69 70 IplImage *tempimage=cvCreateImage(cvGetSize(src),8,1);//创建一个临时数据,保存源图像数据到目标过度数据 71 int t_i,t_j;//每次种子的位置 72 int rt_j,lt_j;//左右搜索 73 cvZero(tempimage);//临时数据初始化,清0 74 for (int i=1;i imageData[i*width+j]) 82 { 83 targetSumNumb++; 84 stk.push_back(cvPoint(i,j));//栈换成链表 85 lst.push_back(cvPoint(i,j)); 86 src->imageData[i*width+j]=0;//二值图像 87 //tempimage->imageData[i*width+j]=255; 88 area=1; 89 while (!stk.empty()) 90 { 91 CvPoint seed=stk.back();//弹出头部 92 stk.pop_back(); 93 t_i=seed.x; 94 rt_j=lt_j=t_j=seed.y; 95 if (t_i<=0||t_i>=height)//上下扫描界限 96 continue; 97 //向右扫描 98 rcount=0,lcount=0; 99 while (rt_j imageData[t_i*width+(++rt_j)])103 {104 rcount++; 105 lst.push_back(cvPoint(t_i,rt_j));106 src->imageData[t_i*width+rt_j]=0;//二值图像107 }108 else109 {110 break;111 }112 }113 area+=rcount;114 yRight=t_j+rcount;//右边坐标115 //向左扫描116 while (lt_j>0)117 {118 //++t_j;119 if (src->imageData[t_i*width+(--lt_j)])120 {121 lcount++; 122 lst.push_back(cvPoint(t_i,lt_j));123 124 src->imageData[t_i*width+lt_j]=0;//二值图像125 }126 else127 {128 break;129 }130 }131 area+=lcount;132 yLeft=t_j-lcount;//左边坐标133 //上一行搜索入栈点134 int up_yLeft=yLeft,up_yRight=yRight;135 bool up_findNewSeed = false;//判断是否找到种子点136 while(up_yLeft<=up_yRight)137 {138 up_findNewSeed = false;139 while(src->imageData[(t_i-1)*width+up_yLeft]&&up_yLeft imageData[(t_i-1)*width+up_yLeft]=0;//二值图像152 }153 else154 {155 stk.push_back(cvPoint(t_i-1,up_yLeft-1));156 lst.push_back(cvPoint(t_i-1,up_yLeft-1));157 src->imageData[(t_i-1)*width+up_yLeft-1]=0;//二值图像158 }159 up_findNewSeed=false;160 }161 int itemp=up_yLeft;162 while (!src->imageData[(t_i-1)*width+up_yLeft]&&up_yLeft imageData[(t_i+1)*width+down_yLeft]&&down_yLeft imageData[(t_i+1)*width+down_yLeft]=0;//二值图像192 }193 else194 {195 ++area;196 stk.push_back(cvPoint(t_i+1,down_yLeft-1));197 lst.push_back(cvPoint(t_i+1,down_yLeft-1));198 src->imageData[(t_i+1)*width+down_yLeft-1]=0;//二值图像199 }200 down_findNewSeed=false;201 }202 int itemp=down_yLeft;203 while (!src->imageData[(t_i+1)*width+down_yLeft]&&down_yLeft MinCutNumb)224 {225 //cvOr(dst,tempimage,dst);226 while (!lst.empty())227 {228 CvPoint tmpPt=lst.front();229 lst.pop_front();230 tempimage->imageData[tmpPt.x*width+tmpPt.y]=255;231 }232 }233 else234 {235 //std::list ().swap(lst);236 //while (!lst.empty()) lst.pop_back();237 //lst.resize(0);238 //lst.239 lst.clear();240 }241 242 }//判断是否入栈243 //CvPoint 244 245 }246 }247 //cvZero(dst);248 cvCopy(tempimage,dst);249 cvReleaseImage(&tempimage);250 return targetSumNumb;251 }
效果如下图:
小结:去除小面积效果还好,这里实现两种算法的时间优化并不是很明显,自己编程实现效率并不是很高,仅供参考,有园友写的比较好的代码可以分享一下,大家互相学习。