tesseract
3.03
|
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 }