Маркировка связанных областей, итог

Apr 21, 2013 19:06

Итак, как я уже говорил в предыдущей записи, алгоритм китайцев у меня заработал. Вот - сравнение с моим:

Производительность, по вертикали - логарифм времени выполнения операции, по горизонтали - корень из количества пикселей на картинке. Синее, красное, розовое - мое; зеленое, голубое и желтое - китайцев. Верхняя пара кривых - для 50% соотношения пикселей изображения и фона; средняя - для 10%; нижняя - для 90%. В ходе тестов, кстати, выяснилось, что мой алгоритм глючит: периодически он пропускает переименование меток (видимо, какой-то косяк в функции перемаркировки, но мне лень его искать: мой алгоритм настолько тормозной, что я его использовать не буду, выкину нафиг).
Пока что я не стал добавлять поиск 4-связанных областей по алгоритму китайцев, сделаю это позже. Ну и еще не сделано выделение объектов (это - примитивная операция, сделать несложно). В общем же все достаточно просто:
/** * label 4-connected components on image * (slow algorythm, but easy to parallel) * * @param I (i) - image ("packed") * @param W,H,W_0 - size of the image (W - width in pixels, W_0 - width in octets) * @param Nobj (o) - number of objects found * @return an array of labeled components */ CCbox *cclabel4(unsigned char *Img, int W, int H, int W_0, size_t *Nobj){ unsigned char *I = FC_filter(Img, W_0, H); #include "cclabling.h" FREE(I); return ret; } // label 8-connected components, look cclabel4 CCbox *cclabel8(unsigned char *I, int W, int H, int W_0, size_t *Nobj){ #define LABEL_8 #include "cclabling.h" #undef LABEL_8 return ret; }
И вот реализация алгоритма китайцев:
CCbox *ret = NULL; size_t N = 0, // current label Ntot = 0, // number of found objects Nmax = W*H/4; // max number of labels size_t *labels = char2st(I, W, H, W_0); int w = W - 1, h = H - 1; int y; size_t *assoc = Malloc(Nmax, sizeof(size_t)); // allocate memory for "remark" array inline void remark(size_t old, size_t *newv){ size_t new = *newv; if(old == new || assoc[old] == assoc[new]) return; if(!assoc[old]){ // try to remark non-marked value assoc[old] = new; return; } // value was remarked -> we have to remark "new" to assoc[old] // and decrement all marks, that are greater than "new" size_t ao = assoc[old], an = assoc[new]; // now we must check assoc[old]: if it is < new -> change *newv, else remark assoc[old] if(ao < an) *newv = old; else{ size_t x = ao; ao = an; an = x; // swap values } OMP_FOR() for(int i = 1; i <= N; i++){ size_t m = assoc[i]; if(m < an) continue; if(m == an) assoc[i] = ao; else assoc[i]--; } Ntot--; } size_t *ptr = labels; for(y = 0; y < H; y++){ // FIXME!!!! throw out that fucking "if" checking coordinates!!! bool found = false; size_t curmark = 0; // mark of pixel to the left for(int x = 0; x < W; x++, ptr++){ size_t curval = *ptr; if(!curval){ found = false; continue;} size_t *U = (y) ? &ptr[-W] : NULL; size_t upmark = 0; // mark from line above if(!found){ // new pixel, check neighbours above found = true; // now check neighbours in upper string: if(U){ if(x && U[-1]){ // there is something in upper left corner -> use its value upmark = U[-1]; }else // check point above only if there's nothing in left up if(U[0]) upmark = U[0]; if(x < w && U[1]){ // there's something in upper right if(upmark){ // to the left of it was pixels remark(U[1], &upmark); }else upmark = U[1]; } } if(!upmark){ // there's nothing above - set current pixel to incremented counter size_t *D = (y < h) ? &ptr[W] : NULL; if( !(x && ((D && D[-1]) || ptr[-1])) // no left neighbours && !(x < w && ((D && D[1]) || ptr[1])) // no right neighbours && !(D && D[0])){ // no neighbour down *ptr = 0; // erase this hermit! continue; } upmark = ++N; assoc[upmark] = ++Ntot; // refresh "assoc" } *ptr = upmark; curmark = upmark; }else{ // there was something to the left -> we must chek only U[1] if(U){ if(x < w && U[1]){ // there's something in upper right remark(U[1], &curmark); } } *ptr = curmark; } } } // Step 2: rename markers // first correct complex assotiations in assoc OMP_FOR() for(y = 0; y < H; y++){ size_t *ptr = &labels[y*W]; for(int x = 0; x < W; x++, ptr++){ size_t p = *ptr; if(!p){continue;} *ptr = assoc[p]; } } FREE(assoc); FREE(labels);
А вот - так сказать, на память - мое нерабочее поделие:
CCbox *ret = NULL; size_t N _U_ = 0, Ncur = 0; size_t *labels = char2st(I, W, H, W_0); int y; // Step 1: mark neighbours by strings size_t *ptr = labels; for(y = 0; y < H; y++){ bool found = false; for(int x = 0; x < W; x++, ptr++){ if(!*ptr){ found = false; continue;} if(!found){ found = true; ++Ncur; } *ptr = Ncur; } } // Step 2: fill associative array with remarking N = Ncur+1; // size of markers array (starting from 1) size_t i, *assoc = Malloc(N, sizeof(size_t)); for(i = 0; i < N; i++) assoc[i] = i; // primary initialisation // now we should scan image again to rebuild enumeration // THIS PROCESS AVOID PARALLELISATION??? Ncur = 0; size_t h = H-1 #ifdef LABEL_8 ,w = W-1; #endif ; inline void remark(size_t old, size_t *newv){ size_t new = *newv; if(assoc[old] == new) DBG("(%zd)\n", new); if(assoc[old] == old){ // remark non-remarked value assoc[old] = new; return; } // value was remarked -> we have to remark "new" to assoc[old] // and decrement all marks, that are greater than "new" size_t ao = assoc[old]; // now we must check assoc[old]: if it is < new -> change *newv, else remark assoc[old] if(ao < new) *newv = ao; else{ size_t x = ao; ao = new; new = x; // swap values } int xx = old + W / 2; if(xx > N) xx = N; OMP_FOR() for(int i = 1; i <= xx; i++){ size_t m = assoc[i]; if(m < new) continue; if(m == new) assoc[i] = ao; else if(m <= Ncur) assoc[i]--; } Ncur--; } for(y = 0; y < H; y++){ // FIXME!!!! throw out that fucking "if" checking coordinates!!! size_t *ptr = &labels[y*W]; bool found = false; for(int x = 0; x < W; x++, ptr++){ size_t curval = *ptr; if(!curval){ found = false; continue;} if(found) continue; // we go through remarked pixels found = true; // now check neighbours in upper and lower string: size_t *U = (y) ? &ptr[-W] : NULL; size_t *D = (y < h) ? &ptr[W] : NULL; size_t upmark = 0; // mark from line above if(U){ #ifdef LABEL_8 if(x && U[-1]){ // there is something in upper left corner -> make remark by its value upmark = assoc[U[-1]]; }else // check point above only if there's nothing in left up #endif if(U[0]) upmark = assoc[U[0]]; #ifdef LABEL_8 if(x < w) if(U[1]){ // there's something in upper right if(upmark){ // to the left of it was pixels remark(U[1], &upmark); }else upmark = assoc[U[1]]; } #endif } if(!upmark){ // there's nothing above - set current pixel to incremented counter #ifdef LABEL_8 // check, whether pixel is not single if( !(x && ((D && D[-1]) || ptr[-1])) // no left neighbours && !(x < w && ((D && D[1]) || ptr[1])) // no right neighbours && !(D && D[0])){ // no neighbour down *ptr = 0; // erase this hermit! continue; } #endif upmark = ++Ncur; } // now remark DL and DR corners (bottom pixel will be checked on next line) if(D){ #ifdef LABEL_8 if(x && D[-1]){ remark(D[-1], &upmark); } if(x < w && D[1]){ remark(D[1], &upmark); } #else if(D[0]) remark(D[0], &upmark); #endif } // remark current remark(curval, &upmark); } } // Step 3: rename markers // first correct complex assotiations in assoc OMP_FOR() for(y = 0; y < H; y++){ size_t *ptr = &labels[y*W]; for(int x = 0; x < W; x++, ptr++){ size_t p = *ptr; if(!p){continue;} *ptr = assoc[p]; } } FREE(assoc); FREE(labels);

c, snippets, поиск связанных областей

Previous post Next post
Up