tesseract 3.04.01

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 
00051 C_OUTLINE::C_OUTLINE (CRACKEDGE * startpt, ICOORD bot_left, 
00052                       ICOORD top_right, inT16 length)
00053     : box (bot_left, top_right), start (startpt->pos), offsets(NULL) {
00054   inT16 stepindex;               //index to step
00055   CRACKEDGE *edgept;             //current point
00056 
00057   stepcount = length;            //no of steps
00058   if (length == 0) {
00059     steps = NULL;
00060     return;
00061   }
00062                                  //get memory
00063   steps = (uinT8 *) alloc_mem (step_mem());
00064   memset(steps, 0, step_mem());
00065   edgept = startpt;
00066 
00067   for (stepindex = 0; stepindex < length; stepindex++) {
00068                                  //set compact step
00069     set_step (stepindex, edgept->stepdir);
00070     edgept = edgept->next;
00071   }
00072 }
00073 
00074 
00080 C_OUTLINE::C_OUTLINE (
00081 //constructor
00082                                  //steps to copy
00083 ICOORD startpt, DIR128 * new_steps,
00084 inT16 length                     //length of loop
00085 ):start (startpt), offsets(NULL) {
00086   inT8 dirdiff;                  //direction difference
00087   DIR128 prevdir;                //previous direction
00088   DIR128 dir;                    //current direction
00089   DIR128 lastdir;                //dir of last step
00090   TBOX new_box;                   //easy bounding
00091   inT16 stepindex;               //index to step
00092   inT16 srcindex;                //source steps
00093   ICOORD pos;                    //current position
00094 
00095   pos = startpt;
00096   stepcount = length;            // No. of steps.
00097   ASSERT_HOST(length >= 0);
00098   steps = reinterpret_cast<uinT8*>(alloc_mem(step_mem()));  // Get memory.
00099   memset(steps, 0, step_mem());
00100 
00101   lastdir = new_steps[length - 1];
00102   prevdir = lastdir;
00103   for (stepindex = 0, srcindex = 0; srcindex < length;
00104   stepindex++, srcindex++) {
00105     new_box = TBOX (pos, pos);
00106     box += new_box;
00107                                  //copy steps
00108     dir = new_steps[srcindex];
00109     set_step(stepindex, dir);
00110     dirdiff = dir - prevdir;
00111     pos += step (stepindex);
00112     if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
00113       stepindex -= 2;            //cancel there-and-back
00114       prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
00115     }
00116     else
00117       prevdir = dir;
00118   }
00119   ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
00120   do {
00121     dirdiff = step_dir (stepindex - 1) - step_dir (0);
00122     if (dirdiff == 64 || dirdiff == -64) {
00123       start += step (0);
00124       stepindex -= 2;            //cancel there-and-back
00125       for (int i = 0; i < stepindex; ++i)
00126         set_step(i, step_dir(i + 1));
00127     }
00128   }
00129   while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
00130   stepcount = stepindex;
00131   ASSERT_HOST (stepcount >= 4);
00132 }
00133 
00142 C_OUTLINE::C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation) : 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 
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 
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 
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 
00343 inT32 C_OUTLINE::count_transitions(inT32 threshold) {
00344   BOOL8 first_was_max_x;         //what was first
00345   BOOL8 first_was_max_y;
00346   BOOL8 looking_for_max_x;       //what is next
00347   BOOL8 looking_for_min_x;
00348   BOOL8 looking_for_max_y;       //what is next
00349   BOOL8 looking_for_min_y;
00350   int stepindex;                 //current step
00351   inT32 total_steps;             //steps to do
00352                                  //current limits
00353   inT32 max_x, min_x, max_y, min_y;
00354   inT32 initial_x, initial_y;    //initial limits
00355   inT32 total;                   //total changes
00356   ICOORD pos;                    //position of point
00357   ICOORD next_step;              //step to next pix
00358 
00359   pos = start_pos ();
00360   total_steps = pathlength ();
00361   total = 0;
00362   max_x = min_x = pos.x ();
00363   max_y = min_y = pos.y ();
00364   looking_for_max_x = TRUE;
00365   looking_for_min_x = TRUE;
00366   looking_for_max_y = TRUE;
00367   looking_for_min_y = TRUE;
00368   first_was_max_x = FALSE;
00369   first_was_max_y = FALSE;
00370   initial_x = pos.x ();
00371   initial_y = pos.y ();          //stop uninit warning
00372   for (stepindex = 0; stepindex < total_steps; stepindex++) {
00373                                  //all intersected
00374     next_step = step (stepindex);
00375     pos += next_step;
00376     if (next_step.x () < 0) {
00377       if (looking_for_max_x && pos.x () < min_x)
00378         min_x = pos.x ();
00379       if (looking_for_min_x && max_x - pos.x () > threshold) {
00380         if (looking_for_max_x) {
00381           initial_x = max_x;
00382           first_was_max_x = FALSE;
00383         }
00384         total++;
00385         looking_for_max_x = TRUE;
00386         looking_for_min_x = FALSE;
00387         min_x = pos.x ();        //reset min
00388       }
00389     }
00390     else if (next_step.x () > 0) {
00391       if (looking_for_min_x && pos.x () > max_x)
00392         max_x = pos.x ();
00393       if (looking_for_max_x && pos.x () - min_x > threshold) {
00394         if (looking_for_min_x) {
00395           initial_x = min_x;     //remember first min
00396           first_was_max_x = TRUE;
00397         }
00398         total++;
00399         looking_for_max_x = FALSE;
00400         looking_for_min_x = TRUE;
00401         max_x = pos.x ();
00402       }
00403     }
00404     else if (next_step.y () < 0) {
00405       if (looking_for_max_y && pos.y () < min_y)
00406         min_y = pos.y ();
00407       if (looking_for_min_y && max_y - pos.y () > threshold) {
00408         if (looking_for_max_y) {
00409           initial_y = max_y;     //remember first max
00410           first_was_max_y = FALSE;
00411         }
00412         total++;
00413         looking_for_max_y = TRUE;
00414         looking_for_min_y = FALSE;
00415         min_y = pos.y ();        //reset min
00416       }
00417     }
00418     else {
00419       if (looking_for_min_y && pos.y () > max_y)
00420         max_y = pos.y ();
00421       if (looking_for_max_y && pos.y () - min_y > threshold) {
00422         if (looking_for_min_y) {
00423           initial_y = min_y;     //remember first min
00424           first_was_max_y = TRUE;
00425         }
00426         total++;
00427         looking_for_max_y = FALSE;
00428         looking_for_min_y = TRUE;
00429         max_y = pos.y ();
00430       }
00431     }
00432 
00433   }
00434   if (first_was_max_x && looking_for_min_x) {
00435     if (max_x - initial_x > threshold)
00436       total++;
00437     else
00438       total--;
00439   }
00440   else if (!first_was_max_x && looking_for_max_x) {
00441     if (initial_x - min_x > threshold)
00442       total++;
00443     else
00444       total--;
00445   }
00446   if (first_was_max_y && looking_for_min_y) {
00447     if (max_y - initial_y > threshold)
00448       total++;
00449     else
00450       total--;
00451   }
00452   else if (!first_was_max_y && looking_for_max_y) {
00453     if (initial_y - min_y > threshold)
00454       total++;
00455     else
00456       total--;
00457   }
00458 
00459   return total;
00460 }
00461 
00462 
00470 BOOL8
00471 C_OUTLINE::operator< (const C_OUTLINE & other) const
00472 {
00473   inT16 count = 0;               //winding count
00474   ICOORD pos;                    //position of point
00475   inT32 stepindex;               //index to cstep
00476 
00477   if (!box.overlap (other.box))
00478     return FALSE;                //can't be contained
00479   if (stepcount == 0)
00480     return other.box.contains(this->box);
00481 
00482   pos = start;
00483   for (stepindex = 0; stepindex < stepcount
00484     && (count = other.winding_number (pos)) == INTERSECTING; stepindex++)
00485     pos += step (stepindex);     //try all points
00486   if (count == INTERSECTING) {
00487                                  //all intersected
00488     pos = other.start;
00489     for (stepindex = 0; stepindex < other.stepcount
00490       && (count = winding_number (pos)) == INTERSECTING; stepindex++)
00491                                  //try other way round
00492       pos += other.step (stepindex);
00493     return count == INTERSECTING || count == 0;
00494   }
00495   return count != 0;
00496 }
00497 
00498 
00506 inT16 C_OUTLINE::winding_number(ICOORD point) const {
00507   inT16 stepindex;               //index to cstep
00508   inT16 count;                   //winding count
00509   ICOORD vec;                    //to current point
00510   ICOORD stepvec;                //step vector
00511   inT32 cross;                   //cross product
00512 
00513   vec = start - point;           //vector to it
00514   count = 0;
00515   for (stepindex = 0; stepindex < stepcount; stepindex++) {
00516     stepvec = step (stepindex);  //get the step
00517                                  //crossing the line
00518     if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) {
00519       cross = vec * stepvec;     //cross product
00520       if (cross > 0)
00521         count++;                 //crossing right half
00522       else if (cross == 0)
00523         return INTERSECTING;     //going through point
00524     }
00525     else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) {
00526       cross = vec * stepvec;
00527       if (cross < 0)
00528         count--;                 //crossing back
00529       else if (cross == 0)
00530         return INTERSECTING;     //illegal
00531     }
00532     vec += stepvec;              //sum vectors
00533   }
00534   return count;                  //winding number
00535 }
00536 
00537 
00544 inT16 C_OUTLINE::turn_direction() const {  //winding number
00545   DIR128 prevdir;                //previous direction
00546   DIR128 dir;                    //current direction
00547   inT16 stepindex;               //index to cstep
00548   inT8 dirdiff;                  //direction difference
00549   inT16 count;                   //winding count
00550 
00551   if (stepcount == 0)
00552     return 128;
00553   count = 0;
00554   prevdir = step_dir (stepcount - 1);
00555   for (stepindex = 0; stepindex < stepcount; stepindex++) {
00556     dir = step_dir (stepindex);
00557     dirdiff = dir - prevdir;
00558     ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
00559     count += dirdiff;
00560     prevdir = dir;
00561   }
00562   ASSERT_HOST (count == 128 || count == -128);
00563   return count;                  //winding number
00564 }
00565 
00566 
00573 void C_OUTLINE::reverse() {  //reverse drection
00574   DIR128 halfturn = MODULUS / 2; //amount to shift
00575   DIR128 stepdir;                //direction of step
00576   inT16 stepindex;               //index to cstep
00577   inT16 farindex;                //index to other side
00578   inT16 halfsteps;               //half of stepcount
00579 
00580   halfsteps = (stepcount + 1) / 2;
00581   for (stepindex = 0; stepindex < halfsteps; stepindex++) {
00582     farindex = stepcount - stepindex - 1;
00583     stepdir = step_dir (stepindex);
00584     set_step (stepindex, step_dir (farindex) + halfturn);
00585     set_step (farindex, stepdir + halfturn);
00586   }
00587 }
00588 
00589 
00597 void C_OUTLINE::move(const ICOORD vec) {
00598   C_OUTLINE_IT it(&children);  // iterator
00599 
00600   box.move (vec);
00601   start += vec;
00602 
00603   for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
00604     it.data ()->move (vec);      // move child outlines
00605 }
00606 
00613 bool C_OUTLINE::IsLegallyNested() const {
00614   if (stepcount == 0) return true;
00615   int parent_area = outer_area();
00616   // We aren't going to modify the list, or its contents, but there is
00617   // no const iterator.
00618   C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST*>(&children));
00619   for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) {
00620     const C_OUTLINE* child = child_it.data();
00621     if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested())
00622       return false;
00623   }
00624   return true;
00625 }
00626 
00636 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) {
00637   if (box.width() < min_size || box.height() < min_size) {
00638     ASSERT_HOST(this == it->data());
00639     delete it->extract();  // Too small so get rid of it and any children.
00640   } else if (!children.empty()) {
00641     // Search the children of this, deleting any that are too small.
00642     C_OUTLINE_IT child_it(&children);
00643     for (child_it.mark_cycle_pt(); !child_it.cycled_list();
00644          child_it.forward()) {
00645       C_OUTLINE* child = child_it.data();
00646       child->RemoveSmallRecursive(min_size, &child_it);
00647     }
00648   }
00649 }
00650 
00651 // Factored out helpers below are used only by ComputeEdgeOffsets to operate
00652 // on data from an 8-bit Pix, and assume that any input x and/or y are already
00653 // constrained to be legal Pix coordinates.
00654 
00660 static void ComputeGradient(const l_uint32* data, int wpl,
00661                             int x, int y, int width, int height,
00662                             ICOORD* gradient) {
00663   const l_uint32* line = data + y * wpl;
00664   int pix_x_y = x < width && y < height ?
00665       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x) : 255;
00666   int pix_x_prevy = x < width && y > 0 ?
00667       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line - wpl)), x) : 255;
00668   int pix_prevx_prevy = x > 0 && y > 0 ?
00669       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<void const*>(line - wpl)), x - 1) : 255;
00670   int pix_prevx_y = x > 0 && y < height ?
00671       GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x - 1) : 255;
00672   gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy));
00673   gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y));
00674 }
00675 
00681 static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign,
00682                                  int x, int y, int height,
00683                                  int* best_diff, int* best_sum, int* best_y) {
00684   if (y <= 0 || y >= height)
00685     return false;
00686   const l_uint32* line = data + y * wpl;
00687   int pixel1 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line - wpl)), x);
00688   int pixel2 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x);
00689   int diff = (pixel2 - pixel1) * diff_sign;
00690   if (diff > *best_diff) {
00691     *best_diff = diff;
00692     *best_sum = pixel1 + pixel2;
00693     *best_y = y;
00694   }
00695   return diff > 0;
00696 }
00697 
00703 static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign,
00704                                    int x, int width,
00705                                    int* best_diff, int* best_sum, int* best_x) {
00706   if (x <= 0 || x >= width)
00707     return false;
00708   int pixel1 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x - 1);
00709   int pixel2 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x);
00710   int diff = (pixel2 - pixel1) * diff_sign;
00711   if (diff > *best_diff) {
00712     *best_diff = diff;
00713     *best_sum = pixel1 + pixel2;
00714     *best_x = x;
00715   }
00716   return diff > 0;
00717 }
00718 
00734 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) {
00735   if (pixGetDepth(pix) != 8) return;
00736   const l_uint32* data = pixGetData(pix);
00737   int wpl = pixGetWpl(pix);
00738   int width = pixGetWidth(pix);
00739   int height = pixGetHeight(pix);
00740   bool negative = flag(COUT_INVERSE);
00741   delete [] offsets;
00742   offsets = new EdgeOffset[stepcount];
00743   ICOORD pos = start;
00744   ICOORD prev_gradient;
00745   ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
00746                   &prev_gradient);
00747   for (int s = 0; s < stepcount; ++s) {
00748     ICOORD step_vec = step(s);
00749     TPOINT pt1(pos);
00750     pos += step_vec;
00751     TPOINT pt2(pos);
00752     ICOORD next_gradient;
00753     ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
00754                     &next_gradient);
00755     // Use the sum of the prev and next as the working gradient.
00756     ICOORD gradient = prev_gradient + next_gradient;
00757     // best_diff will be manipulated to be always positive.
00758     int best_diff = 0;
00759     // offset will be the extrapolation of the location of the greyscale
00760     // threshold from the edge with the largest difference, relative to the
00761     // location of the binary edge.
00762     int offset = 0;
00763     if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) {
00764       // Horizontal step. diff_sign == 1 indicates black above.
00765       int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1;
00766       int x = MIN(pt1.x, pt2.x);
00767       int y = height - pt1.y;
00768       int best_sum = 0;
00769       int best_y = y;
00770       EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height,
00771                            &best_diff, &best_sum, &best_y);
00772       // Find the strongest edge.
00773       int test_y = y;
00774       do {
00775         ++test_y;
00776       } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
00777                                     &best_diff, &best_sum, &best_y));
00778       test_y = y;
00779       do {
00780         --test_y;
00781       } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
00782                                     &best_diff, &best_sum, &best_y));
00783       offset = diff_sign * (best_sum / 2 - threshold) +
00784           (y - best_y) * best_diff;
00785     } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) {
00786       // Vertical step. diff_sign == 1 indicates black on the left.
00787       int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1;
00788       int x = pt1.x;
00789       int y = height - MAX(pt1.y, pt2.y);
00790       const l_uint32* line = pixGetData(pix) + y * wpl;
00791       int best_sum = 0;
00792       int best_x = x;
00793       EvaluateHorizontalDiff(line, diff_sign, x, width,
00794                              &best_diff, &best_sum, &best_x);
00795       // Find the strongest edge.
00796       int test_x = x;
00797       do {
00798         ++test_x;
00799       } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
00800                                       &best_diff, &best_sum, &best_x));
00801       test_x = x;
00802       do {
00803         --test_x;
00804       } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
00805                                       &best_diff, &best_sum, &best_x));
00806       offset = diff_sign * (threshold - best_sum / 2) +
00807           (best_x - x) * best_diff;
00808     }
00809     offsets[s].offset_numerator =
00810         static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
00811     offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
00812                                                            MAX_UINT8));
00813     if (negative) gradient = -gradient;
00814     // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2)
00815     // to convert from gradient direction to edge direction.
00816     offsets[s].direction =
00817         Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256);
00818     prev_gradient = next_gradient;
00819   }
00820 }
00821 
00851 void C_OUTLINE::ComputeBinaryOffsets() {
00852   delete [] offsets;
00853   offsets = new EdgeOffset[stepcount];
00854   // Count of the number of steps in each direction in the sliding window.
00855   int dir_counts[4];
00856   // Sum of the positions (y for a horizontal step, x for vertical) in each
00857   // direction in the sliding window.
00858   int pos_totals[4];
00859   memset(dir_counts, 0, sizeof(dir_counts));
00860   memset(pos_totals, 0, sizeof(pos_totals));
00861   ICOORD pos = start;
00862   ICOORD tail_pos = pos;
00863   // tail_pos is the trailing position, with the next point to be lost from
00864   // the window.
00865   tail_pos -= step(stepcount - 1);
00866   tail_pos -= step(stepcount - 2);
00867   // head_pos is the leading position, with the next point to be added to the
00868   // window.
00869   ICOORD head_pos = tail_pos;
00870   // Set up the initial window with 4 points in [-2, 2)
00871   for (int s = -2; s < 2; ++s) {
00872     increment_step(s, 1, &head_pos, dir_counts, pos_totals);
00873   }
00874   for (int s = 0; s < stepcount; pos += step(s++)) {
00875     // At step s, s in in the middle of [s-2, s+2].
00876     increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals);
00877     int dir_index = chain_code(s);
00878     ICOORD step_vec = step(s);
00879     int best_diff = 0;
00880     int offset = 0;
00881     // Use only steps that have a count of >=2 OR the strong U-turn with a
00882     // single d and 2 at d-1 and 2 at d+1 (mod 4).
00883     if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 &&
00884         dir_counts[Modulo(dir_index - 1, 4)] == 2 &&
00885         dir_counts[Modulo(dir_index + 1, 4)] == 2)) {
00886       // Valid step direction.
00887       best_diff = dir_counts[dir_index];
00888       int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y();
00889       // The offset proposes that the actual step should be positioned at
00890       // the mean position of the steps in the window of the same direction.
00891       // See ASCII art above.
00892       offset = pos_totals[dir_index] - best_diff * edge_pos;
00893     }
00894     offsets[s].offset_numerator =
00895         static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
00896     offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
00897                                                            MAX_UINT8));
00898     // The direction is just the vector from start to end of the window.
00899     FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y());
00900     offsets[s].direction = direction.to_direction();
00901     increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals);
00902   }
00903 }
00904 
00909 void C_OUTLINE::render(int left, int top, Pix* pix) const {
00910   ICOORD pos = start;
00911   for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
00912     ICOORD next_step = step(stepindex);
00913     if (next_step.y() < 0) {
00914       pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1,
00915                   PIX_NOT(PIX_DST), NULL, 0, 0);
00916     } else if (next_step.y() > 0) {
00917       pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1,
00918                   PIX_NOT(PIX_DST), NULL, 0, 0);
00919     }
00920     pos += next_step;
00921   }
00922 }
00923 
00931 void C_OUTLINE::render_outline(int left, int top, Pix* pix) const {
00932   ICOORD pos = start;
00933   for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
00934     ICOORD next_step = step(stepindex);
00935     if (next_step.y() < 0) {
00936       pixSetPixel(pix, pos.x() - left, top - pos.y(), 1);
00937     } else if (next_step.y() > 0) {
00938       pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1);
00939     } else if (next_step.x() < 0) {
00940       pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1);
00941     } else if (next_step.x() > 0) {
00942       pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1);
00943     }
00944     pos += next_step;
00945   }
00946 }
00947 
00956 #ifndef GRAPHICS_DISABLED
00957 void C_OUTLINE::plot(ScrollView* window,
00958                      ScrollView::Color colour) const {
00959   inT16 stepindex;               // index to cstep
00960   ICOORD pos;                    // current position
00961   DIR128 stepdir;                // direction of step
00962 
00963   pos = start;                   // current position
00964   window->Pen(colour);
00965   if (stepcount == 0) {
00966     window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
00967     return;
00968   }
00969   window->SetCursor(pos.x(), pos.y());
00970 
00971   stepindex = 0;
00972   while (stepindex < stepcount) {
00973     pos += step(stepindex);    // step to next
00974     stepdir = step_dir(stepindex);
00975     stepindex++;               // count steps
00976     // merge straight lines
00977     while (stepindex < stepcount &&
00978            stepdir.get_dir() == step_dir(stepindex).get_dir()) {
00979       pos += step(stepindex);
00980       stepindex++;
00981     }
00982     window->DrawTo(pos.x(), pos.y());
00983   }
00984 }
00985 
00990 void C_OUTLINE::plot_normed(const DENORM& denorm, ScrollView::Color colour,
00991                             ScrollView* window) const {
00992   window->Pen(colour);
00993   if (stepcount == 0) {
00994     window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
00995     return;
00996   }
00997   const DENORM* root_denorm = denorm.RootDenorm();
00998   ICOORD pos = start;                   // current position
00999   FCOORD f_pos = sub_pixel_pos_at_index(pos, 0);
01000   FCOORD pos_normed;
01001   denorm.NormTransform(root_denorm, f_pos, &pos_normed);
01002   window->SetCursor(IntCastRounded(pos_normed.x()),
01003                     IntCastRounded(pos_normed.y()));
01004   for (int s = 0; s < stepcount; pos += step(s++)) {
01005     int edge_weight = edge_strength_at_index(s);
01006     if (edge_weight == 0) {
01007       // This point has conflicting gradient and step direction, so ignore it.
01008       continue;
01009     }
01010     FCOORD f_pos = sub_pixel_pos_at_index(pos, s);
01011     FCOORD pos_normed;
01012     denorm.NormTransform(root_denorm, f_pos, &pos_normed);
01013     window->DrawTo(IntCastRounded(pos_normed.x()),
01014                    IntCastRounded(pos_normed.y()));
01015   }
01016 }
01017 #endif
01018 
01019 
01027 C_OUTLINE & C_OUTLINE::operator= (const C_OUTLINE & source) {
01028   box = source.box;
01029   start = source.start;
01030   if (steps != NULL)
01031     free_mem(steps);
01032   stepcount = source.stepcount;
01033   steps = (uinT8 *) alloc_mem (step_mem());
01034   memmove (steps, source.steps, step_mem());
01035   if (!children.empty ())
01036     children.clear ();
01037   children.deep_copy(&source.children, &deep_copy);
01038   delete [] offsets;
01039   if (source.offsets != NULL) {
01040     offsets = new EdgeOffset[stepcount];
01041     memcpy(offsets, source.offsets, stepcount * sizeof(*offsets));
01042   } else {
01043     offsets = NULL;
01044   }
01045   return *this;
01046 }
01047 
01054 void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos,
01055                                int* dir_counts, int* pos_totals) const {
01056   int step_index = Modulo(s, stepcount);
01057   int dir_index = chain_code(step_index);
01058   dir_counts[dir_index] += increment;
01059   ICOORD step_vec = step(step_index);
01060   if (step_vec.x() == 0)
01061     pos_totals[dir_index] += pos->x() * increment;
01062   else
01063     pos_totals[dir_index] += pos->y() * increment;
01064   *pos += step_vec;
01065 }
01066 
01067 ICOORD C_OUTLINE::chain_step(int chaindir) {
01068   return step_coords[chaindir % 4];
01069 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines