Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

cmv_region.h

Go to the documentation of this file.
00001 #ifndef __CMV_REGION_H__
00002 #define __CMV_REGION_H__
00003 
00004 /*! @file
00005 * @brief Region and connected component support for #CMVision
00006 * @author James R. Bruce, School of Computer Science, Carnegie Mellon University
00007 *
00008 * Licensed under the <a href="../gpl-2.0.txt">GNU GPL version 2</a>
00009 *
00010 * $Author: ejt $
00011 * $Name: tekkotsu-4_0 $
00012 * $Revision: 1.11 $
00013 * $State: Exp $
00014 * $Date: 2007/11/11 23:57:27 $
00015 */
00016 
00017 //@todo this should go away - ET
00018 const unsigned int MAX_COLORS=20;
00019 
00020 
00021 #include <stdio.h>
00022 //#include "Shared/Util.h"
00023 //#include "Visiondefines.h"
00024 
00025 #include "cmv_types.h"
00026 #include <ext/hash_map>
00027 
00028 struct hashcmp_eqstr { bool operator()(const char* s1, const char* s2) const
00029                      { return strcasecmp(s1, s2) == 0; } };
00030 
00031 namespace CMVision{
00032 
00033 template<class T>
00034 class DummyT1 {
00035 };
00036 
00037 //==== Utility Functions ===========================================//
00038 
00039 // sum of integers over range [x,x+w)
00040 inline int range_sum(int x,int w)
00041 {
00042   return(w*(2*x + w-1) / 2);
00043 }
00044 
00045 // table used by bottom_bit
00046 const int log2modp[37] = {
00047   0, 1, 2,27, 3,24,28, 0, 4,17,25,31,29,12, 0,14, 5, 8,18,
00048   0,26,23,32,16,30,11,13, 7, 0,22,15,10, 6,21, 9,20,19
00049 };
00050 
00051 // returns index of least significant set bit
00052 template <class num>
00053 inline int bottom_bit(num n)
00054 {
00055   return(log2modp[(n & -n) % 37]);
00056 }
00057 
00058 // returns index of most significant set bit
00059 template <class num>
00060 inline num top_bit(num n)
00061 {
00062   /*
00063   n = n | (n >>  1);
00064   n = n | (n >>  2);
00065   n = n | (n >>  4);
00066   n = n | (n >>  8);
00067   n = n | (n >> 16);
00068   n = n - (n >>  1);
00069   return(log2modp[n % 37]);
00070   */
00071 
00072   n |= (n >> 1) | (n >> 2) | (n >>  3);
00073   n |= (n >> 4) | (n >> 8) | (n >> 12);
00074   n |= (n >> 16);
00075   n -= (n >>  1);
00076   return(log2modp[n % 37]);
00077 
00078   /*
00079   int i = 1;
00080   if(!n) return(0);
00081   while(n>>i) i++;
00082   return(i);
00083   */
00084 }
00085 
00086 //==== Main Library Functions ======================================//
00087 
00088 #define REMOVE_NOISE
00089 
00090 template <class rle_t,class tmap_t>
00091 int EncodeRuns(rle_t *rle,tmap_t *map,int width,int height,int max_runs)
00092 // Changes the flat array version of the thresholded image into a run
00093 // length encoded version, which speeds up later processing since we
00094 // only have to look at the points where values change.
00095 {
00096   tmap_t m,save;
00097   tmap_t *row;
00098   int x,y,j,l;
00099  
00100 #ifdef REMOVE_NOISE
00101   int lastcolor;
00102   int noise;
00103   int noise_back;
00104 #endif
00105   
00106   rle_t r;
00107 
00108   r.next = 0;
00109 
00110   // initialize terminator restore
00111   save = map[0];
00112 
00113   j = 0;
00114 
00115   for(y=0; y<height; y++){
00116     row = &map[y * width];
00117 
00118     // restore previous terminator and store next
00119     // one in the first pixel on the next row
00120     row[0] = save;
00121     save = row[width];
00122     row[width] = MAX_COLORS;
00123     r.y = y;
00124 
00125     x = 0;
00126 
00127 #ifdef REMOVE_NOISE
00128     lastcolor=0;
00129     noise=0;
00130     noise_back=0;
00131 #endif
00132     while(x < width){
00133       m = row[x];
00134       r.x = x;
00135 
00136       l = x;
00137       while(row[x] == m) x++;
00138 
00139 #ifdef REMOVE_NOISE
00140       if (x - l > 4) {
00141         if (noise>=4) {
00142           j=j-noise_back;
00143           lastcolor=0;
00144         }
00145         noise=0; 
00146         noise_back=0;
00147       } else {
00148         noise++;
00149         if (m) noise_back++;
00150       }
00151 #endif
00152 
00153       if(m!=0 || x>=width){
00154         r.color = m; 
00155         r.width = x - l;
00156         r.parent = j;
00157         rle[j++] = r;
00158 
00159         if(j >= max_runs) {
00160           row[width] = save;
00161           return(j);
00162         }
00163       }
00164 #ifdef REMOVE_NOISE
00165       else if (!m && lastcolor && x-l<5) {
00166         rle[j-1].width+=x-l;
00167       }
00168       
00169       lastcolor=m;
00170 #endif
00171     }
00172   }
00173 
00174   return(j);
00175 }
00176 
00177 template <class rle_t,class tmap_t,class edge_t>
00178 int EncodeRunsUseEdges(rle_t *rle,tmap_t *map,edge_t *edge_map,int width,int height,int max_runs)
00179 // Changes the flat array version of the thresholded image into a run
00180 // length encoded version, which speeds up later processing since we
00181 // only have to look at the points where values change.
00182 {
00183   tmap_t m,save;
00184   tmap_t *row;
00185   int x,y,j,l;
00186   rle_t r;
00187 
00188   r.next = 0;
00189 
00190   // initialize terminator restore
00191   save = map[0];
00192 
00193   j = 0;
00194   for(y=0; y<height; y++){
00195     row = &map[y * width];
00196 
00197     // restore previous terminator and store next
00198     // one in the first pixel on the next row
00199     row[0] = save;
00200     save = row[width];
00201     row[width] = MAX_COLORS;
00202     r.y = y;
00203 
00204     x = 0;
00205     while(x < width){
00206       m = row[x];
00207       r.x = x;
00208 
00209       l = x;
00210       while(row[x] == m) x++;
00211 
00212       if(m!=0 || x>=width){
00213   r.color = m; 
00214   r.width = x - l;
00215   r.parent = j;
00216   rle[j++] = r;
00217 
00218         // printf("run (%d,%d):%d %d\n",r.x,r.y,r.width,r.color);
00219 
00220   if(j >= max_runs) return(j);
00221       }
00222     }
00223   }
00224 
00225   return(j);
00226 }
00227 
00228 #ifdef ENABLE_JOIN_NEARBY
00229 template<class rle_t>
00230 inline int AdvanceToNextRun(int run_idx, rle_t *runs) {
00231   run_idx++;
00232   if(runs[run_idx].x==-1)
00233     run_idx=runs[run_idx].next;
00234   return run_idx;
00235 }
00236 #else
00237 #define AdvanceToNextRun(x,y) (x+1)
00238 #endif
00239 
00240 //#define DUMP_RUNS
00241 
00242 // returns true if the runs are ok, false otherwise
00243 template<class rle_t>
00244 bool CheckRuns(rle_t *rle,int num_runs,int width,int height) {
00245   bool error;
00246   int x,y;
00247   int run_idx;
00248 
00249   error=false;
00250   x = y = 0;
00251 
00252   run_idx = 0;
00253   while(run_idx < num_runs && !error) {
00254     rle_t *cur_run=&rle[run_idx];
00255 
00256     if(cur_run->x < x) {
00257       printf("\nat (%d,%d) backwards x movement, cur x=%d run x=%d\n",x,y,x,cur_run->x);
00258       error=true;
00259     }
00260 
00261     if(cur_run->width < 0) {
00262       printf("\nat (%d,%d) negative run width %d\n",x,y,cur_run->width);
00263       error=true;
00264     }
00265 
00266     if(cur_run->y != y) {
00267       printf("\nat (%d,%d) wrong y value, cur y=%d run y=%d\n",x,y,y,cur_run->y);
00268       error=true;
00269     }
00270 
00271     x = cur_run->x + cur_run->width;
00272     if(x == width) {
00273       x = 0;
00274       y++;
00275     }
00276 
00277     run_idx++;
00278   }
00279 
00280   if(!error && (x!=0 || y!=height)) {
00281     printf("at (%d,%d) wrong ending position\n",x,y);
00282     error=true;
00283   }
00284 
00285   return !error;
00286 }
00287 
00288 #ifdef ENABLE_JOIN_NEARBY
00289 // join nearby runs of the same color
00290 // specifically those seperated by only one pixel of black
00291 template <class rle_t>
00292 int JoinNearbyRuns(rle_t *rle_from,rle_t *rle_to,int num_runs,int width,int height) {
00293   int fup,fon,fdn; // runs with x that is largest but still <= x
00294   int ton;
00295   int fup_n,fon_n,fdn_n;
00296   int x,y; // location on fill row of output
00297   int next_x,cand_next_x; // x to move to next
00298   int which_bump; // which row to bump
00299   rle_t rup,ron,rdn;
00300   rle_t rup_n,ron_n,rdn_n;
00301   rle_t rout;
00302 
00303   fup = -1;
00304   fup=AdvanceToNextRun(fup,rle_from);
00305   rup = rle_from[fup];
00306   fup_n=AdvanceToNextRun(fup,rle_from);
00307   rup_n=rle_from[fup_n];
00308 
00309   fon = fup;
00310   fon=AdvanceToNextRun(fon,rle_from);
00311   ron = rle_from[fon];
00312   fon_n=AdvanceToNextRun(fon,rle_from);
00313   ron_n=rle_from[fon_n];
00314 
00315   fdn = fon;
00316   while(rle_from[fdn].y==0)
00317     fdn=AdvanceToNextRun(fdn,rle_from);
00318   rdn = rle_from[fdn];
00319   
00320   ton = 0;
00321 
00322   // store black row before image
00323   rout.color = 0;
00324   rout.width = width;
00325   rout.x = 0;
00326   rout.y = -1;
00327   rout.parent = ton;
00328   rle_to[ton++] = rout;
00329 
00330   rout = ron;
00331 
00332   y = 0;
00333   x = ron.x+ron.width-1;
00334 
00335   while(rup_n.x <= x && rup_n.y < y) {
00336     fup=fup_n;
00337     rup=rup_n;
00338     fup_n=AdvanceToNextRun(fup,rle_from);
00339     rup_n=rle_from[fup_n];
00340   }
00341 
00342   while(y < height && fon < num_runs-1) {
00343     static const int bridge_width=1;
00344     
00345     bool output=true;
00346     bool advance=true;
00347 
00348     // check if should merge based on this row's runs
00349     // case on row left, on row right
00350     if(rout.x+rout.width+bridge_width >= ron_n.x && rout.color == ron_n.color && rout.y == ron_n.y) {
00351       // merge
00352       rout.width = ron_n.x+ron_n.width - rout.x;
00353       output = false;
00354     }
00355     else {
00356       // check if should merge based on this row and row above
00357       // case on row left, up row right
00358       int x_diff;
00359       x_diff = rup_n.x - (rout.x+rout.width-1);
00360       if(0 < x_diff && x_diff<=2 && rout.color == rup_n.color && rout.y == rup_n.y+1 && (rup_n.x < ron_n.x || rout.y != ron_n.y)) {
00361         // extend
00362         rout.width += x_diff;
00363         output = false;
00364         advance = false;
00365       }
00366     }
00367     
00368     if(output) {
00369       // output
00370       rout.parent = ton;
00371       rle_to[ton++] = rout;
00372       rout = ron_n;
00373     }
00374 
00375     if(advance) {
00376       fon=fon_n;
00377       fon_n=AdvanceToNextRun(fon,rle_from);
00378       ron_n=rle_from[fon_n];
00379     }
00380 
00381     next_x = rout.x + rout.width - 1;
00382     x = next_x;
00383     if(x==width-1) {
00384       y++;
00385       x=0;
00386     }
00387 
00388     while(rup.y < y-1 || (rup_n.x <= x && rup_n.y < y)) {
00389       fup=fup_n;
00390       rup=rup_n;
00391       fup_n=AdvanceToNextRun(fup,rle_from);
00392       rup_n=rle_from[fup_n];
00393     }
00394   }
00395 
00396   // store pending output run
00397   rout.parent = ton;
00398   rle_to[ton++] = rout;
00399 
00400   // store black row after image
00401   rout.color = 0;
00402   rout.width = width;
00403   rout.x = 0;
00404   rout.y = height;
00405   rout.parent = ton;
00406   rle_to[ton++] = rout;
00407 
00408   return ton;
00409 }
00410 #endif
00411 
00412 template <class rle_t>
00413 void ConnectComponents(rle_t *map,int num)
00414 // Connect components using four-connecteness so that the runs each
00415 // identify the global parent of the connected region they are a part
00416 // of.  It does this by scanning adjacent rows and merging where
00417 // similar colors overlap.  Used to be union by rank w/ path
00418 // compression, but now is just uses path compression as the global
00419 // parent index is a simpler rank bound in practice.
00420 // WARNING: This code is complicated.  I'm pretty sure it's a correct
00421 //   implementation, but minor changes can easily cause big problems.
00422 //   Read the papers on this library and have a good understanding of
00423 //   tree-based union find before you touch it
00424 {
00425   int l1,l2;
00426   rle_t r1,r2;
00427   int i,j,s;
00428 
00429   // l2 starts on first scan line, l1 starts on second
00430   l2 = 0;
00431 #ifdef ENABLE_JOIN_NEARBY
00432   l2=AdvanceToNextRun(l2,map);
00433 #endif
00434   l1 = 1;
00435   while(map[l1].y == 0) 
00436     l1=AdvanceToNextRun(l1,map);
00437 
00438   // Do rest in lock step
00439   r1 = map[l1];
00440   r2 = map[l2];
00441   s = l1;
00442   while(l1 < num){
00443     /*
00444     printf("%6d:(%3d,%3d,%3d) %6d:(%3d,%3d,%3d)\n",
00445      l1,r1.x,r1.y,r1.width,
00446      l2,r2.x,r2.y,r2.width);
00447     */
00448 
00449     if(r1.color==r2.color && r1.color){
00450       // case 1: r2.x <= r1.x < r2.x + r2.width
00451       // case 2: r1.x <= r2.x < r1.x + r1.width
00452       if((r2.x<=r1.x && r1.x<r2.x+r2.width) ||
00453    (r1.x<=r2.x && r2.x<r1.x+r1.width)){
00454         if(s != l1){
00455           // if we didn't have a parent already, just take this one
00456           map[l1].parent = r1.parent = r2.parent;
00457           s = l1;
00458         }else if(r1.parent != r2.parent){
00459           // otherwise union two parents if they are different
00460 
00461           // find terminal roots of each path up tree
00462           i = r1.parent;
00463           while(i != map[i].parent) i = map[i].parent;
00464           j = r2.parent;
00465           while(j != map[j].parent) j = map[j].parent;
00466 
00467           // union and compress paths; use smaller of two possible
00468           // representative indicies to preserve DAG property
00469           if(i < j){
00470       map[j].parent = i;
00471             map[l1].parent = map[l2].parent = r1.parent = r2.parent = i;
00472           }else{
00473             map[i].parent = j;
00474             map[l1].parent = map[l2].parent = r1.parent = r2.parent = j;
00475           }
00476         }
00477       }
00478     }
00479 
00480     // Move to next point where values may change
00481     i = (r2.x + r2.width) - (r1.x + r1.width);
00482     if(i >= 0) r1 = map[l1=AdvanceToNextRun(l1,map)];
00483     if(i <= 0) r2 = map[l2=AdvanceToNextRun(l2,map)];
00484   }
00485  
00486   // Now we need to compress all parent paths
00487   i=0;
00488 #ifdef ENABLE_JOIN_NEARBY
00489   if(map[i].x==-1)
00490     i = map[i].next;
00491 #endif
00492   while(i<num) {
00493     j = map[i].parent;
00494     map[i].parent = map[j].parent;
00495 
00496     i=AdvanceToNextRun(i,map);
00497   }
00498 }
00499 
00500 template <class region_t,class rle_t>
00501 int ExtractRegions(region_t *reg,int max_reg,rle_t *rmap,int num)
00502 // Takes the list of runs and formats them into a region table,
00503 // gathering the various statistics along the way.  num is the number
00504 // of runs in the rmap array, and the number of unique regions in
00505 // reg[] (bounded by max_reg) is returned.  Implemented as a single
00506 // pass over the array of runs.
00507 {
00508   int b,i,n,a;
00509   rle_t r;
00510 
00511   n = 0;
00512   i=0;
00513 #ifdef ENABLE_JOIN_NEARBY
00514   i=AdvanceToNextRun(i,rmap);
00515 #endif
00516   while(i<num) {
00517     if(rmap[i].color){
00518       r = rmap[i];
00519       if(r.parent == i){
00520         // Add new region if this run is a root (i.e. self parented)
00521         rmap[i].parent = b = n;  // renumber to point to region id
00522         reg[b].color = r.color;
00523         reg[b].area = r.width;
00524         reg[b].x1 = r.x;
00525         reg[b].y1 = r.y;
00526         reg[b].x2 = r.x + r.width;
00527         reg[b].y2 = r.y;
00528         reg[b].cen_x = range_sum(r.x,r.width);
00529         reg[b].cen_y = r.y * r.width;
00530   reg[b].run_start = i;
00531   reg[b].iterator_id = i; // temporarily use to store last run
00532         n++;
00533         if(n >= max_reg) return(max_reg);
00534       }else{
00535         // Otherwise update region stats incrementally
00536         b = rmap[r.parent].parent;
00537         rmap[i].parent = b; // update parent to identify region id
00538         reg[b].area += r.width;
00539         reg[b].x2 = std::max(r.x + r.width,reg[b].x2);
00540         reg[b].x1 = std::min((int)r.x,reg[b].x1);
00541         reg[b].y2 = r.y; // last set by lowest run
00542         reg[b].cen_x += range_sum(r.x,r.width);
00543         reg[b].cen_y += r.y * r.width;
00544   // set previous run to point to this one as next
00545   rmap[reg[b].iterator_id].next = i;
00546   reg[b].iterator_id = i;
00547       }
00548     }
00549 
00550     i=AdvanceToNextRun(i,rmap);
00551   }
00552 
00553   // calculate centroids from stored sums
00554   i=0;
00555 #ifdef ENABLE_JOIN_NEARBY
00556   i=AdvanceToNextRun(i,rmap);
00557 #endif
00558   while(i<n) {
00559     a = reg[i].area;
00560     reg[i].cen_x = (float)reg[i].cen_x / a;
00561     reg[i].cen_y = (float)reg[i].cen_y / a;
00562     reg[i].iterator_id = 0;
00563     reg[i].x2--; // change to inclusive range
00564     i=AdvanceToNextRun(i,rmap);
00565   }
00566 
00567   return(n);
00568 }
00569 
00570 template <class color_class_state_t,class region_t>
00571 int SeparateRegions(color_class_state_t *color,int colors,
00572         region_t *reg,int num)
00573 // Splits the various regions in the region table a separate list for
00574 // each color.  The lists are threaded through the table using the
00575 // region's 'next' field.  Returns the maximal area of the regions,
00576 // which can be used later to speed up sorting.
00577 {
00578   region_t *p;
00579   int i;
00580   int c;
00581   int area,max_area;
00582 
00583   // clear out the region list head table
00584   for(i=0; i<colors; i++){
00585     color[i].list = NULL;
00586     color[i].num  = 0;
00587   }
00588 
00589   // step over the table, adding successive
00590   // regions to the front of each list
00591   max_area = 0;
00592   for(i=0; i<num; i++){
00593     p = &reg[i];
00594     c = p->color;
00595     area = p->area;
00596 
00597     if(area >= color[c].min_area){
00598       if(area > max_area) max_area = area;
00599       color[c].num++;
00600       p->next = color[c].list;
00601       color[c].list = p;
00602     }
00603   }
00604 
00605   return(max_area);
00606 }
00607 
00608 // These are the tweaking values for the radix sort given below
00609 // Feel free to change them, though these values seemed to work well
00610 // in testing.  Don't worry about extra passes to get all 32 bits of
00611 // the area; the implementation only does as many passes as needed to
00612 // touch the most significant set bit (MSB of largest region's area)
00613 #define CMV_RBITS 6
00614 #define CMV_RADIX (1 << CMV_RBITS)
00615 #define CMV_RMASK (CMV_RADIX-1)
00616 
00617 template <class region_t>
00618 region_t *SortRegionListByArea(region_t *list,int passes)
00619 // Sorts a list of regions by their area field.
00620 // Uses a linked list based radix sort to process the list.
00621 {
00622   region_t *tbl[CMV_RADIX],*p,*pn;
00623   int slot,shift;
00624   int i,j;
00625 
00626   // Handle trivial cases
00627   if(!list || !list->next) return(list);
00628 
00629   // Initialize table
00630   for(j=0; j<CMV_RADIX; j++) tbl[j] = NULL;
00631 
00632   for(i=0; i<passes; i++){
00633     // split list into buckets
00634     shift = CMV_RBITS * i;
00635     p = list;
00636     while(p){
00637       pn = p->next;
00638       slot = ((p->area) >> shift) & CMV_RMASK;
00639       p->next = tbl[slot];
00640       tbl[slot] = p;
00641       p = pn;
00642     }
00643 
00644     // integrate back into partially ordered list
00645     list = NULL;
00646     for(j=0; j<CMV_RADIX; j++){
00647       p = tbl[j];
00648       tbl[j] = NULL; // clear out table for next pass
00649       while(p){
00650         pn = p->next;
00651         p->next = list;
00652         list = p;
00653         p = pn;
00654       }
00655     }
00656   }
00657 
00658   return(list);
00659 }
00660 
00661 template <class color_class_state_t>
00662 void SortRegions(color_class_state_t *color,int colors,int max_area)
00663 // Sorts entire region table by area, using the above
00664 // function to sort each threaded region list.
00665 {
00666   int i,p;
00667 
00668   // do minimal number of passes sufficient to touch all set bits
00669   p = (top_bit(max_area) + CMV_RBITS-1) / CMV_RBITS;
00670 
00671   // sort each list
00672   for(i=0; i<colors; i++){
00673     color[i].list = SortRegionListByArea(color[i].list,p);
00674   }
00675 }
00676 
00677 //#define DEBUG_REGION_MERGE
00678 //#define STATS_REGION_MERGE
00679 //#define DEBUG_CONSIDER_REGION_MERGE
00680 
00681 template<class region,class rle_t>
00682 void MergeRegions(region *p,region *q,region **q_prev_next,rle_t *runs) {
00683   int l,r,t,b;
00684   int a;
00685 
00686   // find union box
00687   l = std::min(p->x1,q->x1);
00688   r = std::max(p->x2,q->x2);
00689   t = std::min(p->y1,q->y1);
00690   b = std::max(p->y2,q->y2);
00691 
00692   // merge them to create a new region
00693   a = p->area + q->area;
00694   p->x1 = l;
00695   p->x2 = r;
00696   p->y1 = t;
00697   p->y2 = b;
00698   p->cen_x = ((p->cen_x * p->area) + (q->cen_x * q->area)) / a;
00699   p->cen_y = ((p->cen_y * p->area) + (q->cen_y * q->area)) / a;
00700   p->area = a;
00701 
00702   // reparent runs and hook up next pointers
00703   int parent_run_idx;
00704   int p_run_idx,q_run_idx;
00705   rle_t *p_run,*q_run,*last_run;
00706 
00707   last_run = NULL;
00708   p_run_idx = p->run_start;
00709   p_run = &runs[p_run_idx];
00710   parent_run_idx = p_run->parent;
00711   q_run_idx = q->run_start;
00712   q_run = &runs[q_run_idx];
00713 
00714   if(p_run_idx == 0) {
00715     last_run = p_run;
00716     p_run_idx = p_run->next;
00717     p_run = &runs[p_run_idx];
00718   } else if(q_run_idx == 0) {
00719     p->run_start = q_run_idx;
00720     last_run = q_run;
00721     last_run->parent = parent_run_idx;
00722     q_run_idx = q_run->next;
00723     q_run = &runs[q_run_idx];
00724   }
00725 
00726   if(p_run_idx!=0 &&
00727      (p_run_idx < q_run_idx || q_run_idx==0)) {
00728     if(last_run)
00729       last_run->next = p_run_idx;
00730     last_run = p_run;
00731     p_run_idx = p_run->next;
00732     p_run = &runs[p_run_idx];
00733     while(p_run_idx != 0 && p_run_idx < q_run_idx) {
00734       last_run = p_run;
00735       p_run_idx = p_run->next;
00736       p_run = &runs[p_run_idx];
00737     }
00738   } else {
00739     if(last_run)
00740       last_run->next = q_run_idx;
00741     else
00742       p->run_start = q_run_idx;
00743     last_run = q_run;
00744     last_run->parent = parent_run_idx;
00745     q_run_idx = q_run->next;
00746     q_run = &runs[q_run_idx];
00747   }
00748 
00749   // last_run is non-null at this point
00750   // p->run_start is correct at this point
00751 
00752   while(q_run_idx!=0 && p_run_idx!=0) {
00753     if(p_run_idx < q_run_idx) {
00754       last_run->next = p_run_idx;
00755       last_run = p_run;
00756       p_run_idx = p_run->next;
00757       p_run = &runs[p_run_idx];
00758     } else {
00759       last_run->next = q_run_idx;
00760       last_run = q_run;
00761       last_run->parent = parent_run_idx;
00762       q_run_idx = q_run->next;
00763       q_run = &runs[q_run_idx];
00764     }
00765   }
00766 
00767   if(p_run_idx != 0) {
00768     last_run->next = p_run_idx;
00769   }
00770   if(q_run_idx != 0) {
00771     do {
00772       last_run->next = q_run_idx;
00773       last_run = q_run;
00774       last_run->parent = parent_run_idx;
00775       q_run_idx = q_run->next;
00776       q_run = &runs[q_run_idx];
00777     } while(q_run_idx != 0);
00778     last_run->next = 0;
00779   }
00780   
00781   // remove q from list (old smaller region)
00782   *q_prev_next = q->next;
00783 }
00784 
00785 template<class region>
00786 void CalcXYBounds(region *p,double density_thresh,int area,int &xl,int &xh,int &yl,int& yh) {
00787   int a,l,r,t,b;
00788   int width,height;
00789   int xexp,yexp;
00790 
00791   a = p->area;
00792   l = p->x1;
00793   r = p->x2;
00794   t = p->y1;
00795   b = p->y2;
00796 
00797   width  = r - l + 1;
00798   height = b - t + 1;
00799 
00800   // derived from:
00801   // (a + area) / ((width + xexp) * height) > density_thresh
00802   xexp = (int) ((a + area) / (density_thresh * height) - width); // round down
00803 
00804   xl = l - xexp;
00805   xh = r + xexp;
00806 
00807   // derived from:
00808   // (a + area) / (width * (height + yexp)) > density_thresh
00809   yexp = (int) ((a + area) / (density_thresh * width) - height); // round down
00810 
00811   yl = t - yexp;
00812   yh = b + yexp;
00813 }
00814 
00815 template<class region,class rle_t>
00816 int MergeRegions(region *p,double density_thresh,rle_t *runs)
00817 {
00818   region *q,*s;
00819   int l,r,t,b;
00820   int a;
00821   int merged;
00822   int last_area; // last size used to calculate bounds
00823   int xbl,xbh; // x bound low/high
00824   int ybl,ybh; // y bound low/high
00825   
00826   merged = 0;
00827   last_area = 0;
00828 
00829   while(p){
00830     q = p->next;
00831     s = p;
00832 
00833     if(q) {
00834       CalcXYBounds(p,density_thresh,q->area,xbl,xbh,ybl,ybh);
00835       last_area = q->area;
00836     }
00837 
00838     while(q){
00839       bool advance=true;
00840 
00841       if(q->area > last_area) {
00842         CalcXYBounds(p,density_thresh,q->area,xbl,xbh,ybl,ybh);
00843         last_area = q->area;
00844       }
00845 
00846       if(q->x1 >= xbl &&
00847          q->x2 <= xbh &&
00848          q->y1 >= ybl &&
00849          q->y2 <= ybh) {
00850         // find union box and get its total area
00851         l = std::min(p->x1,q->x1);
00852         r = std::max(p->x2,q->x2);
00853         t = std::min(p->y1,q->y1);
00854         b = std::max(p->y2,q->y2);
00855         a = (r-l+1) * (b-t+1);
00856 
00857         CalcXYBounds(p,density_thresh,q->area,xbl,xbh,ybl,ybh);
00858         last_area = q->area;
00859 
00860         // if density of merged region is still above threshold
00861         if((double)(p->area + q->area) / a > density_thresh){
00862 
00863           MergeRegions(p,q,&s->next,runs);
00864 
00865           // return to next region after p
00866           q = p->next;
00867           s = p;
00868           merged++;
00869           advance=false;
00870         }
00871       }
00872 
00873       if(advance) {
00874   s = q;
00875   q = q->next;
00876       }
00877     }
00878     p = p->next;
00879   }
00880 
00881   return(merged);
00882 }
00883 
00884 template<class color_class_state_t,class rle_t>
00885 int MergeRegions(color_class_state_t *color,int colors,rle_t *runs)
00886 {
00887   int i,m;
00888   int num;
00889 
00890   num = 0;
00891 
00892   for(i=0; i<colors; i++){
00893     if(color[i].merge_threshold >= 1.0)
00894       m = 0;
00895     else
00896       m = MergeRegions(color[i].list,color[i].merge_threshold,runs);
00897     num += m;
00898     color[i].num -= m;
00899   }
00900 
00901   return(num);
00902 }
00903 
00904 template<class region, class rle_t>
00905 bool CheckRegions(region *p,rle_t *runs) {
00906   bool ok=true;
00907 
00908   while(p && ok) {
00909     int area;
00910     int run_idx,next_run_idx;
00911     rle_t *currun,*next_run;
00912 
00913     run_idx = p->run_start;
00914     currun = &runs[run_idx];
00915     area = 0;
00916 
00917     do {
00918       area += currun->width;
00919       
00920       if(currun->parent != runs[p->run_start].parent) {
00921         printf("wrong parent id on run %d (claims parent %d should be %d)\n",
00922                 run_idx,currun->parent,runs[p->run_start].parent);
00923         ok = false;
00924       }
00925       
00926       next_run_idx = currun->next;
00927       next_run = (next_run_idx!=0 ? &runs[next_run_idx] : NULL);
00928       
00929       if(next_run_idx != 0 && 
00930          next_run_idx <= run_idx) {
00931         printf("failed to progress, going from run %d to run %d\n",run_idx,next_run_idx);
00932         ok = false;
00933       }
00934       
00935       run_idx = next_run_idx;
00936       currun = next_run;
00937     } while(run_idx != 0 && ok);
00938 
00939     if(p->area != area) {
00940       ok = false;
00941       printf("area mismatch, claimed %d actually %d\n",p->area,area);
00942     }
00943 
00944     p = p->next;
00945   }
00946 
00947   return ok;
00948 }
00949 
00950 template<class color_class_state_t,class rle_t>
00951 bool CheckRegions(color_class_state_t *color,int colors,rle_t *runs) {
00952   bool ok=true;
00953   int i;
00954 
00955   for(i=0; i<colors && ok; i++){
00956     ok = CheckRegions(color[i].list,runs);
00957     if(!ok) {
00958       printf("region check failed for color %d\n",i);
00959     }
00960   }
00961 
00962   return(ok);
00963 }
00964 
00965 template <class region_t,class rle_t>
00966 int FindStart(rle_t *rmap,int left,int right,int x,DummyT1<region_t> dummy=DummyT1<region_t>())
00967 // This function uses binary search to find the leftmost run whose
00968 // interval either contains or is greater than x.
00969 {
00970   int m;
00971 
00972   while(right > left){
00973     m = (left + right) / 2;
00974     if(x > rmap[m].x+rmap[m].width){
00975       left = m + 1;
00976     }else if(x < rmap[m].x){
00977       right = m;
00978     }else{
00979       return(m);
00980     }
00981   }
00982 
00983   return(m);
00984 }
00985 
00986 template <class rle_t>
00987 int FindStart(rle_t *rmap,int left,int right,int x,int y)
00988 // This function uses binary search to find the leftmost run whose
00989 // interval either contains or is greater than x and equals y
00990 {
00991   int m=0;
00992 
00993   while(right > left){
00994     m = (left + right) / 2;
00995     if(y > rmap[m].y || 
00996        (y == rmap[m].y && x > rmap[m].x+rmap[m].width)){
00997       left = m + 1;
00998     }else if(y < rmap[m].y || 
00999              (y == rmap[m].y && x < rmap[m].x)){
01000       right = m;
01001     }else{
01002       return(m);
01003     }
01004   }
01005 
01006   return(m);
01007 }
01008 
01009 template <class region_t,class rle_t>
01010 void CreateRunIndex(int *yindex,rle_t *rmap,int num,DummyT1<region_t> dummy=DummyT1<region_t>())
01011 // Creates an index of where rows start in the run map.  This can be
01012 // used to speed up searching over the map.  This function assumes
01013 // there is at least one run in every row.
01014 {
01015   int y = 0;
01016   yindex[y] = 0;
01017 
01018   for(int i=0; i<num; i++){
01019     if(rmap[i].y > y){
01020       y = rmap[i].y;
01021       yindex[y] = i;
01022     }
01023   }
01024 }
01025 
01026 template <class color_class_state_t>
01027 void GetNextRegion(color_class_state_t *color,int colors,int max_area)
01028 {
01029   // TODO
01030 }
01031 
01032 template <class color_class_state_t>
01033 void CalcTotalArea(color_class_state_t *color)
01034 {
01035   region *cur_reg;
01036 
01037   cur_reg = color->list;
01038   color->total_area = 0;
01039   while(cur_reg!=NULL) {
01040     color->total_area += cur_reg->area;
01041     cur_reg = cur_reg->next;
01042   }
01043 }
01044 
01045 template <class color_class_state_t>
01046 void CalcTotalArea(color_class_state_t *color,int colors)
01047 {
01048   for(int i=0; i<colors; i++)
01049     CalcTotalArea(&color[i]);
01050 }
01051 
01052 template <class data>
01053 int find(data *arr,int start,int end,data key)
01054 {
01055   int i;
01056 
01057   for(i=start; i<end; i++){
01058     if(arr[i] == key) return(i);
01059   }
01060 
01061   return(end);
01062 }
01063 
01064 template <class color_class_state_t>
01065 int LoadColorInformation(color_class_state_t *color,int max,const char *filename, __gnu_cxx::hash_map<const char*, unsigned int, __gnu_cxx::hash<const char*>, hashcmp_eqstr> &color_names)
01066 {
01067   char buf[512];
01068   FILE *in;
01069   int len;
01070   int sl,sr;
01071   int num;
01072 
01073   int idx,r,g,b,ms;
01074   float merge_threshold;
01075   char *name;
01076 
01077   //printf("about to open color file\n");
01078   in = fopen(filename,"rt");
01079   if(!in) return(0);
01080 
01081   memset(color,0,sizeof(color_class_state_t)*max);
01082   num = 0;
01083 
01084   //printf("about to fgets\n");
01085   while(fgets(buf,256,in)){
01086     //printf("fgets result is '%s'\n",buf);
01087     len = strlen(buf) - 1;
01088     buf[len] = 0;
01089 
01090     if(len && buf[0]!='#'){
01091       sl = find(buf,   0,len,'"');
01092       sr = find(buf,sl+1,len,'"');
01093       if(sl<len && sr<len){
01094   buf[sl] = buf[sr] = 0;
01095   idx = r = g = b = ms = 0;
01096   sscanf(buf,"%d (%d %d %d)",&idx,&r,&g,&b);
01097   name = buf+sl+1;
01098         merge_threshold = 1.0;
01099   sscanf(buf+sr+1,"%d %g",&ms,&merge_threshold);
01100 
01101   if(idx>=0 && idx<max && ms>0){
01102     color[idx].min_area = ms;
01103           color[idx].merge_threshold = merge_threshold;
01104     color[idx].color.red   = r;
01105     color[idx].color.green = g;
01106     color[idx].color.blue  = b;
01107     color[idx].name = strdup(name);
01108     color_names[color[idx].name]=idx;
01109     if(idx >= num) num = idx+1;
01110   } else {
01111     printf("Options error: %2d (%3d %3d %3d) '%s' %d\n",
01112                   idx,r,g,b,name,ms);
01113   }
01114       }
01115     }
01116   }
01117 
01118   fclose(in);
01119 
01120   return(num);
01121 }
01122 
01123 } // namespace
01124 
01125 #endif

Tekkotsu v4.0
Generated Thu Nov 22 00:54:51 2007 by Doxygen 1.5.4