tesseract  3.03
/usr/local/google/home/jbreiden/tesseract-ocr-read-only/ccstruct/coutln.cpp
Go to the documentation of this file.
00001 /**********************************************************************
00002  * File:        coutln.c  (Formerly coutline.c)
00003  * Description: Code for the C_OUTLINE class.
00004  * Author:                  Ray Smith
00005  * Created:                 Mon Oct 07 16:01:57 BST 1991
00006  *
00007  * (C) Copyright 1991, Hewlett-Packard Ltd.
00008  ** Licensed under the Apache License, Version 2.0 (the "License");
00009  ** you may not use this file except in compliance with the License.
00010  ** You may obtain a copy of the License at
00011  ** http://www.apache.org/licenses/LICENSE-2.0
00012  ** Unless required by applicable law or agreed to in writing, software
00013  ** distributed under the License is distributed on an "AS IS" BASIS,
00014  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  ** See the License for the specific language governing permissions and
00016  ** limitations under the License.
00017  *
00018  **********************************************************************/
00019 
00020 #include <string.h>
00021 #ifdef __UNIX__
00022 #include <assert.h>
00023 #endif
00024 
00025 #include "coutln.h"
00026 
00027 #include "allheaders.h"
00028 #include "blobs.h"
00029 #include "normalis.h"
00030 
00031 // Include automatically generated configuration file if running autoconf.
00032 #ifdef HAVE_CONFIG_H
00033 #include "config_auto.h"
00034 #endif
00035 
00036 ELISTIZE (C_OUTLINE)
00037 ICOORD C_OUTLINE::step_coords[4] = {
00038   ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1)
00039 };
00040 
00041 /**********************************************************************
00042  * C_OUTLINE::C_OUTLINE
00043  *
00044  * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP.
00045  **********************************************************************/
00046 
00047 C_OUTLINE::C_OUTLINE (
00048 //constructor
00049 CRACKEDGE * startpt,             //outline to convert
00050 ICOORD bot_left,                 //bounding box
00051 ICOORD top_right, inT16 length   //length of loop
00052 ):box (bot_left, top_right), start (startpt->pos), offsets(NULL) {
00053   inT16 stepindex;               //index to step
00054   CRACKEDGE *edgept;             //current point
00055 
00056   stepcount = length;            //no of steps
00057   if (length == 0) {
00058     steps = NULL;
00059     return;
00060   }
00061                                  //get memory
00062   steps = (uinT8 *) alloc_mem (step_mem());
00063   memset(steps, 0, step_mem());
00064   edgept = startpt;
00065 
00066   for (stepindex = 0; stepindex < length; stepindex++) {
00067                                  //set compact step
00068     set_step (stepindex, edgept->stepdir);
00069     edgept = edgept->next;
00070   }
00071 }
00072 
00073 
00074 /**********************************************************************
00075  * C_OUTLINE::C_OUTLINE
00076  *
00077  * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG.
00078  **********************************************************************/
00079 C_OUTLINE::C_OUTLINE (
00080 //constructor
00081                                  //steps to copy
00082 ICOORD startpt, DIR128 * new_steps,
00083 inT16 length                     //length of loop
00084 ):start (startpt), offsets(NULL) {
00085   inT8 dirdiff;                  //direction difference
00086   DIR128 prevdir;                //previous direction
00087   DIR128 dir;                    //current direction
00088   DIR128 lastdir;                //dir of last step
00089   TBOX new_box;                   //easy bounding
00090   inT16 stepindex;               //index to step
00091   inT16 srcindex;                //source steps
00092   ICOORD pos;                    //current position
00093 
00094   pos = startpt;
00095   stepcount = length;            // No. of steps.
00096   ASSERT_HOST(length >= 0);
00097   steps = reinterpret_cast<uinT8*>(alloc_mem(step_mem()));  // Get memory.
00098   memset(steps, 0, step_mem());
00099 
00100   lastdir = new_steps[length - 1];
00101   prevdir = lastdir;
00102   for (stepindex = 0, srcindex = 0; srcindex < length;
00103   stepindex++, srcindex++) {
00104     new_box = TBOX (pos, pos);
00105     box += new_box;
00106                                  //copy steps
00107     dir = new_steps[srcindex];
00108     set_step(stepindex, dir);
00109     dirdiff = dir - prevdir;
00110     pos += step (stepindex);
00111     if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
00112       stepindex -= 2;            //cancel there-and-back
00113       prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
00114     }
00115     else
00116       prevdir = dir;
00117   }
00118   ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
00119   do {
00120     dirdiff = step_dir (stepindex - 1) - step_dir (0);
00121     if (dirdiff == 64 || dirdiff == -64) {
00122       start += step (0);
00123       stepindex -= 2;            //cancel there-and-back
00124       for (int i = 0; i < stepindex; ++i)
00125         set_step(i, step_dir(i + 1));
00126     }
00127   }
00128   while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
00129   stepcount = stepindex;
00130   ASSERT_HOST (stepcount >= 4);
00131 }
00132 
00133 /**********************************************************************
00134  * C_OUTLINE::C_OUTLINE
00135  *
00136  * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE.
00137  **********************************************************************/
00138 
00139 C_OUTLINE::C_OUTLINE(                     //constructor
00140                      C_OUTLINE *srcline,  //outline to
00141                      FCOORD rotation      //rotate
00142                     ) : offsets(NULL) {
00143   TBOX new_box;                   //easy bounding
00144   inT16 stepindex;               //index to step
00145   inT16 dirdiff;                 //direction change
00146   ICOORD pos;                    //current position
00147   ICOORD prevpos;                //previous dest point
00148 
00149   ICOORD destpos;                //destination point
00150   inT16 destindex;               //index to step
00151   DIR128 dir;                    //coded direction
00152   uinT8 new_step;
00153 
00154   stepcount = srcline->stepcount * 2;
00155   if (stepcount == 0) {
00156     steps = NULL;
00157     box = srcline->box;
00158     box.rotate(rotation);
00159     return;
00160   }
00161                                  //get memory
00162   steps = (uinT8 *) alloc_mem (step_mem());
00163   memset(steps, 0, step_mem());
00164 
00165   for (int iteration = 0; iteration < 2; ++iteration) {
00166     DIR128 round1 = iteration == 0 ? 32 : 0;
00167     DIR128 round2 = iteration != 0 ? 32 : 0;
00168     pos = srcline->start;
00169     prevpos = pos;
00170     prevpos.rotate (rotation);
00171     start = prevpos;
00172     box = TBOX (start, start);
00173     destindex = 0;
00174     for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) {
00175       pos += srcline->step (stepindex);
00176       destpos = pos;
00177       destpos.rotate (rotation);
00178       //  tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y());
00179       while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) {
00180         dir = DIR128 (FCOORD (destpos - prevpos));
00181         dir += 64;                 //turn to step style
00182         new_step = dir.get_dir ();
00183         //  tprintf(" %i\n", new_step);
00184         if (new_step & 31) {
00185           set_step(destindex++, dir + round1);
00186           prevpos += step(destindex - 1);
00187           if (destindex < 2
00188             || ((dirdiff =
00189             step_dir (destindex - 1) - step_dir (destindex - 2)) !=
00190             -64 && dirdiff != 64)) {
00191             set_step(destindex++, dir + round2);
00192             prevpos += step(destindex - 1);
00193           } else {
00194             prevpos -= step(destindex - 1);
00195             destindex--;
00196             prevpos -= step(destindex - 1);
00197             set_step(destindex - 1, dir + round2);
00198             prevpos += step(destindex - 1);
00199           }
00200         }
00201         else {
00202           set_step(destindex++, dir);
00203           prevpos += step(destindex - 1);
00204         }
00205         while (destindex >= 2 &&
00206                ((dirdiff =
00207                  step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 ||
00208                 dirdiff == 64)) {
00209           prevpos -= step(destindex - 1);
00210           prevpos -= step(destindex - 2);
00211           destindex -= 2;        // Forget u turn
00212         }
00213         //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y());
00214         new_box = TBOX (destpos, destpos);
00215         box += new_box;
00216       }
00217     }
00218     ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
00219     dirdiff = step_dir (destindex - 1) - step_dir (0);
00220     while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) {
00221       start += step (0);
00222       destindex -= 2;
00223       for (int i = 0; i < destindex; ++i)
00224         set_step(i, step_dir(i + 1));
00225       dirdiff = step_dir (destindex - 1) - step_dir (0);
00226     }
00227     if (destindex >= 4)
00228       break;
00229   }
00230   ASSERT_HOST(destindex <= stepcount);
00231   stepcount = destindex;
00232   destpos = start;
00233   for (stepindex = 0; stepindex < stepcount; stepindex++) {
00234     destpos += step (stepindex);
00235   }
00236   ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
00237 }
00238 
00239 // Build a fake outline, given just a bounding box and append to the list.
00240 void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) {
00241   C_OUTLINE_IT ol_it(outlines);
00242   // Make a C_OUTLINE from the bounds. This is a bit of a hack,
00243   // as there is no outline, just a bounding box, but it works nicely.
00244   CRACKEDGE start;
00245   start.pos = box.topleft();
00246   C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0);
00247   ol_it.add_to_end(outline);
00248 }
00249 
00250 /**********************************************************************
00251  * C_OUTLINE::area
00252  *
00253  * Compute the area of the outline.
00254  **********************************************************************/
00255 
00256 inT32 C_OUTLINE::area() const {
00257   int stepindex;                 //current step
00258   inT32 total_steps;             //steps to do
00259   inT32 total;                   //total area
00260   ICOORD pos;                    //position of point
00261   ICOORD next_step;              //step to next pix
00262   // We aren't going to modify the list, or its contents, but there is
00263   // no const iterator.
00264   C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children));
00265 
00266   pos = start_pos ();
00267   total_steps = pathlength ();
00268   total = 0;
00269   for (stepindex = 0; stepindex < total_steps; stepindex++) {
00270                                  //all intersected
00271     next_step = step (stepindex);
00272     if (next_step.x () < 0)
00273       total += pos.y ();
00274     else if (next_step.x () > 0)
00275       total -= pos.y ();
00276     pos += next_step;
00277   }
00278   for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
00279     total += it.data ()->area ();//add areas of children
00280 
00281   return total;
00282 }
00283 
00284 /**********************************************************************
00285  * C_OUTLINE::perimeter
00286  *
00287  * Compute the perimeter of the outline and its first level children.
00288  **********************************************************************/
00289 
00290 inT32 C_OUTLINE::perimeter() const {
00291   inT32 total_steps;             // Return value.
00292   // We aren't going to modify the list, or its contents, but there is
00293   // no const iterator.
00294   C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children));
00295 
00296   total_steps = pathlength();
00297   for (it.mark_cycle_pt(); !it.cycled_list(); it.forward())
00298     total_steps += it.data()->pathlength();  // Add perimeters of children.
00299 
00300   return total_steps;
00301 }
00302 
00303 
00304 /**********************************************************************
00305  * C_OUTLINE::outer_area
00306  *
00307  * Compute the area of the outline.
00308  **********************************************************************/
00309 
00310 inT32 C_OUTLINE::outer_area() const {
00311   int stepindex;                 //current step
00312   inT32 total_steps;             //steps to do
00313   inT32 total;                   //total area
00314   ICOORD pos;                    //position of point
00315   ICOORD next_step;              //step to next pix
00316 
00317   pos = start_pos ();
00318   total_steps = pathlength ();
00319   if (total_steps == 0)
00320     return box.area();
00321   total = 0;
00322   for (stepindex = 0; stepindex < total_steps; stepindex++) {
00323                                  //all intersected
00324     next_step = step (stepindex);
00325     if (next_step.x () < 0)
00326       total += pos.y ();
00327     else if (next_step.x () > 0)
00328       total -= pos.y ();
00329     pos += next_step;
00330   }
00331 
00332   return total;
00333 }
00334 
00335 
00336 /**********************************************************************
00337  * C_OUTLINE::count_transitions
00338  *
00339  * Compute the number of x and y maxes and mins in the outline.
00340  **********************************************************************/
00341 
00342 inT32 C_OUTLINE::count_transitions(                 //winding number
00343                                    inT32 threshold  //on size
00344                                   ) {
00345   BOOL8 first_was_max_x;         //what was first
00346   BOOL8 first_was_max_y;
00347   BOOL8 looking_for_max_x;       //what is next
00348   BOOL8 looking_for_min_x;
00349   BOOL8 looking_for_max_y;       //what is next
00350   BOOL8 looking_for_min_y;
00351   int stepindex;                 //current step
00352   inT32 total_steps;             //steps to do
00353                                  //current limits
00354   inT32 max_x, min_x, max_y, min_y;
00355   inT32 initial_x, initial_y;    //initial limits
00356   inT32 total;                   //total changes
00357   ICOORD pos;                    //position of point
00358   ICOORD next_step;              //step to next pix
00359 
00360   pos = start_pos ();
00361   total_steps = pathlength ();
00362   total = 0;
00363   max_x = min_x = pos.x ();
00364   max_y = min_y = pos.y ();
00365   looking_for_max_x = TRUE;
00366   looking_for_min_x = TRUE;
00367   looking_for_max_y = TRUE;
00368   looking_for_min_y = TRUE;
00369   first_was_max_x = FALSE;
00370   first_was_max_y = FALSE;
00371   initial_x = pos.x ();
00372   initial_y = pos.y ();          //stop uninit warning
00373   for (stepindex = 0; stepindex < total_steps; stepindex++) {
00374                                  //all intersected
00375     next_step = step (stepindex);
00376     pos += next_step;
00377     if (next_step.x () < 0) {
00378       if (looking_for_max_x && pos.x () < min_x)
00379         min_x = pos.x ();
00380       if (looking_for_min_x && max_x - pos.x () > threshold) {
00381         if (looking_for_max_x) {
00382           initial_x = max_x;
00383           first_was_max_x = FALSE;
00384         }
00385         total++;
00386         looking_for_max_x = TRUE;
00387         looking_for_min_x = FALSE;
00388         min_x = pos.x ();        //reset min
00389       }
00390     }
00391     else if (next_step.x () > 0) {
00392       if (looking_for_min_x && pos.x () > max_x)
00393         max_x = pos.x ();
00394       if (looking_for_max_x && pos.x () - min_x > threshold) {
00395         if (looking_for_min_x) {
00396           initial_x = min_x;     //remember first min
00397           first_was_max_x = TRUE;
00398         }
00399         total++;
00400         looking_for_max_x = FALSE;
00401         looking_for_min_x = TRUE;
00402         max_x = pos.x ();
00403       }
00404     }
00405     else if (next_step.y () < 0) {
00406       if (looking_for_max_y && pos.y () < min_y)
00407         min_y = pos.y ();
00408       if (looking_for_min_y && max_y - pos.y () > threshold) {
00409         if (looking_for_max_y) {
00410           initial_y = max_y;     //remember first max
00411           first_was_max_y = FALSE;
00412         }
00413         total++;
00414         looking_for_max_y = TRUE;
00415         looking_for_min_y = FALSE;
00416         min_y = pos.y ();        //reset min
00417       }
00418     }
00419     else {
00420       if (looking_for_min_y && pos.y () > max_y)
00421         max_y = pos.y ();
00422       if (looking_for_max_y && pos.y () - min_y > threshold) {
00423         if (looking_for_min_y) {
00424           initial_y = min_y;     //remember first min
00425           first_was_max_y = TRUE;
00426         }
00427         total++;
00428         looking_for_max_y = FALSE;
00429         looking_for_min_y = TRUE;
00430         max_y = pos.y ();
00431       }
00432     }
00433 
00434   }
00435   if (first_was_max_x && looking_for_min_x) {
00436     if (max_x - initial_x > threshold)
00437       total++;
00438     else
00439       total--;
00440   }
00441   else if (!first_was_max_x && looking_for_max_x) {
00442     if (initial_x - min_x > threshold)
00443       total++;
00444     else
00445       total--;
00446   }
00447   if (first_was_max_y && looking_for_min_y) {
00448     if (max_y - initial_y > threshold)
00449       total++;
00450     else
00451       total--;
00452   }
00453   else if (!first_was_max_y && looking_for_max_y) {
00454     if (initial_y - min_y > threshold)
00455       total++;
00456     else
00457       total--;
00458   }
00459 
00460   return total;
00461 }
00462 
00463 
00464 /**********************************************************************
00465  * C_OUTLINE::operator<
00466  *
00467  * Return TRUE if the left operand is inside the right one.
00468  **********************************************************************/
00469 
00470 BOOL8
00471 C_OUTLINE::operator< (           //winding number
00472 const C_OUTLINE & other          //other outline
00473 ) const
00474 {
00475   inT16 count = 0;               //winding count
00476   ICOORD pos;                    //position of point
00477   inT32 stepindex;               //index to cstep
00478 
00479   if (!box.overlap (other.box))
00480     return FALSE;                //can't be contained
00481   if (stepcount == 0)
00482     return other.box.contains(this->box);
00483 
00484   pos = start;
00485   for (stepindex = 0; stepindex < stepcount
00486     && (count = other.winding_number (pos)) == INTERSECTING; stepindex++)
00487     pos += step (stepindex);     //try all points
00488   if (count == INTERSECTING) {
00489                                  //all intersected
00490     pos = other.start;
00491     for (stepindex = 0; stepindex < other.stepcount
00492       && (count = winding_number (pos)) == INTERSECTING; stepindex++)
00493                                  //try other way round
00494       pos += other.step (stepindex);
00495     return count == INTERSECTING || count == 0;
00496   }
00497   return count != 0;
00498 }
00499 
00500 
00501 /**********************************************************************
00502  * C_OUTLINE::winding_number
00503  *
00504  * Return the winding number of the outline around the given point.
00505  **********************************************************************/
00506 
00507 inT16 C_OUTLINE::winding_number(              //winding number
00508                                 ICOORD point  //point to wind around
00509                                ) const {
00510   inT16 stepindex;               //index to cstep
00511   inT16 count;                   //winding count
00512   ICOORD vec;                    //to current point
00513   ICOORD stepvec;                //step vector
00514   inT32 cross;                   //cross product
00515 
00516   vec = start - point;           //vector to it
00517   count = 0;
00518   for (stepindex = 0; stepindex < stepcount; stepindex++) {
00519     stepvec = step (stepindex);  //get the step
00520                                  //crossing the line
00521     if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) {
00522       cross = vec * stepvec;     //cross product
00523       if (cross > 0)
00524         count++;                 //crossing right half
00525       else if (cross == 0)
00526         return INTERSECTING;     //going through point
00527     }
00528     else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) {
00529       cross = vec * stepvec;
00530       if (cross < 0)
00531         count--;                 //crossing back
00532       else if (cross == 0)
00533         return INTERSECTING;     //illegal
00534     }
00535     vec += stepvec;              //sum vectors
00536   }
00537   return count;                  //winding number
00538 }
00539 
00540 
00541 /**********************************************************************
00542  * C_OUTLINE::turn_direction
00543  *
00544  * Return the sum direction delta of the outline.
00545  **********************************************************************/
00546 
00547 inT16 C_OUTLINE::turn_direction() const {  //winding number
00548   DIR128 prevdir;                //previous direction
00549   DIR128 dir;                    //current direction
00550   inT16 stepindex;               //index to cstep
00551   inT8 dirdiff;                  //direction difference
00552   inT16 count;                   //winding count
00553 
00554   if (stepcount == 0)
00555     return 128;
00556   count = 0;
00557   prevdir = step_dir (stepcount - 1);
00558   for (stepindex = 0; stepindex < stepcount; stepindex++) {
00559     dir = step_dir (stepindex);
00560     dirdiff = dir - prevdir;
00561     ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
00562     count += dirdiff;
00563     prevdir = dir;
00564   }
00565   ASSERT_HOST (count == 128 || count == -128);
00566   return count;                  //winding number
00567 }
00568 
00569 
00570 /**********************************************************************
00571  * C_OUTLINE::reverse
00572  *
00573  * Reverse the direction of an outline.
00574  **********************************************************************/
00575 
00576 void C_OUTLINE::reverse() {  //reverse drection
00577   DIR128 halfturn = MODULUS / 2; //amount to shift
00578   DIR128 stepdir;                //direction of step
00579   inT16 stepindex;               //index to cstep
00580   inT16 farindex;                //index to other side
00581   inT16 halfsteps;               //half of stepcount
00582 
00583   halfsteps = (stepcount + 1) / 2;
00584   for (stepindex = 0; stepindex < halfsteps; stepindex++) {
00585     farindex = stepcount - stepindex - 1;
00586     stepdir = step_dir (stepindex);
00587     set_step (stepindex, step_dir (farindex) + halfturn);
00588     set_step (farindex, stepdir + halfturn);
00589   }
00590 }
00591 
00592 
00593 /**********************************************************************
00594  * C_OUTLINE::move
00595  *
00596  * Move C_OUTLINE by vector
00597  **********************************************************************/
00598 
00599 void C_OUTLINE::move(                  // reposition OUTLINE
00600                      const ICOORD vec  // by vector
00601                     ) {
00602   C_OUTLINE_IT it(&children);  // iterator
00603 
00604   box.move (vec);
00605   start += vec;
00606 
00607   for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
00608     it.data ()->move (vec);      // move child outlines
00609 }
00610 
00611 // Returns true if *this and its children are legally nested.
00612 // The outer area of a child should have the opposite sign to the
00613 // parent. If not, it means we have discarded an outline in between
00614 // (probably due to excessive length).
00615 bool C_OUTLINE::IsLegallyNested() const {
00616   if (stepcount == 0) return true;
00617   int parent_area = outer_area();
00618   // We aren't going to modify the list, or its contents, but there is
00619   // no const iterator.
00620   C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST*>(&children));
00621   for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) {
00622     const C_OUTLINE* child = child_it.data();
00623     if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested())
00624       return false;
00625   }
00626   return true;
00627 }
00628 
00629 // If this outline is smaller than the given min_size, delete this and
00630 // remove from its list, via *it, after checking that *it points to this.
00631 // Otherwise, if any children of this are too small, delete them.
00632 // On entry, *it must be an iterator pointing to this. If this gets deleted
00633 // then this is extracted from *it, so an iteration can continue.
00634 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) {
00635   if (box.width() < min_size || box.height() < min_size) {
00636     ASSERT_HOST(this == it->data());
00637     delete it->extract();  // Too small so get rid of it and any children.
00638   } else if (!children.empty()) {
00639     // Search the children of this, deleting any that are too small.
00640     C_OUTLINE_IT child_it(&children);
00641     for (child_it.mark_cycle_pt(); !child_it.cycled_list();
00642          child_it.forward()) {
00643       C_OUTLINE* child = child_it.data();
00644       child->RemoveSmallRecursive(min_size, &child_it);
00645     }
00646   }
00647 }
00648 
00649 // Factored out helpers below are used only by ComputeEdgeOffsets to operate
00650 // on data from an 8-bit Pix, and assume that any input x and/or y are already
00651 // constrained to be legal Pix coordinates.
00652 
00653 // Helper computes the local 2-D gradient (dx, dy) from the 2x2 cell centered
00654 // on the given (x,y). If the cell would go outside the image, it is padded
00655 // with white.
00656 static void ComputeGradient(const l_uint32* data, int wpl,
00657                             int x, int y, int width, int height,
00658                             ICOORD* gradient) {
00659   const l_uint32* line = data + y * wpl;
00660   int pix_x_y = x < width && y < height ?
00661       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x) : 255;
00662   int pix_x_prevy = x < width && y > 0 ?
00663       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line - wpl)), x) : 255;
00664   int pix_prevx_prevy = x > 0 && y > 0 ?
00665       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<void const*>(line - wpl)), x - 1) : 255;
00666   int pix_prevx_y = x > 0 && y < height ?
00667       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x - 1) : 255;
00668   gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy));
00669   gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y));
00670 }
00671 
00672 // Helper evaluates a vertical difference, (x,y) - (x,y-1), returning true if
00673 // the difference, matches diff_sign and updating the best_diff, best_sum,
00674 // best_y if a new max.
00675 static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign,
00676                                  int x, int y, int height,
00677                                  int* best_diff, int* best_sum, int* best_y) {
00678   if (y <= 0 || y >= height)
00679     return false;
00680   const l_uint32* line = data + y * wpl;
00681   int pixel1 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line - wpl)), x);
00682   int pixel2 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x);
00683   int diff = (pixel2 - pixel1) * diff_sign;
00684   if (diff > *best_diff) {
00685     *best_diff = diff;
00686     *best_sum = pixel1 + pixel2;
00687     *best_y = y;
00688   }
00689   return diff > 0;
00690 }
00691 
00692 // Helper evaluates a horizontal difference, (x,y) - (x-1,y), where y is implied
00693 // by the input image line, returning true if the difference matches diff_sign
00694 // and updating the best_diff, best_sum, best_x if a new max.
00695 static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign,
00696                                    int x, int width,
00697                                    int* best_diff, int* best_sum, int* best_x) {
00698   if (x <= 0 || x >= width)
00699     return false;
00700   int pixel1 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x - 1);
00701   int pixel2 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x);
00702   int diff = (pixel2 - pixel1) * diff_sign;
00703   if (diff > *best_diff) {
00704     *best_diff = diff;
00705     *best_sum = pixel1 + pixel2;
00706     *best_x = x;
00707   }
00708   return diff > 0;
00709 }
00710 
00711 // Adds sub-pixel resolution EdgeOffsets for the outline if the supplied
00712 // pix is 8-bit. Does nothing otherwise.
00713 // Operation: Consider the following near-horizontal line:
00714 // _________
00715 //          |________
00716 //                   |________
00717 // At *every* position along this line, the gradient direction will be close
00718 // to vertical. Extrapoaltion/interpolation of the position of the threshold
00719 // that was used to binarize the image gives a more precise vertical position
00720 // for each horizontal step, and the conflict in step direction and gradient
00721 // direction can be used to ignore the vertical steps.
00722 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) {
00723   if (pixGetDepth(pix) != 8) return;
00724   const l_uint32* data = pixGetData(pix);
00725   int wpl = pixGetWpl(pix);
00726   int width = pixGetWidth(pix);
00727   int height = pixGetHeight(pix);
00728   bool negative = flag(COUT_INVERSE);
00729   delete [] offsets;
00730   offsets = new EdgeOffset[stepcount];
00731   ICOORD pos = start;
00732   ICOORD prev_gradient;
00733   ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
00734                   &prev_gradient);
00735   for (int s = 0; s < stepcount; ++s) {
00736     ICOORD step_vec = step(s);
00737     TPOINT pt1(pos);
00738     pos += step_vec;
00739     TPOINT pt2(pos);
00740     ICOORD next_gradient;
00741     ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
00742                     &next_gradient);
00743     // Use the sum of the prev and next as the working gradient.
00744     ICOORD gradient = prev_gradient + next_gradient;
00745     // best_diff will be manipulated to be always positive.
00746     int best_diff = 0;
00747     // offset will be the extrapolation of the location of the greyscale
00748     // threshold from the edge with the largest difference, relative to the
00749     // location of the binary edge.
00750     int offset = 0;
00751     if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) {
00752       // Horizontal step. diff_sign == 1 indicates black above.
00753       int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1;
00754       int x = MIN(pt1.x, pt2.x);
00755       int y = height - pt1.y;
00756       int best_sum = 0;
00757       int best_y = y;
00758       EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height,
00759                            &best_diff, &best_sum, &best_y);
00760       // Find the strongest edge.
00761       int test_y = y;
00762       do {
00763         ++test_y;
00764       } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
00765                                     &best_diff, &best_sum, &best_y));
00766       test_y = y;
00767       do {
00768         --test_y;
00769       } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
00770                                     &best_diff, &best_sum, &best_y));
00771       offset = diff_sign * (best_sum / 2 - threshold) +
00772           (y - best_y) * best_diff;
00773     } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) {
00774       // Vertical step. diff_sign == 1 indicates black on the left.
00775       int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1;
00776       int x = pt1.x;
00777       int y = height - MAX(pt1.y, pt2.y);
00778       const l_uint32* line = pixGetData(pix) + y * wpl;
00779       int best_sum = 0;
00780       int best_x = x;
00781       EvaluateHorizontalDiff(line, diff_sign, x, width,
00782                              &best_diff, &best_sum, &best_x);
00783       // Find the strongest edge.
00784       int test_x = x;
00785       do {
00786         ++test_x;
00787       } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
00788                                       &best_diff, &best_sum, &best_x));
00789       test_x = x;
00790       do {
00791         --test_x;
00792       } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
00793                                       &best_diff, &best_sum, &best_x));
00794       offset = diff_sign * (threshold - best_sum / 2) +
00795           (best_x - x) * best_diff;
00796     }
00797     offsets[s].offset_numerator =
00798         static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
00799     offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
00800                                                            MAX_UINT8));
00801     if (negative) gradient = -gradient;
00802     // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2)
00803     // to convert from gradient direction to edge direction.
00804     offsets[s].direction =
00805         Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256);
00806     prev_gradient = next_gradient;
00807   }
00808 }
00809 
00810 // Adds sub-pixel resolution EdgeOffsets for the outline using only
00811 // a binary image source.
00812 // Runs a sliding window of 5 edge steps over the outline, maintaining a count
00813 // of the number of steps in each of the 4 directions in the window, and a
00814 // sum of the x or y position of each step (as appropriate to its direction.)
00815 // Ignores single-count steps EXCEPT the sharp U-turn and smoothes out the
00816 // perpendicular direction. Eg
00817 // ___              ___       Chain code from the left:
00818 //    |___    ___   ___|      222122212223221232223000
00819 //        |___|  |_|          Corresponding counts of each direction:
00820 //                          0   00000000000000000123
00821 //                          1   11121111001111100000
00822 //                          2   44434443443333343321
00823 //                          3   00000001111111112111
00824 // Count of direction at center 41434143413313143313
00825 // Step gets used?              YNYYYNYYYNYYNYNYYYyY (y= U-turn exception)
00826 // Path redrawn showing only the used points:
00827 // ___              ___
00828 //     ___    ___   ___|
00829 //         ___    _
00830 // Sub-pixel edge position cannot be shown well with ASCII-art, but each
00831 // horizontal step's y position is the mean of the y positions of the steps
00832 // in the same direction in the sliding window, which makes a much smoother
00833 // outline, without losing important detail.
00834 void C_OUTLINE::ComputeBinaryOffsets() {
00835   delete [] offsets;
00836   offsets = new EdgeOffset[stepcount];
00837   // Count of the number of steps in each direction in the sliding window.
00838   int dir_counts[4];
00839   // Sum of the positions (y for a horizontal step, x for vertical) in each
00840   // direction in the sliding window.
00841   int pos_totals[4];
00842   memset(dir_counts, 0, sizeof(dir_counts));
00843   memset(pos_totals, 0, sizeof(pos_totals));
00844   ICOORD pos = start;
00845   ICOORD tail_pos = pos;
00846   // tail_pos is the trailing position, with the next point to be lost from
00847   // the window.
00848   tail_pos -= step(stepcount - 1);
00849   tail_pos -= step(stepcount - 2);
00850   // head_pos is the leading position, with the next point to be added to the
00851   // window.
00852   ICOORD head_pos = tail_pos;
00853   // Set up the initial window with 4 points in [-2, 2)
00854   for (int s = -2; s < 2; ++s) {
00855     increment_step(s, 1, &head_pos, dir_counts, pos_totals);
00856   }
00857   for (int s = 0; s < stepcount; pos += step(s++)) {
00858     // At step s, s in in the middle of [s-2, s+2].
00859     increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals);
00860     int dir_index = chain_code(s);
00861     ICOORD step_vec = step(s);
00862     int best_diff = 0;
00863     int offset = 0;
00864     // Use only steps that have a count of >=2 OR the strong U-turn with a
00865     // single d and 2 at d-1 and 2 at d+1 (mod 4).
00866     if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 &&
00867         dir_counts[Modulo(dir_index - 1, 4)] == 2 &&
00868         dir_counts[Modulo(dir_index + 1, 4)] == 2)) {
00869       // Valid step direction.
00870       best_diff = dir_counts[dir_index];
00871       int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y();
00872       // The offset proposes that the actual step should be positioned at
00873       // the mean position of the steps in the window of the same direction.
00874       // See ASCII art above.
00875       offset = pos_totals[dir_index] - best_diff * edge_pos;
00876     }
00877     offsets[s].offset_numerator =
00878         static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
00879     offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
00880                                                            MAX_UINT8));
00881     // The direction is just the vector from start to end of the window.
00882     FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y());
00883     offsets[s].direction = direction.to_direction();
00884     increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals);
00885   }
00886 }
00887 
00888 // Renders the outline to the given pix, with left and top being
00889 // the coords of the upper-left corner of the pix.
00890 void C_OUTLINE::render(int left, int top, Pix* pix) const {
00891   ICOORD pos = start;
00892   for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
00893     ICOORD next_step = step(stepindex);
00894     if (next_step.y() < 0) {
00895       pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1,
00896                   PIX_NOT(PIX_DST), NULL, 0, 0);
00897     } else if (next_step.y() > 0) {
00898       pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1,
00899                   PIX_NOT(PIX_DST), NULL, 0, 0);
00900     }
00901     pos += next_step;
00902   }
00903 }
00904 
00905 // Renders just the outline to the given pix (no fill), with left and top
00906 // being the coords of the upper-left corner of the pix.
00907 void C_OUTLINE::render_outline(int left, int top, Pix* pix) const {
00908   ICOORD pos = start;
00909   for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
00910     ICOORD next_step = step(stepindex);
00911     if (next_step.y() < 0) {
00912       pixSetPixel(pix, pos.x() - left, top - pos.y(), 1);
00913     } else if (next_step.y() > 0) {
00914       pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1);
00915     } else if (next_step.x() < 0) {
00916       pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1);
00917     } else if (next_step.x() > 0) {
00918       pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1);
00919     }
00920     pos += next_step;
00921   }
00922 }
00923 
00924 /**********************************************************************
00925  * C_OUTLINE::plot
00926  *
00927  * Draw the outline in the given colour.
00928  **********************************************************************/
00929 
00930 #ifndef GRAPHICS_DISABLED
00931 void C_OUTLINE::plot(                //draw it
00932                      ScrollView* window,       // window to draw in
00933                      ScrollView::Color colour  // colour to draw in
00934                     ) const {
00935   inT16 stepindex;               // index to cstep
00936   ICOORD pos;                    // current position
00937   DIR128 stepdir;                // direction of step
00938 
00939   pos = start;                   // current position
00940   window->Pen(colour);
00941   if (stepcount == 0) {
00942     window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
00943     return;
00944   }
00945   window->SetCursor(pos.x(), pos.y());
00946 
00947   stepindex = 0;
00948   while (stepindex < stepcount) {
00949     pos += step(stepindex);    // step to next
00950     stepdir = step_dir(stepindex);
00951     stepindex++;               // count steps
00952     // merge straight lines
00953     while (stepindex < stepcount &&
00954            stepdir.get_dir() == step_dir(stepindex).get_dir()) {
00955       pos += step(stepindex);
00956       stepindex++;
00957     }
00958     window->DrawTo(pos.x(), pos.y());
00959   }
00960 }
00961 // Draws the outline in the given colour, normalized using the given denorm,
00962 // making use of sub-pixel accurate information if available.
00963 void C_OUTLINE::plot_normed(const DENORM& denorm, ScrollView::Color colour,
00964                             ScrollView* window) const {
00965   window->Pen(colour);
00966   if (stepcount == 0) {
00967     window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
00968     return;
00969   }
00970   const DENORM* root_denorm = denorm.RootDenorm();
00971   ICOORD pos = start;                   // current position
00972   FCOORD f_pos = sub_pixel_pos_at_index(pos, 0);
00973   FCOORD pos_normed;
00974   denorm.NormTransform(root_denorm, f_pos, &pos_normed);
00975   window->SetCursor(IntCastRounded(pos_normed.x()),
00976                     IntCastRounded(pos_normed.y()));
00977   for (int s = 0; s < stepcount; pos += step(s++)) {
00978     int edge_weight = edge_strength_at_index(s);
00979     if (edge_weight == 0) {
00980       // This point has conflicting gradient and step direction, so ignore it.
00981       continue;
00982     }
00983     FCOORD f_pos = sub_pixel_pos_at_index(pos, s);
00984     FCOORD pos_normed;
00985     denorm.NormTransform(root_denorm, f_pos, &pos_normed);
00986     window->DrawTo(IntCastRounded(pos_normed.x()),
00987                    IntCastRounded(pos_normed.y()));
00988   }
00989 }
00990 #endif
00991 
00992 
00993 /**********************************************************************
00994  * C_OUTLINE::operator=
00995  *
00996  * Assignment - deep copy data
00997  **********************************************************************/
00998 
00999                                  //assignment
01000 C_OUTLINE & C_OUTLINE::operator= (
01001 const C_OUTLINE & source         //from this
01002 ) {
01003   box = source.box;
01004   start = source.start;
01005   if (steps != NULL)
01006     free_mem(steps);
01007   stepcount = source.stepcount;
01008   steps = (uinT8 *) alloc_mem (step_mem());
01009   memmove (steps, source.steps, step_mem());
01010   if (!children.empty ())
01011     children.clear ();
01012   children.deep_copy(&source.children, &deep_copy);
01013   delete [] offsets;
01014   if (source.offsets != NULL) {
01015     offsets = new EdgeOffset[stepcount];
01016     memcpy(offsets, source.offsets, stepcount * sizeof(*offsets));
01017   } else {
01018     offsets = NULL;
01019   }
01020   return *this;
01021 }
01022 
01023 // Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals
01024 // by the step, increment, and vertical step ? x : y position * increment
01025 // at step s Mod stepcount respectively. Used to add or subtract the
01026 // direction and position to/from accumulators of a small neighbourhood.
01027 void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos,
01028                                int* dir_counts, int* pos_totals) const {
01029   int step_index = Modulo(s, stepcount);
01030   int dir_index = chain_code(step_index);
01031   dir_counts[dir_index] += increment;
01032   ICOORD step_vec = step(step_index);
01033   if (step_vec.x() == 0)
01034     pos_totals[dir_index] += pos->x() * increment;
01035   else
01036     pos_totals[dir_index] += pos->y() * increment;
01037   *pos += step_vec;
01038 }
01039 
01040 ICOORD C_OUTLINE::chain_step(int chaindir) {
01041   return step_coords[chaindir % 4];
01042 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines