项目需要看了种子填充算法,改进了算法主要去除面积小的部分。种子填充算法分为两种,简单的和基于扫描线的方法,简单的算法如下描述(笔者针对的是二值图像):
(1)从上到下,从左到有,依次扫描每个像素;
(2)遇到一个非零数值压栈,并置原图像像素点值为0,面积初始化为1;否则,处理完毕。
(3)对栈非空查找,如果非空弹出栈顶,检测4领域或8领域,如果非空压栈,并置原图像像素点为0,标示不在处理此点,面积加1;如果为空,停止;
(4)判断面积是否大于给定阈值,小于的删掉,大于的把得到的所有像素点保存到目标图像上去,继续扫描像素,转2。
这里我用c++实现,开始用的stl栈,运行一段时间会有中断,之后换成链表可以了,代码共享如下,可以运行,图片使用二值,有需要的可以留下邮箱,一起研究:
1 //视频处理测试算法,种子填充算法,扫描线算法,二值图像 2 //用栈会保存,这里把栈换成链表了,下面有栈注释掉代码 3 //20140911 4 #include <iostream> 5 #include "cv.h" 6 #include "highgui.h" 7 #include <stack> 8 #include <list> 9 #include <string> 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"<<e-s; 37 cvShowImage("处理后图片",ipl_target); 38 cvWaitKey(1); 39 cvReleaseImage(&ipl_origin); 40 cvReleaseImage(&ipl_target); 41 } 42 43 44 cvWaitKey(0); 45 46 cvDestroyWindow("原始图片"); 47 cvDestroyWindow("处理后图片"); 48 49 } 50 //MinCutNumb代表剔除面积小于MinCutNumb的值; 51 //返回找到目标数 52 int ScanLine_SeedFillingAlgo(IplImage *src,IplImage *dst,int MinCutNumb) 53 { 54 int i, j, k; 55 for ( i = 0; i < 3; i++ ) //上下两行 56 { 57 unsigned char * t_pPos = (unsigned char *) ( &src->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<CvPoint> stk;//stl栈 103 std::list<CvPoint> lst;//stl链表 104 cvZero(dst); 105 //IplImage *tempimage=cvCreateImage(cvGetSize(src),8,1);//创建一个临时数据,保存源图像数据到目标过度数据 106 int t_i;//每次种子的位置 107 int t_j; 108 //cvZero(tempimage);//临时数据初始化,清0 109 for (int i=1;i<height-1;i++) 110 { 111 for (int j=1;j<width-1;j++) 112 { 113 //int s=clock(); 114 115 // 116 if (src->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<n_Count;ii++)//扫描各个方向 133 { 134 if (src->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<<e-s; 146 if (area>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 else 157 { 158 //std::list<CvPoint>().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 #include <iostream> 3 #include "cv.h" 4 #include "highgui.h" 5 #include <stack> 6 #include <list> 7 #include <string> 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"<<e-s; 35 cvShowImage("处理后图片",ipl_origin); 36 cvWaitKey(1); 37 38 } 39 40 41 42 43 44 45 cvWaitKey(0); 46 cvReleaseImage(&ipl_origin); 47 //cvReleaseImage(&ipl_target); 48 cvDestroyWindow("原始图片"); 49 cvDestroyWindow("处理后图片"); 50 51 } 52 //MinCutNumb代表剔除面积小于MinCutNumb的值; 53 //返回找到目标数 54 int ScanLine_SeedFillingAlgoE(IplImage *src,IplImage *dst,int MinCutNumb) 55 { 56 int width = src->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<CvPoint> stk;//stl栈 68 std::list<CvPoint> 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<height-1;i++) 75 { 76 for (int j=1;j<width-1;j++) 77 { 78 //int s=clock(); 79 80 // 81 if (src->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<width) 100 { 101 //++t_j; 102 if (src->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 else 109 { 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 else 127 { 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<width) 140 { 141 up_findNewSeed=true; 142 up_yLeft++; 143 } 144 145 if (up_findNewSeed) 146 { 147 if (up_yLeft==up_yRight) 148 { 149 stk.push_back(cvPoint(t_i-1,up_yLeft)); 150 lst.push_back(cvPoint(t_i-1,up_yLeft)); 151 src->imageData[(t_i-1)*width+up_yLeft]=0;//二值图像 152 } 153 else 154 { 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<up_yRight) 163 { 164 up_yLeft++; 165 } 166 if (itemp==up_yLeft) 167 { 168 up_yLeft++; 169 } 170 } 171 172 //下一行搜索入栈点 173 int down_yLeft=yLeft,down_yRight=yRight; 174 bool down_findNewSeed = false;//判断是否找到种子点 175 while(down_yLeft<=down_yRight) 176 { 177 down_findNewSeed = false; 178 while(src->imageData[(t_i+1)*width+down_yLeft]&&down_yLeft<width) 179 { 180 down_findNewSeed=true; 181 down_yLeft++; 182 } 183 184 if (down_findNewSeed) 185 { 186 if (down_yLeft==down_yRight) 187 { 188 ++area; 189 stk.push_back(cvPoint(t_i+1,down_yLeft)); 190 lst.push_back(cvPoint(t_i+1,down_yLeft)); 191 src->imageData[(t_i+1)*width+down_yLeft]=0;//二值图像 192 } 193 else 194 { 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<down_yRight) 204 { 205 down_yLeft++; 206 } 207 if (itemp==down_yLeft) 208 { 209 down_yLeft++; 210 } 211 212 } 213 214 215 216 217 218 219 220 } 221 //int e=clock(); 222 //std::cout<<e-s; 223 if (area>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 else 234 { 235 //std::list<CvPoint>().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 }
效果如下图:
小结:去除小面积效果还好,这里实现两种算法的时间优化并不是很明显,自己编程实现效率并不是很高,仅供参考,有园友写的比较好的代码可以分享一下,大家互相学习。