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

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:37 2016 by Doxygen 1.6.3