00001 /* --------------------------------------------------------------------------- 00002 Phission : 00003 Realtime Vision Processing System 00004 00005 Copyright (C) 2003-2006 Philip D.S. Thoren (pthoren@cs.uml.edu) 00006 University of Massachusetts at Lowell, 00007 Laboratory for Artificial Intelligence and Robotics 00008 00009 This file is part of Phission. 00010 00011 Phission is free software; you can redistribute it and/or modify 00012 it under the terms of the GNU Lesser General Public License as published by 00013 the Free Software Foundation; either version 2 of the License, or 00014 (at your option) any later version. 00015 00016 Phission is distributed in the hope that it will be useful, 00017 but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 GNU Lesser General Public License for more details. 00020 00021 You should have received a copy of the GNU Lesser General Public License 00022 along with Phission; if not, write to the Free Software 00023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00024 00025 ---------------------------------------------------------------------------*/ 00026 #ifdef HAVE_CONFIG_H 00027 #include <phissionconfig.h> 00028 #endif 00029 00030 #include <phStandard.h> 00031 00032 #include <graphSegmentation_Filter.h> 00033 00034 #include <algorithm> 00035 #include <cmath> 00036 00037 #include <phError.h> 00038 #include <phMemory.h> 00039 #include <phPrint.h> 00040 00041 #define phGRAPH_USE_RANDOM_COLORS() 0 00042 00043 /* --------------------------------------------------------------------------- 00044 * Code in this file from "Efficient Graph Based Segmentation" source code 00045 * 00046 * graphSegmentation_Filter::SegmentImage 00047 * graphSegmentation_Filter::SegmentGraph 00048 * graphSegmentation_Filter::ProcessGraph 00049 * 00050 * dist_rgb 00051 * 00052 * class phUniverse 00053 * 00054 * bool operator<(const ph_edge &a, const ph_edge &b) 00055 * 00056 * 05/27/2007 00057 * Code was edited by Philip Thoren (pthoren@cs.uml.edu) 00058 * 00059 * Formatted to my own crazy standards and optimized it for uint8_t images. 00060 * 00061 * --------------------------------------------------------------------------- 00062 * 00063 * Copyright (C) 2006 Pedro Felzenszwalb 00064 * 00065 * This program is free software; you can redistribute it and/or modify 00066 * it under the terms of the GNU General Public License as published by 00067 * the Free Software Foundation; either version 2 of the License, or 00068 * (at your option) any later version. 00069 * 00070 * This program is distributed in the hope that it will be useful, 00071 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00072 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00073 * GNU General Public License for more details. 00074 * 00075 * You should have received a copy of the GNU General Public License 00076 * along with this program; if not, write to the Free Software 00077 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00078 */ 00079 /* ------------------------------------------------------------------------ */ 00080 phUniverse::phUniverse( uint32_t elements ) 00081 { 00082 phFUNCTION("phUniverse::phUniverse") 00083 uint32_t i = 0; 00084 00085 this->m_elts = NULL; 00086 this->m_elts_size = 0; 00087 00088 if (elements > 0) 00089 { 00090 phDALLOC(this->m_elts, 00091 this->m_elts_size, 00092 elements, 00093 ph_uni_elt); 00094 for (i = 0; i < elements; i++) 00095 { 00096 this->m_elts[i].rank = 0; 00097 this->m_elts[i].size = 1; 00098 this->m_elts[i].p = i; 00099 } 00100 } 00101 00102 this->m_elt_count = elements; 00103 this->m_num = elements; 00104 00105 error: 00106 return; 00107 } 00108 00109 /* ------------------------------------------------------------------------ */ 00110 phUniverse::~phUniverse() 00111 { 00112 phFree(this->m_elts); 00113 this->m_elts_size = 0; 00114 this->m_elt_count = 0; 00115 this->m_num = 0; 00116 } 00117 00118 /* ------------------------------------------------------------------------ */ 00119 void phUniverse::resize( uint32_t elements ) 00120 { 00121 phFUNCTION("phUniverse::resize") 00122 if (elements > 0) 00123 { 00124 uint32_t i = 0; 00125 00126 phDALLOC(this->m_elts, 00127 this->m_elts_size, 00128 elements, 00129 ph_uni_elt); 00130 for (i = 0; i < elements; i++) 00131 { 00132 this->m_elts[i].rank = 0; 00133 this->m_elts[i].size = 1; 00134 this->m_elts[i].p = i; 00135 } 00136 00137 this->m_elt_count = elements; 00138 this->m_num = elements; 00139 } 00140 00141 return; 00142 error: 00143 this->m_num = 0; 00144 this->m_elt_count = 0; 00145 this->m_elts_size = 0; 00146 } 00147 00148 /* ------------------------------------------------------------------------ */ 00149 int phUniverse::find(int x) 00150 { 00151 int y = x; 00152 00153 while (y != this->m_elts[y].p) 00154 { 00155 y = this->m_elts[y].p; 00156 } 00157 00158 this->m_elts[x].p = y; 00159 00160 return y; 00161 } 00162 00163 /* ------------------------------------------------------------------------ */ 00164 int phUniverse::size(int x) const 00165 { 00166 return this->m_elts[x].size; 00167 } 00168 00169 /* ------------------------------------------------------------------------ */ 00170 uint32_t phUniverse::num_sets() const 00171 { 00172 return this->m_num; 00173 } 00174 00175 /* ------------------------------------------------------------------------ */ 00176 void phUniverse::join(int x, int y) 00177 { 00178 ph_uni_elt *elts = this->m_elts; 00179 00180 /* compare the rank of x and y... */ 00181 /* if the rank of x is greater than y, we'll merge y into x */ 00182 if (elts[x].rank > elts[y].rank) 00183 { 00184 /* Set the new parent of y */ 00185 elts[y].p = x; 00186 /* Add the size of component y to new parent x */ 00187 elts[x].size += elts[y].size; 00188 } 00189 else 00190 { 00191 /* Set the new parent of x to y */ 00192 elts[x].p = y; 00193 /* Add the size of component x to new parent y */ 00194 elts[y].size += elts[x].size; 00195 /* increase parent y's rank */ 00196 if (elts[x].rank == elts[y].rank) 00197 elts[y].rank++; 00198 } 00199 00200 if (this->m_num > 0) this->m_num--; 00201 } 00202 00203 /* ---------------------------------------------------------------------- */ 00204 typedef float (*phDistFunctionType)( const uint8_t *ptr1, 00205 const uint8_t *ptr2 ); 00206 00207 /* ---------------------------------------------------------------------- */ 00208 float dist_grey8(const uint8_t *ptr1, 00209 const uint8_t *ptr2 ) 00210 { 00211 return ((float)ptr1[0]-(float)ptr2[0]); 00212 } 00213 00214 /* ---------------------------------------------------------------------- */ 00215 float dist_hsv24(const uint8_t *ptr1, 00216 const uint8_t *ptr2 ) 00217 { 00218 float hd = ((float)ptr1[0]-(float)ptr2[0]); 00219 float sd = ((float)ptr1[1]-(float)ptr2[1]); 00220 float vd = ((float)ptr1[2]-(float)ptr2[2]); 00221 return sqrt((hd*hd)+(sd*sd)+(vd*vd)); 00222 } 00223 00224 /* ---------------------------------------------------------------------- */ 00225 float dist_rgb24(const uint8_t *ptr1, 00226 const uint8_t *ptr2 ) 00227 { 00228 float rd = ((float)ptr1[0]-(float)ptr2[0]); 00229 float gd = ((float)ptr1[1]-(float)ptr2[1]); 00230 float bd = ((float)ptr1[2]-(float)ptr2[2]); 00231 return sqrt((rd*rd)+(gd*gd)+(bd*bd)); 00232 } 00233 00234 /* ---------------------------------------------------------------------- */ 00235 graphSegmentation_Filter::graphSegmentation_Filter( uint32_t min_size, 00236 uint32_t threshold, 00237 int color_blobs ): 00238 phFilter("graphSegmentation_Filter") 00239 00240 { 00241 //phImageValidFormatMask; 00242 this->m_format = phImageRGB24 | phImageHSV24 | phImageGREY8; 00243 00244 this->m_num_edges = 0; 00245 00246 this->m_edges = NULL; 00247 this->m_edges_size = 0; 00248 00249 this->m_output = NULL; 00250 this->m_output_size = 0; 00251 00252 this->m_thresholds = NULL; 00253 this->m_thresholds_size = 0; 00254 00255 this->m_universe = new phUniverse(0); 00256 00257 this->m_min_size = min_size; 00258 this->m_thresh = threshold; 00259 this->m_color_blobs = color_blobs; 00260 00261 this->m_last_thresh = threshold == 0 ? 1 : 0; 00262 this->m_last_thresholds_size = 0; 00263 } 00264 00265 /* ---------------------------------------------------------------------- */ 00266 graphSegmentation_Filter::~graphSegmentation_Filter() 00267 { 00268 phFree(this->m_edges); 00269 this->m_edges_size = 0; 00270 phFree(this->m_output); 00271 this->m_output_size = 0; 00272 phFree(this->m_thresholds); 00273 this->m_thresholds_size = 0; 00274 phDelete(this->m_universe); 00275 } 00276 00277 /* ------------------------------------------------------------------------ */ 00278 phFilter *graphSegmentation_Filter::cloneFilter() 00279 { 00280 phFUNCTION("graphSegmentation_Filter::cloneFilter") 00281 int locked = 0; 00282 graphSegmentation_Filter *graphSegmentation = new graphSegmentation_Filter(); 00283 00284 phTHIS_LOOSE_LOCK(locked); 00285 00286 /* graphSegmentation->set(this->m_value); */ 00287 00288 phTHIS_LOOSE_UNLOCK(locked); 00289 00290 return (phFilter *)graphSegmentation; 00291 } 00292 00293 /* ---------------------------------------------------------------------- */ 00294 int graphSegmentation_Filter::set( uint32_t min_size, 00295 uint32_t threshold, 00296 int color_blobs ) 00297 { 00298 phFUNCTION("graphSegmentation_Filter::set") 00299 int locked = 0; 00300 phTHIS_LOOSE_LOCK(locked); 00301 00302 this->m_min_size = min_size; 00303 this->m_thresh = threshold; 00304 this->m_color_blobs = color_blobs; 00305 00306 phTHIS_LOOSE_UNLOCK(locked); 00307 00308 return phSUCCESS; 00309 } 00310 00311 /* ---------------------------------------------------------------------- */ 00312 int graphSegmentation_Filter::setMinSize( uint32_t min_size ) 00313 { 00314 phFUNCTION("graphSegmentation_Filter::set") 00315 int locked = 0; 00316 phTHIS_LOOSE_LOCK(locked); 00317 00318 this->m_min_size = min_size; 00319 00320 phTHIS_LOOSE_UNLOCK(locked); 00321 00322 return phSUCCESS; 00323 } 00324 00325 /* ---------------------------------------------------------------------- */ 00326 int graphSegmentation_Filter::setThreshold( uint32_t threshold ) 00327 { 00328 phFUNCTION("graphSegmentation_Filter::set") 00329 int locked = 0; 00330 phTHIS_LOOSE_LOCK(locked); 00331 00332 this->m_thresh = threshold; 00333 00334 phTHIS_LOOSE_UNLOCK(locked); 00335 00336 return phSUCCESS; 00337 } 00338 00339 /* ---------------------------------------------------------------------- */ 00340 int graphSegmentation_Filter::setColorBlobs( int color_blobs ) 00341 { 00342 phFUNCTION("graphSegmentation_Filter::set") 00343 int locked = 0; 00344 phTHIS_LOOSE_LOCK(locked); 00345 00346 this->m_color_blobs = color_blobs; 00347 00348 phTHIS_LOOSE_UNLOCK(locked); 00349 00350 return phSUCCESS; 00351 } 00352 /* ---------------------------------------------------------------------- */ 00353 uint32_t graphSegmentation_Filter::getMinSize() 00354 { 00355 phFUNCTION("graphSegmentation_Filter::set") 00356 int locked = 0; 00357 uint32_t min_size = 0; 00358 00359 phTHIS_LOOSE_LOCK(locked); 00360 00361 min_size = this->m_min_size; 00362 00363 phTHIS_LOOSE_UNLOCK(locked); 00364 00365 return min_size; 00366 00367 } 00368 00369 /* ---------------------------------------------------------------------- */ 00370 uint32_t graphSegmentation_Filter::getThreshold() 00371 { 00372 phFUNCTION("graphSegmentation_Filter::set") 00373 int locked = 0; 00374 uint32_t threshold = 0; 00375 00376 phTHIS_LOOSE_LOCK(locked); 00377 00378 threshold = this->m_thresh; 00379 00380 phTHIS_LOOSE_UNLOCK(locked); 00381 00382 return threshold; 00383 00384 } 00385 00386 /* ---------------------------------------------------------------------- */ 00387 int graphSegmentation_Filter::getColorBlobs() 00388 { 00389 phFUNCTION("graphSegmentation_Filter::set") 00390 int locked = 0; 00391 int color_blobs = 0; 00392 00393 phTHIS_LOOSE_LOCK(locked); 00394 00395 color_blobs = this->m_color_blobs; 00396 00397 phTHIS_LOOSE_UNLOCK(locked); 00398 00399 return color_blobs; 00400 } 00401 00402 /* ---------------------------------------------------------------------- */ 00403 /* This algorithm was adapted from the "Efficient Graph Based Segmentation" 00404 * source code at: 00405 * http://people.cs.uchicago.edu/~pff/segment/ 00406 * 00407 * It has been changed to work with a uint8_t buffer and use pointer arithmetic 00408 * to try and speed up the algorithm. 00409 * 00410 */ 00411 void graphSegmentation_Filter::SegmentImage( ) 00412 { 00413 ph_edge *cur_edge = NULL; 00414 uint32_t num_edges= 0; 00415 00416 phDistFunctionType distpixels = NULL; 00417 00418 uint8_t *ptr= this->Image; 00419 const uint32_t w = this->width; 00420 const uint32_t h = this->height; 00421 const uint32_t d = this->depth; 00422 00423 register uint32_t x = 0; 00424 register uint32_t y = 0; 00425 register int a = 0; 00426 //register int b = 0; 00427 00428 uint8_t *ptr_cur = (uint8_t *)ptr; 00429 uint8_t *ptr_right = NULL; 00430 uint8_t *ptr_down = NULL; 00431 uint8_t *ptr_down_right = NULL; 00432 uint8_t *ptr_up_right = NULL; 00433 00434 switch (format) 00435 { 00436 case phImageRGB24: 00437 distpixels = dist_rgb24; 00438 break; 00439 case phImageHSV24: 00440 distpixels = dist_hsv24; 00441 break; 00442 case phImageGREY8: 00443 distpixels = dist_grey8; 00444 break; 00445 } 00446 00447 this->m_num_edges = 0; 00448 cur_edge = this->m_edges; 00449 00450 for (y = 0; y < h; y++) 00451 { 00452 ptr_right = &(ptr_cur[d]); 00453 if (y < (h-1)) 00454 { 00455 ptr_down = &(ptr_cur[w*d]); 00456 ptr_down_right = &(ptr_cur[(w+1)*d]); 00457 } 00458 if (y > 0) 00459 { 00460 ptr_up_right = &(ptr[((((y-1) * w)+1) * d)]); 00461 } 00462 a = y * w; 00463 for (x = 0; x < w; x++, ptr_cur += d, a++) 00464 { 00465 /* right */ 00466 if (x < w-1) 00467 { 00468 cur_edge->a = a; 00469 cur_edge->b = y * w + (x+1); 00470 cur_edge->w = (distpixels)(ptr_cur, ptr_right); 00471 00472 cur_edge++; 00473 num_edges++; 00474 ptr_right += d; 00475 } 00476 00477 /* down */ 00478 if (y < h-1) 00479 { 00480 cur_edge->a = a; 00481 cur_edge->b = (y+1) * w + x; 00482 cur_edge->w = (distpixels)(ptr_cur, ptr_down); 00483 00484 cur_edge++; 00485 num_edges++; 00486 ptr_down += d; 00487 } 00488 00489 if ((x < w-1) && (y < h-1)) 00490 { 00491 cur_edge->a = a; 00492 cur_edge->b = (y+1) * w + (x+1); 00493 cur_edge->w = (distpixels)(ptr_cur, ptr_down_right); 00494 00495 cur_edge++; 00496 num_edges++; 00497 ptr_down_right += d; 00498 } 00499 00500 if ((x < w-1) && (y > 0)) 00501 { 00502 cur_edge->a = a; 00503 cur_edge->b = (y-1) * w + (x+1); 00504 cur_edge->w = (distpixels)(ptr_cur, ptr_up_right); 00505 00506 cur_edge++; 00507 num_edges++; 00508 ptr_up_right += d; 00509 } 00510 } 00511 } 00512 00513 this->m_num_edges = num_edges; 00514 } 00515 00516 /* ---------------------------------------------------------------------- */ 00517 bool operator<(const ph_edge &a, const ph_edge &b) 00518 { 00519 return a.w < b.w; 00520 } 00521 00522 #define ph_GRAPHTHRESHOLD(size, c) (c/size) 00523 00524 /* ---------------------------------------------------------------------- */ 00525 int graphSegmentation_Filter::SegmentGraph() 00526 { 00527 phFUNCTION("graphSegmentation_Filter::SegmentGraph") 00528 00529 const uint32_t num_vertices= (this->width * this->height); 00530 register uint32_t i = 0; 00531 register int a = 0; 00532 register int b = 0; 00533 00534 /* sort edges by weight */ 00535 std::sort(this->m_edges, this->m_edges + this->m_num_edges); 00536 00537 /* make a disjoint-set forest */ 00538 this->m_universe->resize(num_vertices); 00539 00540 /* init / alloc thresholds */ 00541 phDALLOC(this->m_thresholds, 00542 this->m_thresholds_size, 00543 num_vertices, 00544 float); 00545 00546 if ((this->m_thresh != this->m_last_thresh ) || 00547 (this->m_thresholds_size != this->m_last_thresholds_size )) 00548 { 00549 for (i = 0; i < num_vertices; i++) 00550 { 00551 this->m_thresholds[i] = ph_GRAPHTHRESHOLD(1,this->m_thresh); 00552 } 00553 this->m_last_thresh = this->m_thresh; 00554 this->m_last_thresholds_size = this->m_thresholds_size; 00555 } 00556 00557 /* for each ph_edge, in non-decreasing weight order... */ 00558 for (i = 0; i < this->m_num_edges; i++) 00559 { 00560 ph_edge *pedge = &(this->m_edges[i]); 00561 00562 /* components connected by this ph_edge */ 00563 a = this->m_universe->find(pedge->a); 00564 b = this->m_universe->find(pedge->b); 00565 if (a != b) 00566 { 00567 if ((pedge->w <= this->m_thresholds[a]) && 00568 (pedge->w <= this->m_thresholds[b])) 00569 { 00570 this->m_universe->join(a, b); 00571 00572 a = this->m_universe->find(a); 00573 00574 this->m_thresholds[a] = pedge->w + 00575 ph_GRAPHTHRESHOLD(this->m_universe->size(a), 00576 this->m_thresh); 00577 } 00578 } 00579 } 00580 00581 return phSUCCESS; 00582 error: 00583 return phFAIL; 00584 } 00585 00586 /* ---------------------------------------------------------------------- */ 00587 /* 00588 * This is the code that actually segments the graph from the output 00589 * generated in the graphSegmentation_Filter::SegmentImage method. 00590 * 00591 * This is also take from the original segment_image function from the 00592 * "Efficient Graph Based Segmentation" source code. 00593 * 00594 */ 00595 void graphSegmentation_Filter::ProcessGraph( ) 00596 { 00597 register uint32_t i = 0; 00598 register uint32_t y; 00599 uint8_t *outptr = this->m_output; 00600 uint32_t d = this->depth; 00601 register int a = 0; 00602 register int b = 0; 00603 const uint32_t lim = this->width * this->height; 00604 00605 phUniverse *u = this->m_universe; 00606 00607 // post process small components 00608 for (i = 0; i < this->m_num_edges; i++) 00609 { 00610 a = u->find(m_edges[i].a); 00611 b = u->find(m_edges[i].b); 00612 if ((a != b) && 00613 ((u->size(a) < this->m_min_size) || 00614 (u->size(b) < this->m_min_size))) 00615 { 00616 u->join(a, b); 00617 } 00618 } 00619 00620 #if phGRAPH_USE_RANDOM_COLORS() 00621 uint8_t *colors = new uint8_t[w*h*d]; 00622 uint8_t *col_ptr= colors; 00623 for (y = 0; y < lim; y++ ) 00624 { 00625 for (x = 0; x < d; x++ ) 00626 { 00627 colors[x+y] = (uint8_t)random(); 00628 } 00629 } 00630 #endif 00631 00632 /* Edited from the original code that colors the blobs in that this 00633 * colors the image for an RGB image represented by a uint8_t buffer */ 00634 if (this->m_color_blobs) 00635 { 00636 for (y = 0; y < lim; y++, outptr += d ) 00637 { 00638 phMemcpy(outptr,&(this->Image[u->find(y) * d]),d); 00639 00640 #if phGRAPH_USE_RANDOM_COLORS() 00641 comp = u->find(y); 00642 phMemcpy(outptr,&(colors[comp*d]),d); 00643 #endif 00644 } 00645 } 00646 00647 #if phGRAPH_USE_RANDOM_COLORS() 00648 delete [] colors; 00649 #endif 00650 } 00651 00652 /* ---------------------------------------------------------------------- */ 00653 int graphSegmentation_Filter::filter() 00654 { 00655 phFUNCTION("graphSegmentation_Filter::filter") 00656 00657 /* defined before being called: 00658 * width, height, depth, format, Image 00659 */ 00660 00661 /* Begin Filter */ 00662 phDALLOC(this->m_output,this->m_output_size, 00663 this->width*this->height*this->depth, 00664 uint8_t); 00665 00666 phDALLOC(this->m_edges,this->m_edges_size, 00667 this->width*this->height*4, 00668 ph_edge) 00669 00670 /* segment the image : populates this->m_edges */ 00671 this->SegmentImage( ); 00672 /* segment : Populates this->m_universe */ 00673 this->SegmentGraph(); 00674 /* process the graph : colors the image */ 00675 this->ProcessGraph( ); 00676 00677 this->m_workspaceImage->setImage( this->width, 00678 this->height, 00679 this->format, 00680 0, 00681 this->m_output ); 00682 00683 /* End Filter */ 00684 00685 /* make sure to free up any space here, not including Image */ 00686 00687 return phSUCCESS; 00688 00689 error: 00690 /* make sure to free up any space here, not including Image */ 00691 00692 return phFAIL; 00693 00694 } 00695 00696
Copyright (C) 2002 - 2007 |
Philip D.S. Thoren ( pthoren@users.sourceforge.net ) University Of Massachusetts at Lowell Robotics Lab |