00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
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
00046
00047
00048
00049
00050 inline int range_sum(int x,int w)
00051 {
00052 return(w*(2*x + w-1) / 2);
00053 }
00054
00055
00056 template <class num>
00057 inline num max(num a,num b)
00058 {
00059 return((a > b)? a : b);
00060 }
00061
00062
00063 template <class num>
00064 inline num min(num a,num b)
00065 {
00066 return((a < b)? a : b);
00067 }
00068
00069
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
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
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
00104
00105 void CMVision::classifyFrame(image_pixel * restrict img,unsigned * restrict map)
00106
00107
00108 {
00109 int i,m,s;
00110 int m1,m2;
00111 image_pixel p;
00112
00113 unsigned *uclas = u_class;
00114 unsigned *vclas = v_class;
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
00140
00141
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
00152 save = map[0];
00153
00154 j = 0;
00155 for(y=0; y<height; y++){
00156 row = &map[y * width];
00157
00158
00159
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
00168 l = x;
00169 while(row[x] == m) x++;
00170
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
00185
00186
00187
00188
00189
00190
00191
00192
00193
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
00204 while(x1 < width){
00205 x1 += map[l1++].length;
00206 }
00207 x1 = 0;
00208
00209
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
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
00227 if(n < p){
00228 map[p].parent = n;
00229 }else{
00230 map[n].parent = p;
00231 }
00232 }
00233 }
00234 }
00235
00236
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
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
00258 }
00259
00260 int CMVision::extractRegions(region * restrict reg,rle * restrict rmap,int num)
00261
00262
00263
00264
00265
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
00279 rmap[i].parent = b = n;
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
00290 n++;
00291 if(n >= CMV_MAX_REGIONS) return(CMV_MAX_REGIONS);
00292 }else{
00293
00294 b = rmap[r.parent].parent;
00295 rmap[i].parent = b;
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;
00300 reg[b].sum_x += range_sum(x,r.length);
00301 reg[b].sum_y += y * r.length;
00302 }
00303
00304
00305
00306
00307
00308 }
00309
00310
00311 x = (x + r.length) % width;
00312 y += (x == 0);
00313 }
00314
00315
00316
00317
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
00331
00332
00333
00334
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
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
00355
00356
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
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
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
00395 }
00396
00397
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
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414 x = xs + r.length;
00415 }
00416 }
00417
00418
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
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441 reg[i].average = avg;
00442 }
00443 }
00444
00445 int CMVision::separateRegions(region * restrict reg,int num)
00446
00447
00448
00449
00450 {
00451 region *p;
00452 int i,l;
00453 int area,max_area;
00454
00455
00456 for(i=0; i<CMV_MAX_COLORS; i++){
00457 region_count[i] = 0;
00458 region_list[i] = NULL;
00459 }
00460
00461
00462
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
00480
00481
00482
00483
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
00490
00491 {
00492 region *tbl[CMV_RADIX],*p,*pn;
00493 int slot,shift;
00494 int i,j;
00495
00496
00497 if(!list || !list->next) return(list);
00498
00499
00500 for(j=0; j<CMV_RADIX; j++) tbl[j] = NULL;
00501
00502 for(i=0; i<passes; i++){
00503
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
00515 list = NULL;
00516 for(j=0; j<CMV_RADIX; j++){
00517 p = tbl[j];
00518 tbl[j] = NULL;
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
00533
00534 {
00535 int i,p;
00536
00537
00538 p = top_bit((max_area + CMV_RBITS-1) / CMV_RBITS);
00539
00540
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
00548
00549
00550
00551
00552 {
00553 region *q,*s;
00554 int l,r,t,b;
00555 int a;
00556 int merged;
00557
00558
00559
00560 merged = 0;
00561
00562 while(p && merged<num){
00563 q = p->next;
00564 s = p;
00565
00566 while(q){
00567
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
00575 if((double)(p->area + q->area) / a > density_thresh){
00576
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
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
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
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
00638 {
00639 width = nwidth;
00640 height = nheight;
00641
00642
00643
00644 map = new unsigned[width * height + 1];
00645
00646
00647 options = CMV_THRESHOLD;
00648
00649 return(map != NULL);
00650 }
00651
00652
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
00683
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
00698 in = fopen(filename,"rt");
00699 if(!in) return(false);
00700
00701
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
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
00734
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
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
00778
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
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
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
00969
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 }