日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

bwlabel算法_bwlabel函数的c++实现

發布時間:2023/12/20 c/c++ 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bwlabel算法_bwlabel函数的c++实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

實驗中需要用到區域聯通的算法,就是類似于matlab中bwlabel的函數。網上找了找c++源碼未果,bwlabel-python版用python描述了matlab中的實現方法,但是最后對標簽的處理部分并未看明白,故自己用c++實現了一個。先直接看bwlabel函數代碼:

cv::Mat bwlabel(const cv::Mat in, int * num, const intmode)

{const int num_runs = number_of_runs(in);int * sc = new int[num_runs];int * ec = new int[num_runs];int * r = new int[num_runs];int * labels = new int[num_runs];

memset(labels,0, sizeof(int)*num_runs);

fill_run_vectors(in, sc, ec, r);

first_pass(sc, ec, r, labels, num_runs, mode);

cv::Mat result= cv::Mat::zeros(in.size(), CV_8UC1);int number = 0;for(int i = 0; i < num_runs; i++)

{

uchar* p_row = result.ptr(r[i]);for(int j = sc[i]; j <= ec[i]; j++)

p_row[j]=labels[i];if(number

number=labels[i];

}if(num !=NULL)*num =number;

delete [] sc;

delete [] ec;

delete [] r;

delete [] labels;returnresult;

}

bwlabel中要用到三個輔助函數:number_of_runs,fill_run_vectors,first_pass。函數number_of_runs計算每一行中非零像素團的個數并累加起來。

1 1 0 0 0 1 1 1 0 0

比如,上面這一行就有2個非零像素團,我們稱這樣的像素團為Run,函數number_of_runs實現如下:

int number_of_runs(const cv::Mat in)

{const int rows = in.rows;const int cols = in.cols;int result = 0;for(int row = 0; row < rows; row++)

{const uchar * p_row = in.ptr(row);if(p_row[0] != 0)

result++;for(int col = 1; col < cols; col++)

{if(p_row[col] != 0 && p_row[col-1] == 0)

result++;

}

}returnresult;

}

這個函數算法思想是,掃描每一行,對每一行,如果當前元素非零并且前一元素為零則Run的個數加一。

函數fill_run_vectors的作用是填充三個數據結構:sc[],ec[],r[],它們分別表示開始列標、結束列標和行標,數組長度為由number_of_runs函數得到的Run的個數。函數fill_run_vectors實現如下:

void fill_run_vectors(const cv::Mat in, int sc[], int ec[], intr[])

{const int rows = in.rows;const int cols = in.cols;int idx = 0;for(int row = 0; row < rows; row++)

{const uchar * p_row = in.ptr(row);int prev = 0;for(int col = 0; col < cols; col++)

{if(p_row[col] !=prev)

{if(prev == 0)

{

sc[idx]=col;

r[idx]=row;

prev= 1;

}else{

ec[idx++] = col - 1;

prev= 0;

}

}if(col == cols-1 && prev == 1)

{

ec[idx++] =col;

}

}

}

}

算法思想還是遍歷每一行,用變量prev保存一行中上一個團是0還是1,如果出現01跳變那么就要記錄下新的Run的開始列標和行標,如果出現10跳變(或者這行結束并且prev=1)那么就記錄下這個Run的結束列標。

函數first_pass顧名思義,字面上說第一次掃描。因為函數掃描每一個Run塊,給它打標簽。當出現如下情況時:

1 1 0 0 1 1 1 0

0 1 1 1 1 0 0 0

函數給第一行第一個Run打上標簽1,第二個Run打上標簽2,當遍歷到第二行時,發現這一行的一個Run與第一行第一個Run相鄰,故打上標簽1,但當繼續遍歷時發現這個Run也與第一行第二個Run相鄰,但函數并沒有改變第一行第二個Run的標簽,而是記錄下這兩個標簽其實該一樣。遍歷完第二行結果為:

1 1 0 0 2 2 2 0

0 1 1 1 1 0 0 0

遍歷完每一個Run過后就是處理剛才未處理的標簽了。函數first_pass實現如下:

void first_pass(const int sc[], const int ec[], const int r[],int labels[], const int num_runs, const intmode)

{int cur_row = 0;int next_label = 1;int first_run_on_prev_row = -1;int last_run_on_prev_row = -1;int first_run_on_this_row = 0;int offset = 0;int * equal_i = new int[num_runs];int * equal_j = new int[num_runs];int equal_idx = 0;if(mode == 8)

offset= 1;for(int k = 0; k < num_runs; k++)

{if(r[k] == cur_row + 1)

{

cur_row+= 1;

first_run_on_prev_row=first_run_on_this_row;

first_run_on_this_row=k;

last_run_on_prev_row= k - 1;

}else if(r[k] > cur_row + 1)

{

first_run_on_prev_row= -1;

last_run_on_prev_row= -1;

first_run_on_this_row=k;

cur_row=r[k];

}if(first_run_on_prev_row >= 0)

{int p =first_run_on_prev_row;while(p <= last_run_on_prev_row && sc[p] <= (ec[k] +offset))

{if(sc[k] <= ec[p] +offset)

{if(labels[k] == 0)

labels[k]=labels[p];else if(labels[k] !=labels[p])

{//labels[p] = labels[k];

equal_i[equal_idx] =labels[k];

equal_j[equal_idx]=labels[p];

equal_idx+= 1;

}

}

p+= 1;

}

}if(labels[k] == 0)

{

labels[k]= next_label++;

}

}/// process labels

for(int i = 0; i < equal_idx; i++)

{int max_label = equal_i[i] > equal_j[i] ?equal_i[i] : equal_j[i];int min_label = equal_i[i] < equal_j[i] ?equal_i[i] : equal_j[i];for(int j = 0; j < num_runs; j++)

{if(labels[j] ==max_label)

labels[j]=min_label;

}

}

delete [] equal_i;

delete [] equal_j;/process ignore labels

int * hist = new int[next_label];int * non_labels = new int[next_label];

memset(hist,0, sizeof(int)*next_label);int non_num = 0;for(int i = 0; i < num_runs; i++)

{

hist[labels[i]]++;

}for(int i = 1; i < next_label; i++)

{if(hist[i] == 0)

non_labels[non_num++] =i;

}for(int j = 0; j < num_runs; j++)

{int k =labels[j];for(int i = non_num-1; i >= 0; i--)

{if(k >non_labels[i])

{

labels[j]-= (i+1);break;

}

}

}

delete [] hist;

delete [] non_labels;

}

前面遍歷每一個Run分兩種情況,上一行有Run和上一行無Run:當上一行無Run時就分配一個新的標簽,當上一行有Run時還要考慮是否與上一行Run相鄰,若相鄰則打上上一行的標簽,當出現上面講到的情況時就保存這兩個標簽到數組equal_i,equal_j中。

接下來就是處理equal_i和equal_j這兩個數組了,要將它們當中相同族的不同標簽合并到一起(注釋process labels下面代碼)。

這樣過后還不能完事,有可能出現標簽間斷的現象(如1,2,4,6),就是還必須把標簽(如1,2,4,6)映射到一個連續的空間(1,2,3,4)。參見注釋process ignore labels以下代碼。

這樣過后就差不多了,最后一步是在bwlabel中給返回的Mat中元素打上對應的標簽。

總結

以上是生活随笔為你收集整理的bwlabel算法_bwlabel函数的c++实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。