項目須要看了種子填充算法,改進了算法主要去除面積小的部分。種子填充算法分爲兩種,簡單的和基於掃描線的方法,簡單的算法以下描述(筆者針對的是二值圖像):ios
(1)從上到下,從左到有,依次掃描每一個像素;c++
(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 }
圖片處理效果:優化
基於掃描線的算法,描述以下(也是針對二值圖像編程的):ui
(1) 初始化一個空的棧用於存放種子點,將種子點(x, y)入棧;spa
(2) 判斷棧是否爲空,若是棧爲空則結束算法,不然取出棧頂元素做爲當前掃描線的種子點(x, y),y是當前的掃描線;code
(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 }
效果以下圖:
小結:去除小面積效果還好,這裏實現兩種算法的時間優化並非很明顯,本身編程實現效率並非很高,僅供參考,有園友寫的比較好的代碼能夠分享一下,你們互相學習。