cmvision.cc
Go to the documentation of this file.
00001 /*=========================================================================
00002     CMVision.cc
00003   -------------------------------------------------------------------------
00004     Implementation of the CMVision real time Color Machine Vision library
00005   -------------------------------------------------------------------------
00006     Copyright 1999, 2000         #### ### ### ## ## ## #### ##  ###  ##  ##
00007     James R. Bruce              ##    ####### ## ## ## ##   ## ## ## ######
00008     School of Computer Science  ##    ## # ## ## ## ##  ### ## ## ## ## ###
00009     Carnegie Mellon University   #### ##   ##  ###  ## #### ##  ###  ##  ##
00010   -------------------------------------------------------------------------
00011     This library is free software; you can redistribute it and/or
00012     modify it under the terms of the GNU Lesser General Public
00013     License as published by the Free Software Foundation; either
00014     version 2.1 of the License, or (at your option) any later version.
00015 
00016     This library is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019     Lesser General Public License for more details.
00020 
00021     You should have received a copy of the GNU Lesser General Public
00022     License along with this library; if not, write to the Free Software
00023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00024   -------------------------------------------------------------------------
00025     Revision History:
00026       1999-11-18:  Initial release version (JRB)
00027       2000-05-20:  Added Bugfixes from Peter,
00028                    fixed bounding box bug (JRB)
00029       2000-06-04:  Some other minor fixes (JRB)
00030       2000-07-02:  Added average color and density merging (JRB)
00031       2000-07-20:  Added dual threshold capability (JRB)
00032   =========================================================================*/
00033 
00034 #include "cmvision.h"
00035 #include <string.h>
00036 #if !defined (WIN32)
00037   #include <strings.h>
00038 #endif
00039 
00040 #if defined (WIN32)
00041   #define strncasecmp _strnicmp
00042   #define strdup _strdup
00043 #endif
00044 
00045 //==== Utility Functions ===========================================//
00046 // These could be coded as macros, but the inline versions seem to
00047 // optimize better, and tend to have cleaner definitions
00048 
00049 // sum of integers over range [x,x+w)
00050 inline int range_sum(int x,int w)
00051 {
00052   return(w*(2*x + w-1) / 2);
00053 }
00054 
00055 // returns maximal value of two parameters
00056 template <class num>
00057 inline num max(num a,num b)
00058 {
00059   return((a > b)? a : b);
00060 }
00061 
00062 // returns minimal value of two parameters
00063 template <class num>
00064 inline num min(num a,num b)
00065 {
00066   return((a < b)? a : b);
00067 }
00068 
00069 // returns index of least significant set bit
00070 int log2modp[37] = {
00071   0, 1, 2,27, 3,24,28, 0, 4,17,25,31,29,12, 0,14, 5, 8,18,
00072   0,26,23,32,16,30,11,13, 7, 0,22,15,10, 6,21, 9,20,19
00073 };
00074 
00075 template <class num>
00076 inline int bottom_bit(num n)
00077 {
00078   return(log2modp[(n & -n) % 37]);
00079 }
00080 
00081 /* Marginally slower naive version of above function
00082 template <class num>
00083 inline num bottom_bit(num n)
00084 {
00085   int i = 0;
00086   if(!n) return(0);
00087   while(!(n&(1<<i))) i++;
00088   return(i + 1);
00089 }
00090 */
00091 
00092 // returns index of most significant set bit
00093 template <class num>
00094 inline num top_bit(num n)
00095 {
00096   int i = 1;
00097   if(!n) return(0);
00098   while(n>>i) i++;
00099   return(i);
00100 }
00101 
00102 
00103 //==== Class Implementation ========================================//
00104 
00105 void CMVision::classifyFrame(image_pixel * restrict img,unsigned * restrict map)
00106 // Classifies an image passed in as img, saving bits in the entries
00107 // of map representing which thresholds that pixel satisfies.
00108 {
00109   int i,m,s;
00110   int m1,m2;
00111   image_pixel p;
00112 
00113   unsigned *uclas = u_class; // Ahh, the joys of a compiler that
00114   unsigned *vclas = v_class; //   has to consider pointer aliasing
00115   unsigned *yclas = y_class;
00116 
00117   s = width * height;
00118 
00119   if(options & CMV_DUAL_THRESHOLD){
00120     for(i=0; i<s; i+=2){
00121       p = img[i/2];
00122       m = uclas[p.u] & vclas[p.v];
00123       m1 = m & yclas[p.y1];
00124       m2 = m & yclas[p.y2];
00125       map[i + 0] = m1 | (m1 >> 16);
00126       map[i + 1] = m2 | (m2 >> 16);
00127     }
00128   }else{
00129     for(i=0; i<s; i+=2){
00130       p = img[i/2];
00131       m = uclas[p.u] & vclas[p.v];
00132       map[i + 0] = m & yclas[p.y1];
00133       map[i + 1] = m & yclas[p.y2];
00134     }
00135   }
00136 }
00137 
00138 int CMVision::encodeRuns(rle * restrict out,unsigned * restrict map)
00139 // Changes the flat array version of the threshold satisfaction map
00140 // into a run length encoded version, which speeds up later processing
00141 // since we only have to look at the points where values change.
00142 {
00143   int x,y,j,l;
00144   unsigned m,save;
00145   int size;
00146   unsigned *row;
00147   rle r;
00148 
00149   size = width * height;
00150 
00151   // initialize terminator restore
00152   save = map[0];
00153 
00154   j = 0;
00155   for(y=0; y<height; y++){
00156     row = &map[y * width];
00157 
00158     // restore previous terminator and store next
00159     // one in the first pixel on the next row
00160     row[0] = save;
00161     save = row[width];
00162     row[width] = CMV_NONE;
00163 
00164     x = 0;
00165     while(x < width){
00166       m = row[x];
00167       // m = m & (~m + 1); // get last bit
00168       l = x;
00169       while(row[x] == m) x++;
00170       // x += (row[x] == CMV_NONE); //  && (last & m);
00171 
00172       r.color  = m;
00173       r.length = x - l;
00174       r.parent = j;
00175       out[j++] = r;
00176       if(j >= CMV_MAX_RUNS) return(0);
00177     }
00178   }
00179 
00180   return(j);
00181 }
00182 
00183 void CMVision::connectComponents(rle * restrict map,int num)
00184 // Connect components using four-connecteness so that the runs each
00185 // identify the global parent of the connected region they are a part
00186 // of.  It does this by scanning adjacent rows and merging where similar
00187 // colors overlap.  Used to be union by rank w/ path compression, but now
00188 // is just uses path compression as the global parent index seems to be
00189 // a simpler fast approximation of rank in practice.
00190 // WARNING: This code is *extremely* complicated and twitchy.  It appears
00191 //   to be a correct implementation, but minor changes can easily cause
00192 //   big problems.  Read the papers on this library and have a good
00193 //   understanding of tree-based union find before you touch it
00194 {
00195   int x1,x2;
00196   int l1,l2;
00197   rle r1,r2;
00198   int i,p,s,n;
00199 
00200   l1 = l2 = 0;
00201   x1 = x2 = 0;
00202 
00203   // Lower scan begins on second line, so skip over first
00204   while(x1 < width){
00205     x1 += map[l1++].length;
00206   }
00207   x1 = 0;
00208 
00209   // Do rest in lock step
00210   r1 = map[l1];
00211   r2 = map[l2];
00212   s = l1;
00213   while(l1 < num){
00214     if(r1.color==r2.color && r1.color){
00215       if((x1>=x2 && x1<x2+r2.length) || (x2>=x1 && x2<x1+r1.length)){
00216         if(s != l1){
00217           map[l1].parent = r1.parent = r2.parent;
00218           s = l1;
00219         }else{
00220           // find terminal roots of each path
00221           n = r1.parent;
00222           while(n != map[n].parent) n = map[n].parent;
00223           p = r2.parent;
00224           while(p != map[p].parent) p = map[p].parent;
00225 
00226           // must use smaller of two to preserve DAGness!
00227           if(n < p){
00228             map[p].parent = n;
00229           }else{
00230             map[n].parent = p;
00231           }
00232         }
00233       }
00234     }
00235 
00236     // Move to next point where values may change
00237     if(x1+r1.length < x2+r2.length){
00238       x1 += r1.length;
00239       r1 = map[++l1];
00240     }else{
00241       x2 += r2.length;
00242       r2 = map[++l2];
00243     }
00244   }
00245 
00246   // Now we need to compress all parent paths
00247   for(i=0; i<num; i++){
00248     p = map[i].parent;
00249     if(p > i){
00250       while(p != map[p].parent) p = map[p].parent;
00251       map[i].parent = p;
00252     }else{
00253       map[i].parent = map[p].parent;
00254     }
00255   }
00256 
00257   // Ouch, my brain hurts.
00258 }
00259 
00260 int CMVision::extractRegions(region * restrict reg,rle * restrict rmap,int num)
00261 // Takes the list of runs and formats them into a region table,
00262 // gathering the various statistics we want along the way.
00263 // num is the number of runs in the rmap array, and the number of
00264 // unique regions in reg[] (< CMV_MAX_REGIONS) is returned.
00265 // Implemented as a single pass over the array of runs.
00266 {
00267   int x,y,i;
00268   int b,n,a;
00269   rle r;
00270   yuv black = {0,0,0};
00271 
00272   x = y = n = 0;
00273   for(i=0; i<num; i++){
00274     r = rmap[i];
00275 
00276     if(r.color){
00277       if(r.parent == i){
00278         // Add new region if this run is a root (i.e. self parented)
00279         rmap[i].parent = b = n;  // renumber to point to region id
00280         reg[b].color = bottom_bit(r.color) - 1;
00281         reg[b].area = r.length;
00282         reg[b].x1 = x;
00283         reg[b].y1 = y;
00284         reg[b].x2 = x + r.length;
00285         reg[b].y2 = y;
00286         reg[b].sum_x = range_sum(x,r.length);
00287         reg[b].sum_y = y * r.length;
00288         reg[b].average = black;
00289         // reg[b].area_check = 0; // DEBUG ONLY
00290         n++;
00291         if(n >= CMV_MAX_REGIONS) return(CMV_MAX_REGIONS);
00292       }else{
00293         // Otherwise update region stats incrementally
00294         b = rmap[r.parent].parent;
00295         rmap[i].parent = b; // update to point to region id
00296         reg[b].area += r.length;
00297         reg[b].x2 = max(x + r.length,reg[b].x2);
00298         reg[b].x1 = min(x,reg[b].x1);
00299         reg[b].y2 = y; // last set by lowest run
00300         reg[b].sum_x += range_sum(x,r.length);
00301         reg[b].sum_y += y * r.length;
00302       }
00303       /* DEBUG
00304       if(r.color == 1){
00305         printf("{%d,%d,%d} ",i,rmap[i].parent,b);
00306       }
00307       */
00308     }
00309 
00310     // step to next location
00311     x = (x + r.length) % width;
00312     y += (x == 0);
00313   }
00314 
00315   // printf("\n");
00316 
00317   // calculate centroids from stored temporaries
00318   for(i=0; i<n; i++){
00319     a = reg[i].area;
00320     reg[i].cen_x = (float)reg[i].sum_x / a;
00321     reg[i].cen_y = (float)reg[i].sum_y / a;
00322   }
00323 
00324   return(n);
00325 }
00326 
00327 void CMVision::calcAverageColors(region * restrict reg,int num_reg,
00328                                  image_pixel * restrict img,
00329                                  rle * restrict rmap,int num_runs)
00330 // calculates the average color for each region.
00331 // num is the number of runs in the rmap array, and the number of
00332 // unique regions in reg[] (< CMV_MAX_REGIONS) is returned.
00333 // Implemented as a single pass over the image, and a second pass over
00334 // the regions.
00335 {
00336   int i,j,x,l;
00337   image_pixel p;
00338   rle r;
00339   int sum_y,sum_u,sum_v;
00340   int b,xs;
00341 
00342   yuv avg;
00343   int area;
00344 
00345   // clear out temporaries
00346   for(i=0; i<num_reg; i++){
00347     reg[i].sum_x = 0;
00348     reg[i].sum_y = 0;
00349     reg[i].sum_z = 0;
00350   }
00351 
00352   x = 0;
00353 
00354   // printf("FRAME_START\n");
00355 
00356   // sum up color components for each region, by traversing image and runs
00357   for(i=0; i<num_runs; i++){
00358     r = rmap[i];
00359     l = r.length;
00360 
00361     if(!r.color){
00362       x += l;
00363     }else{
00364       xs = x;
00365       p = img[x / 2];
00366 
00367       if(x & 1){
00368         sum_y = p.y2;
00369         sum_u = p.u;
00370         sum_v = p.v;
00371         // area = 1;
00372         x++;
00373         l--;
00374       }else{
00375         sum_y = sum_u = sum_v = 0;
00376         area = 0;
00377       }
00378 
00379       for(j=0; j<l/2; j++){
00380         p = img[x / 2];
00381         sum_y += p.y1 + p.y2;
00382         sum_u += 2 * p.u;
00383         sum_v += 2 * p.v;
00384         x+=2;
00385         // area += 2;
00386       }
00387 
00388       if(l & 1){
00389         x++;
00390         p = img[x / 2];
00391         sum_y += p.y1;
00392         sum_u += p.u;
00393         sum_v += p.v;
00394         // area++;
00395       }
00396 
00397       // add sums to region
00398       b = r.parent;
00399       reg[b].sum_x += sum_y;
00400       reg[b].sum_y += sum_u;
00401       reg[b].sum_z += sum_v;
00402       // reg[b].area_check += area;
00403 
00404       /*
00405       if((r.color & (1 << reg[b].color)) != (1 << reg[b].color)){
00406         printf("(%d,%d)",r.color,reg[b].color);
00407       }
00408 
00409       if(x != xs + r.length){
00410         printf("Length mismatch %d:%d\n",x,xs + r.length);
00411       }
00412       */
00413 
00414       x = xs + r.length;
00415     }
00416   }
00417 
00418   // Divide sums by area to calculate average colors
00419   for(i=0; i<num_reg; i++){
00420     area = reg[i].area;
00421     avg.y = reg[i].sum_x / area;
00422     avg.u = reg[i].sum_y / area;
00423     avg.v = reg[i].sum_z / area;
00424 
00425     /*
00426     if(reg[i].area != reg[i].area_check){
00427       printf("Area Mismatch: %d %d\n",reg[i].area,reg[i].area_check);
00428     }
00429 
00430     x = (y_class[avg.y] & u_class[avg.u] & v_class[avg.v]);
00431     j = reg[i].color;
00432     l = (1 << j);
00433     if((x & l) != l){
00434       printf("Error: c=%d a=%d (%d,%d) (%d,%d,%d)\n",
00435              reg[i].color,area,
00436              (int)reg[i].cen_x,(int)reg[i].cen_y,
00437              avg.y,avg.u,avg.v);
00438     }
00439     */
00440 
00441     reg[i].average = avg;
00442   }
00443 }
00444 
00445 int CMVision::separateRegions(region * restrict reg,int num)
00446 // Splits the various regions in the region table a separate list
00447 // for each color.  The lists are threaded through the table using
00448 // the region's 'next' field.  Returns the maximal area of the
00449 // regions, which we use below to speed up sorting.
00450 {
00451   region *p;
00452   int i,l;
00453   int area,max_area;
00454 
00455   // clear out the region table
00456   for(i=0; i<CMV_MAX_COLORS; i++){
00457     region_count[i] = 0;
00458     region_list[i] = NULL;
00459   }
00460 
00461   // step over the table, adding successive
00462   // regions to the front of each list
00463   max_area = 0;
00464   for(i=0; i<num; i++){
00465     p = &reg[i];
00466     area = p->area;
00467     if(area >= CMV_MIN_AREA){
00468       if(area > max_area) max_area = area;
00469       l = p->color;
00470       region_count[l]++;
00471       p->next = region_list[l];
00472       region_list[l] = p;
00473     }
00474   }
00475 
00476   return(max_area);
00477 }
00478 
00479 // These are the tweaking values for the radix sort given below
00480 // Feel free to change them, though these values seemed to work well
00481 // in testing.  Don't worry about extra passes to get all 32 bits of
00482 // the area; the implementation only does as many passes as needed to
00483 // touch the most significant set bit (MSB of biggest region's area)
00484 #define CMV_RBITS 6
00485 #define CMV_RADIX (1 << CMV_RBITS)
00486 #define CMV_RMASK (CMV_RADIX-1)
00487 
00488 CMVision::region *CMVision::sortRegionListByArea(region * restrict list,int passes)
00489 // Sorts a list of regions by their area field.
00490 // Uses a linked list based radix sort to process the list.
00491 {
00492   region *tbl[CMV_RADIX],*p,*pn;
00493   int slot,shift;
00494   int i,j;
00495 
00496   // handle trivial cases
00497   if(!list || !list->next) return(list);
00498 
00499   // Initialize table
00500   for(j=0; j<CMV_RADIX; j++) tbl[j] = NULL;
00501 
00502   for(i=0; i<passes; i++){
00503     // split list into buckets
00504     shift = CMV_RBITS * i;
00505     p = list;
00506     while(p){
00507       pn = p->next;
00508       slot = ((p->area) >> shift) & CMV_RMASK;
00509       p->next = tbl[slot];
00510       tbl[slot] = p;
00511       p = pn;
00512     }
00513 
00514     // integrate back into partially ordered list
00515     list = NULL;
00516     for(j=0; j<CMV_RADIX; j++){
00517       p = tbl[j];
00518       tbl[j] = NULL;  // clear out table for next pass
00519       while(p){
00520         pn = p->next;
00521         p->next = list;
00522         list = p;
00523         p = pn;
00524       }
00525     }
00526   }
00527 
00528   return(list);
00529 }
00530 
00531 void CMVision::sortRegions(int max_area)
00532 // Sorts entire region table by area, using the above
00533 // function to sort each threaded region list.
00534 {
00535   int i,p;
00536 
00537   // do minimal number of passes sufficient to touch all set bits
00538   p = top_bit((max_area + CMV_RBITS-1) / CMV_RBITS);
00539 
00540   // sort each list
00541   for(i=0; i<CMV_MAX_COLORS; i++){
00542     region_list[i] = sortRegionListByArea(region_list[i],p);
00543   }
00544 }
00545 
00546 int CMVision::mergeRegions(region *p,int num,double density_thresh)
00547 // Looks through regions and merges pairs of the same color that would
00548 // have a high density after combining them (where density is the area
00549 // in pixels of the region divided by the bounding box area).  This
00550 // implementation sucks, and I promise real spatial data structures in
00551 // the future so n^2 ugliness like this is not necessary.
00552 {
00553   region *q,*s;
00554   int l,r,t,b;
00555   int a;
00556   int merged;
00557 
00558   //double tmp;
00559 
00560   merged = 0;
00561 
00562   while(p && merged<num){
00563     q = p->next;
00564     s = p;
00565 
00566     while(q){
00567       // find union box and get its total area
00568       l = min(p->x1,q->x1);
00569       r = max(p->x2,q->x2);
00570       t = min(p->y1,q->y1);
00571       b = max(p->y2,q->y2);
00572       a = (r-l) * (b-t);
00573 
00574       // if density of merged region is still above threshold
00575       if((double)(p->area + q->area) / a > density_thresh){
00576         // merge them to create a new region
00577         a = p->area + q->area;
00578         p->x1 = l;
00579         p->x2 = r;
00580         p->y1 = t;
00581         p->y2 = b;
00582         p->cen_x = ((p->cen_x * p->area) + (q->cen_x * q->area)) / a;
00583         p->cen_y = ((p->cen_y * p->area) + (q->cen_y * q->area)) / a;
00584         p->area = a;
00585 
00586         // remove q from list (old smaller region)
00587         q = q->next;
00588         s->next = q;
00589         merged++;
00590       }else{
00591         s = q;
00592         q = q->next;
00593       }
00594     }
00595     p = p->next;
00596   }
00597 
00598   return(merged);
00599 }
00600 
00601 int CMVision::mergeRegions()
00602 // Apply merge operation to all regions using the above function.
00603 {
00604   int i,m;
00605   int num;
00606 
00607   num = 0;
00608 
00609   for(i=0; i<CMV_MAX_COLORS; i++){
00610     m = mergeRegions(region_list[i],colors[i].expected_num,colors[i].merge);
00611     region_count[i] -= m;
00612     num += m;
00613   }
00614 
00615   return(num);
00616 }
00617 
00618 //==== Interface/Public Functions ==================================//
00619 
00620 #define ZERO(x) memset(x,0,sizeof(x))
00621 
00622 void CMVision::clear()
00623 {
00624   ZERO(y_class);
00625   ZERO(u_class);
00626   ZERO(v_class);
00627 
00628   ZERO(region_list);
00629   ZERO(region_count);
00630 
00631   ZERO(colors);
00632 
00633   map = NULL;
00634 }
00635 
00636 bool CMVision::initialize(int nwidth,int nheight)
00637 // Initializes library to work with images of specified size
00638 {
00639   width = nwidth;
00640   height = nheight;
00641 
00642   //  if(map) delete(map);
00643 
00644   map = new unsigned[width * height + 1];
00645   // Need 1 extra element to store terminator value in encodeRuns()
00646 
00647   options = CMV_THRESHOLD;
00648 
00649   return(map != NULL);
00650 }
00651 
00652 // sets bits in k in array arr[l..r]
00653 template <class num>
00654 void set_bits(num *arr,int len,int l,int r,num k)
00655 {
00656   int i;
00657 
00658   l = max(l,0);
00659   r = min(r+1,len);
00660 
00661   for(i=l; i<r; i++) arr[i] |= k;
00662 }
00663 
00664 template <class num>
00665 void clear_bits(num *arr,int len,int l,int r,num k)
00666 {
00667   int i;
00668 
00669   l = max(l,0);
00670   r = min(r+1,len);
00671 
00672   k = ~k;
00673   for(i=l; i<r; i++) arr[i] &= k;
00674 }
00675 
00676 #define CMV_STATE_SCAN   0
00677 #define CMV_STATE_COLORS 1
00678 #define CMV_STATE_THRESH 2
00679 #define CMV_MAX_BUF 256
00680 
00681 bool CMVision::loadOptions(const char *filename)
00682 // Loads in options file specifying color names and representative
00683 // rgb triplets.  Also loads in color class threshold values.
00684 {
00685   char buf[CMV_MAX_BUF],str[CMV_MAX_BUF];
00686   FILE *in;
00687   int state,i,n;
00688 
00689   int r,g,b;
00690   int exp_num;
00691   double merge;
00692   color_info *c;
00693 
00694   int y1,y2,u1,u2,v1,v2;
00695   unsigned k;
00696 
00697   // Open options file
00698   in = fopen(filename,"rt");
00699   if(!in) return(false);
00700 
00701   // Clear out previously set options
00702   for(i=0; i<CMV_COLOR_LEVELS; i++){
00703     y_class[i] = u_class[i] = v_class[i] = 0;
00704   }
00705   for(i=0; i<CMV_MAX_COLORS; i++){
00706     if(colors[i].name){
00707       delete(colors[i].name);
00708       colors[i].name = NULL;
00709     }
00710   }
00711 
00712   // Loop ever lines, processing via a simple parser
00713   state = 0;
00714   while(fgets(buf,CMV_MAX_BUF,in)){
00715     switch(state){
00716       case CMV_STATE_SCAN:
00717         n = sscanf(buf,"[%s",str);
00718         if(n == 1){
00719           if(!strncasecmp(str,"colors]",CMV_MAX_BUF)){
00720             state = CMV_STATE_COLORS;
00721             i = 0;
00722           }else if(!strncasecmp(str,"thresholds]",CMV_MAX_BUF)){
00723             state = CMV_STATE_THRESH;
00724             i = 0;
00725           }else{
00726             printf("CMVision: Ignoring unknown option header '%s'.\n",str);
00727           }
00728         }
00729         break;
00730       case CMV_STATE_COLORS:
00731         n = sscanf(buf,"(%d,%d,%d) %lf %d %s",&r,&g,&b,&merge,&exp_num,str);
00732         if(n == 6){
00733           // printf("(%d,%d,%d) %lf %d '%s'\n",
00734           //        r,g,b,merge,exp_num,str); fflush(stdout);
00735           if(i < CMV_MAX_COLORS){
00736             c = &colors[i];
00737             c->color.red   = r;
00738             c->color.green = g;
00739             c->color.blue  = b;
00740             c->name  = strdup(str);
00741             c->merge = merge;
00742             c->expected_num = exp_num;
00743             i++;
00744           }else{
00745             printf("CMVision: Too many colors, ignoring '%s'.\n",str);
00746           }
00747         }else if(n == 0){
00748           state = CMV_STATE_SCAN;
00749         }
00750         break;
00751       case CMV_STATE_THRESH:
00752         n = sscanf(buf,"(%d:%d,%d:%d,%d:%d)",&y1,&y2,&u1,&u2,&v1,&v2);
00753         if(n == 6){
00754           // printf("(%d:%d,%d:%d,%d:%d)\n",y1,y2,u1,u2,v1,v2);
00755           if(i < CMV_MAX_COLORS){
00756             c = &colors[i];
00757             c->y_low = y1;  c->y_high = y2;
00758             c->u_low = u1;  c->u_high = u2;
00759             c->v_low = v1;  c->v_high = v2;
00760 
00761             k = (1 << i);
00762             set_bits(y_class,CMV_COLOR_LEVELS,y1,y2,k);
00763             set_bits(u_class,CMV_COLOR_LEVELS,u1,u2,k);
00764             set_bits(v_class,CMV_COLOR_LEVELS,v1,v2,k);
00765             i++;
00766           }else{
00767             printf("CMVision: Too many thresholds.\n");
00768           }
00769         }else if(n == 0){
00770           state = CMV_STATE_SCAN;
00771         }
00772         break;
00773     }
00774   }
00775 
00776   /*
00777   for(i=0; i<CMV_COLOR_LEVELS; i++){
00778     printf("%08X %08X %08X\n",y_class[i],u_class[i],v_class[i]);
00779   }
00780   */
00781 
00782   fclose(in);
00783 
00784   return(true);
00785 }
00786 
00787 bool CMVision::saveOptions(char *filename)
00788 {
00789   color_info *c;
00790   FILE *out;
00791   int i;
00792 
00793   out = fopen(filename,"wt");
00794   if(!out) return(false);
00795 
00796   fprintf(out,"[Colors]\n");
00797   i = 0;
00798   while(colors[i].name){
00799     c = &colors[i];
00800     fprintf(out,"(%3d,%3d,%3d) %6.4f %d %s\n",
00801       c->color.red,c->color.green,c->color.blue,
00802       c->merge,c->expected_num,c->name);
00803     i++;
00804   }
00805 
00806   fprintf(out,"\n[Thresholds]\n");
00807   i = 0;
00808   while(colors[i].name){
00809     c = &colors[i];
00810     fprintf(out,"(%3d:%3d,%3d:%3d,%3d:%3d)\n",
00811       c->y_low,c->y_high,
00812       c->u_low,c->u_high,
00813       c->v_low,c->v_high);
00814     i++;
00815   }
00816 
00817   fclose(out);
00818 
00819   return(true);
00820 }
00821 
00822 bool CMVision::enable(unsigned opt)
00823 {
00824   unsigned int valid;
00825 
00826   valid = opt & CMV_VALID_OPTIONS;
00827   options |= valid;
00828 
00829   return(opt == valid);
00830 }
00831 
00832 bool CMVision::disable(unsigned opt)
00833 {
00834   unsigned int valid;
00835 
00836   valid = opt & CMV_VALID_OPTIONS;
00837   options &= ~valid;
00838 
00839   return(opt == valid);
00840 }
00841 
00842 void CMVision::close()
00843 {
00844   if(map) delete(map);
00845   map = NULL;
00846 }
00847 
00848 
00849 //==== Vision Testing Functions ====================================//
00850 
00851 bool CMVision::testClassify(rgb * restrict out,image_pixel * restrict image)
00852 {
00853   int i,s;
00854   rgb black = {0,0,0};
00855 
00856   if(!image || !out) return(false);
00857 
00858   classifyFrame(image,map);
00859 
00860   s = width * height;
00861 
00862   i = 0;
00863   while(i < s){
00864     while(i<s && !map[i]){
00865       out[i] = black;
00866       i++;
00867     }
00868     while(i<s && map[i]){
00869       out[i] = colors[bottom_bit(map[i])-1].color;
00870       i++;
00871     }
00872   }
00873 
00874   return(true);
00875 }
00876 
00877 bool CMVision::getThreshold(int color,
00878        int &y_low,int &y_high,
00879        int &u_low,int &u_high,
00880        int &v_low,int &v_high)
00881 {
00882   color_info *c;
00883 
00884   if(color<0 || color>=CMV_MAX_COLORS) return(false);
00885 
00886   c = &colors[color];
00887   y_low = c->y_low;  y_high = c->y_high;
00888   u_low = c->u_low;  u_high = c->u_high;
00889   v_low = c->v_low;  v_high = c->v_high;
00890 
00891   return(true);
00892 }
00893 
00894 bool CMVision::setThreshold(int color,
00895        int y_low,int y_high,
00896        int u_low,int u_high,
00897        int v_low,int v_high)
00898 {
00899   color_info *c;
00900   unsigned k;
00901 
00902   if(color<0 || color>=CMV_MAX_COLORS) return(false);
00903 
00904   c = &colors[color];
00905   k = 1 << color;
00906 
00907   clear_bits(y_class,CMV_COLOR_LEVELS,c->y_low,c->y_high,k);
00908   clear_bits(u_class,CMV_COLOR_LEVELS,c->u_low,c->u_high,k);
00909   clear_bits(v_class,CMV_COLOR_LEVELS,c->v_low,c->v_high,k);
00910 
00911   c->y_low = y_low;  c->y_high = y_high;
00912   c->u_low = u_low;  c->u_high = u_high;
00913   c->v_low = v_low;  c->v_high = v_high;
00914 
00915   set_bits(y_class,CMV_COLOR_LEVELS,y_low,y_high,k);
00916   set_bits(u_class,CMV_COLOR_LEVELS,u_low,u_high,k);
00917   set_bits(v_class,CMV_COLOR_LEVELS,v_low,v_high,k);
00918 
00919   return(true);
00920 }
00921 
00922 //==== Main Vision Functions =======================================//
00923 
00924 bool CMVision::processFrame(image_pixel *image)
00925 {
00926   int runs;
00927   int regions;
00928   int max_area;
00929 
00930   if(!image) return(false);
00931 
00932   if(options & CMV_THRESHOLD){
00933 
00934     classifyFrame(image,map);
00935     runs = encodeRuns(rmap,map);
00936     connectComponents(rmap,runs);
00937 
00938     regions = extractRegions(region_table,rmap,runs);
00939 
00940     if(options & CMV_COLOR_AVERAGES){
00941       calcAverageColors(region_table,regions,image,rmap,runs);
00942     }
00943 
00944     max_area = separateRegions(region_table,regions);
00945     sortRegions(max_area);
00946 
00947     if(options & CMV_DENSITY_MERGE){
00948       mergeRegions();
00949     }
00950   }
00951 
00952   return(true);
00953 }
00954 
00955 bool CMVision::processFrame(unsigned *map)
00956 {
00957   int runs;
00958   int regions;
00959   int max_area;
00960 
00961   if(!map) return(false);
00962 
00963   runs = encodeRuns(rmap,map);
00964   connectComponents(rmap,runs);
00965 
00966   regions = extractRegions(region_table,rmap,runs);
00967 
00968   // if(options & CMV_COLOR_AVERAGES){
00969   //   calcAverageColors(region_table,regions,image,rmap,runs);
00970   // }
00971 
00972   max_area = separateRegions(region_table,regions);
00973   sortRegions(max_area);
00974 
00975   if(options & CMV_DENSITY_MERGE){
00976     mergeRegions();
00977   }
00978 
00979   return(true);
00980 }
00981 
00982 int CMVision::numRegions(int color_id)
00983 {
00984   if(color_id<0 || color_id>=CMV_MAX_COLORS) return(CMV_NONE);
00985   return(region_count[color_id]);
00986 }
00987 
00988 CMVision::region *CMVision::getRegions(int color_id)
00989 {
00990   if(color_id<0 || color_id>=CMV_MAX_COLORS) return(NULL);
00991   return(region_list[color_id]);
00992 }


image_algos
Author(s): Dejan Pangercic
autogenerated on Mon Oct 6 2014 09:35:23