$search
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 = ®[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 }