Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

cmv_region.h

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

Tekkotsu v2.4.1
Generated Tue Aug 16 16:32:46 2005 by Doxygen 1.4.4