Ejemplo n.º 1
0
// Rotate the grid by rotation, keeping cell contents.
// rotation must be a multiple of 90 degrees.
// NOTE: due to partial cells, cell coverage in the rotated grid will be
// inexact. This is why there is no Rotate for the generic BBGrid.
// TODO(rays) investigate fixing this inaccuracy by moving the origin after
// rotation.
void IntGrid::Rotate(const FCOORD& rotation) {
  ASSERT_HOST(rotation.x() == 0.0f || rotation.y() == 0.0f);
  ICOORD old_bleft(bleft());
  ICOORD old_tright(tright());
  int old_width = gridwidth();
  int old_height = gridheight();
  TBOX box(bleft(), tright());
  box.rotate(rotation);
  int* old_grid = grid_;
  grid_ = NULL;
  Init(gridsize(), box.botleft(), box.topright());
  // Iterate over the old grid, copying data to the rotated position in the new.
  int oldi = 0;
  FCOORD x_step(rotation);
  x_step *= gridsize();
  for (int oldy = 0; oldy < old_height; ++oldy) {
    FCOORD line_pos(old_bleft.x(), old_bleft.y() + gridsize() * oldy);
    line_pos.rotate(rotation);
    for (int oldx = 0; oldx < old_width; ++oldx, line_pos += x_step, ++oldi) {
      int grid_x, grid_y;
      GridCoords(static_cast<int>(line_pos.x() + 0.5),
                 static_cast<int>(line_pos.y() + 0.5),
                 &grid_x, &grid_y);
      grid_[grid_y * gridwidth() + grid_x] = old_grid[oldi];
    }
  }
  delete [] old_grid;
}
Ejemplo n.º 2
0
// Returns a full-resolution binary pix in which each cell over the given
// threshold is filled as a black square. pixDestroy after use.
// Edge cells, which have a zero 4-neighbour, are not marked.
Pix* IntGrid::ThresholdToPix(int threshold) const {
  Pix* pix = pixCreate(tright().x() - bleft().x(),
                       tright().y() - bleft().y(), 1);
  int cellsize = gridsize();
  for (int y = 0; y < gridheight(); ++y) {
    for (int x = 0; x < gridwidth(); ++x) {
      if (GridCellValue(x, y) > threshold &&
          GridCellValue(x - 1, y) > 0 && GridCellValue(x + 1, y) > 0 &&
              GridCellValue(x, y - 1) > 0 && GridCellValue(x, y + 1) > 0) {
        pixRasterop(pix, x * cellsize, tright().y() - ((y + 1) * cellsize),
                    cellsize, cellsize, PIX_SET, NULL, 0, 0);
      }
    }
  }
  return pix;
}
Ejemplo n.º 3
0
// Finds horizontal line objects in the given pix.
// Uses the given resolution to determine size thresholds instead of any
// that may be present in the pix.
// The output vectors are owned by the list and Frozen (cannot refit) by
// having no boxes, as there is no need to refit or merge separator lines.
void LineFinder::FindHorizontalLines(int resolution,  Pix* pix,
                                     TabVector_LIST* vectors) {
#ifdef HAVE_LIBLEPT
  Pix* line_pix;
  Boxa* boxes = GetHLineBoxes(resolution, pix, &line_pix);
  C_BLOB_LIST line_cblobs;
  int width = pixGetWidth(pix);
  int height = pixGetHeight(pix);
  ConvertBoxaToBlobs(height, width, &boxes, &line_cblobs);
  // Make the BLOBNBOXes from the C_BLOBs.
  BLOBNBOX_LIST line_bblobs;
  C_BLOB_IT blob_it(&line_cblobs);
  BLOBNBOX_IT bbox_it(&line_bblobs);
  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
    C_BLOB* cblob = blob_it.data();
    BLOBNBOX* bblob = new BLOBNBOX(cblob);
    bbox_it.add_to_end(bblob);
  }
  ICOORD bleft(0, 0);
  ICOORD tright(height, width);
  int vertical_x, vertical_y;
  FindLineVectors(bleft, tright, &line_bblobs, &vertical_x, &vertical_y,
                  vectors);
  if (!vectors->empty()) {
    // Some lines were found, so erase the unused blobs from the line image
    // and then subtract the line image from the source.
    bbox_it.move_to_first();
    for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
      BLOBNBOX* blob = bbox_it.data();
      if (blob->left_tab_type() == TT_UNCONFIRMED) {
        const TBOX& box = blob->bounding_box();
        // Coords are in tess format so filp x and y and then covert
        // to leptonica by height -y.
        Box* pixbox = boxCreate(box.bottom(), height - box.right(),
                                box.height(), box.width());
        pixClearInRect(line_pix, pixbox);
        boxDestroy(&pixbox);
      }
    }
    pixDilateBrick(line_pix, line_pix, 3, 1);
    pixSubtract(pix, pix, line_pix);
    if (textord_tabfind_show_vlines)
      pixWrite("hlinesclean.png", line_pix, IFF_PNG);
    ICOORD vertical;
    vertical.set_with_shrink(vertical_x, vertical_y);
    TabVector::MergeSimilarTabVectors(vertical, vectors, NULL);
    // Iterate the vectors to flip them.
    TabVector_IT h_it(vectors);
    for (h_it.mark_cycle_pt(); !h_it.cycled_list(); h_it.forward()) {
      h_it.data()->XYFlip();
    }
  }
  pixDestroy(&line_pix);
#endif
}
Ejemplo n.º 4
0
// Tests each blob in the list to see if it is certain non-text using 2
// conditions:
// 1. blob overlaps a cell with high value in noise_density_ (previously set
// by ComputeNoiseDensity).
// OR 2. The blob overlaps more than max_blob_overlaps in *this grid. This
// condition is disabled with max_blob_overlaps == -1.
// If it does, the blob is declared non-text, and is used to mark up the
// nontext_mask. Such blobs are fully deleted, and non-noise blobs have their
// neighbours reset, as they may now point to deleted data.
// WARNING: The blobs list blobs may be in the *this grid, but they are
// not removed. If any deleted blobs might be in *this, then this must be
// Clear()ed immediately after MarkAndDeleteNonTextBlobs is called.
// If the win is not NULL, deleted blobs are drawn on it in red, and kept
// blobs are drawn on it in ok_color.
void CCNonTextDetect::MarkAndDeleteNonTextBlobs(BLOBNBOX_LIST* blobs,
        int max_blob_overlaps,
        ScrollView* win,
        ScrollView::Color ok_color,
        Pix* nontext_mask) {
    int imageheight = tright().y() - bleft().x();
    BLOBNBOX_IT blob_it(blobs);
    BLOBNBOX_LIST dead_blobs;
    BLOBNBOX_IT dead_it(&dead_blobs);
    for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
        BLOBNBOX* blob = blob_it.data();
        TBOX box = blob->bounding_box();
        if (!noise_density_->RectMostlyOverThreshold(box, max_noise_count_) &&
                (max_blob_overlaps < 0 ||
                 !BlobOverlapsTooMuch(blob, max_blob_overlaps))) {
            blob->ClearNeighbours();
#ifndef GRAPHICS_DISABLED
            if (win != NULL)
                blob->plot(win, ok_color, ok_color);
#endif  // GRAPHICS_DISABLED
        } else {
            if (noise_density_->AnyZeroInRect(box)) {
                // There is a danger that the bounding box may overlap real text, so
                // we need to render the outline.
                Pix* blob_pix = blob->cblob()->render_outline();
                pixRasterop(nontext_mask, box.left(), imageheight - box.top(),
                            box.width(), box.height(), PIX_SRC | PIX_DST,
                            blob_pix, 0, 0);
                pixDestroy(&blob_pix);
            } else {
                if (box.area() < gridsize() * gridsize()) {
                    // It is a really bad idea to make lots of small components in the
                    // photo mask, so try to join it to a bigger area by expanding the
                    // box in a way that does not touch any zero noise density cell.
                    box = AttemptBoxExpansion(box, *noise_density_, gridsize());
                }
                // All overlapped cells are non-zero, so just mark the rectangle.
                pixRasterop(nontext_mask, box.left(), imageheight - box.top(),
                            box.width(), box.height(), PIX_SET, NULL, 0, 0);
            }
#ifndef GRAPHICS_DISABLED
            if (win != NULL)
                blob->plot(win, ScrollView::RED, ScrollView::RED);
#endif  // GRAPHICS_DISABLED
            // It is safe to delete the cblob now, as it isn't used by the grid
            // or BlobOverlapsTooMuch, and the BLOBNBOXes will go away with the
            // dead_blobs list.
            // TODO(rays) delete the delete when the BLOBNBOX destructor deletes
            // the cblob.
            delete blob->cblob();
            dead_it.add_to_end(blob_it.extract());
        }
    }
}
Ejemplo n.º 5
0
// Finds vertical line objects in the given pix.
// Uses the given resolution to determine size thresholds instead of any
// that may be present in the pix.
// The output vertical_x and vertical_y contain a sum of the output vectors,
// thereby giving the mean vertical direction.
// The output vectors are owned by the list and Frozen (cannot refit) by
// having no boxes, as there is no need to refit or merge separator lines.
void LineFinder::FindVerticalLines(int resolution,  Pix* pix,
                                   int* vertical_x, int* vertical_y,
                                   TabVector_LIST* vectors) {
#ifdef HAVE_LIBLEPT
  Pix* line_pix;
  Boxa* boxes = GetVLineBoxes(resolution, pix, &line_pix);
  C_BLOB_LIST line_cblobs;
  int width = pixGetWidth(pix);
  int height = pixGetHeight(pix);
  ConvertBoxaToBlobs(width, height, &boxes, &line_cblobs);
  // Make the BLOBNBOXes from the C_BLOBs.
  BLOBNBOX_LIST line_bblobs;
  C_BLOB_IT blob_it(&line_cblobs);
  BLOBNBOX_IT bbox_it(&line_bblobs);
  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
    C_BLOB* cblob = blob_it.data();
    BLOBNBOX* bblob = new BLOBNBOX(cblob);
    bbox_it.add_to_end(bblob);
  }
  ICOORD bleft(0, 0);
  ICOORD tright(width, height);
  FindLineVectors(bleft, tright, &line_bblobs, vertical_x, vertical_y, vectors);
  if (!vectors->empty()) {
    // Some lines were found, so erase the unused blobs from the line image
    // and then subtract the line image from the source.
    bbox_it.move_to_first();
    for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
      BLOBNBOX* blob = bbox_it.data();
      if (blob->left_tab_type() == TT_UNCONFIRMED) {
        const TBOX& box = blob->bounding_box();
        Box* pixbox = boxCreate(box.left(), height - box.top(),
                                box.width(), box.height());
        pixClearInRect(line_pix, pixbox);
        boxDestroy(&pixbox);
      }
    }
    pixDilateBrick(line_pix, line_pix, 1, 3);
    pixSubtract(pix, pix, line_pix);
    if (textord_tabfind_show_vlines)
      pixWrite("vlinesclean.png", line_pix, IFF_PNG);
    ICOORD vertical;
    vertical.set_with_shrink(*vertical_x, *vertical_y);
    TabVector::MergeSimilarTabVectors(vertical, vectors, NULL);
  }
  pixDestroy(&line_pix);
#endif
}
Ejemplo n.º 6
0
// Returns a new IntGrid containing values equal to the sum of all the
// neighbouring cells. The returned grid must be deleted after use.
// For ease of implementation, edge cells are double counted, to make them
// have the same range as the non-edge cells.
IntGrid* IntGrid::NeighbourhoodSum() const {
  IntGrid* sumgrid = new IntGrid(gridsize(), bleft(), tright());
  for (int y = 0; y < gridheight(); ++y) {
    for (int x = 0; x < gridwidth(); ++x) {
      int cell_count = 0;
      for (int yoffset = -1; yoffset <= 1; ++yoffset) {
        for (int xoffset = -1; xoffset <= 1; ++xoffset) {
          int grid_x = x + xoffset;
          int grid_y = y + yoffset;
          ClipGridCoords(&grid_x, &grid_y);
          cell_count += GridCellValue(grid_x, grid_y);
        }
      }
      if (GridCellValue(x, y) > 1)
        sumgrid->SetGridCell(x, y, cell_count);
    }
  }
  return sumgrid;
}
Ejemplo n.º 7
0
void start(void){
	unsigned short left;
	unsigned short right;

	left = ADRead(0);
	right = ADRead(1);

	Wait(1000);
	for (;;) {
		if(right > 500 && left >500){
			stop();
		}else if(right > 500){
			tright();
		}else if (left > 500){
			tleft();
		}else{
			go();
		}
	}
}
Ejemplo n.º 8
0
// Creates and returns a Pix with the same resolution as the original
// in which 1 (black) pixels represent likely non text (photo, line drawing)
// areas of the page, deleting from the blob_block the blobs that were
// determined to be non-text.
// The photo_map is used to bias the decision towards non-text, rather than
// supplying definite decision.
// The blob_block is the usual result of connected component analysis,
// holding the detected blobs.
// The returned Pix should be PixDestroyed after use.
Pix* CCNonTextDetect::ComputeNonTextMask(bool debug, Pix* photo_map,
        TO_BLOCK* blob_block) {
    // Insert the smallest blobs into the grid.
    InsertBlobList(&blob_block->small_blobs);
    InsertBlobList(&blob_block->noise_blobs);
    // Add the medium blobs that don't have a good strokewidth neighbour.
    // Those that do go into good_grid as an antidote to spreading beyond the
    // real reaches of a noise region.
    BlobGrid good_grid(gridsize(), bleft(), tright());
    BLOBNBOX_IT blob_it(&blob_block->blobs);
    for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
        BLOBNBOX* blob = blob_it.data();
        double perimeter_area_ratio = blob->cblob()->perimeter() / 4.0;
        perimeter_area_ratio *= perimeter_area_ratio / blob->enclosed_area();
        if (blob->GoodTextBlob() == 0 || perimeter_area_ratio < kMinGoodTextPARatio)
            InsertBBox(true, true, blob);
        else
            good_grid.InsertBBox(true, true, blob);
    }
    noise_density_ = ComputeNoiseDensity(debug, photo_map, &good_grid);
    good_grid.Clear();  // Not needed any more.
    Pix* pix = noise_density_->ThresholdToPix(max_noise_count_);
    if (debug) {
        pixWrite("junknoisemask.png", pix, IFF_PNG);
    }
    ScrollView* win = NULL;
#ifndef GRAPHICS_DISABLED
    if (debug) {
        win = MakeWindow(0, 400, "Photo Mask Blobs");
    }
#endif  // GRAPHICS_DISABLED
    // Large and medium blobs are not text if they overlap with "a lot" of small
    // blobs.
    MarkAndDeleteNonTextBlobs(&blob_block->large_blobs,
                              kMaxLargeOverlapsWithSmall,
                              win, ScrollView::DARK_GREEN, pix);
    MarkAndDeleteNonTextBlobs(&blob_block->blobs, kMaxMediumOverlapsWithSmall,
                              win, ScrollView::WHITE, pix);
    // Clear the grid of small blobs and insert the medium blobs.
    Clear();
    InsertBlobList(&blob_block->blobs);
    MarkAndDeleteNonTextBlobs(&blob_block->large_blobs,
                              kMaxLargeOverlapsWithMedium,
                              win, ScrollView::DARK_GREEN, pix);
    // Clear again before we start deleting the blobs in the grid.
    Clear();
    MarkAndDeleteNonTextBlobs(&blob_block->noise_blobs, -1,
                              win, ScrollView::CORAL, pix);
    MarkAndDeleteNonTextBlobs(&blob_block->small_blobs, -1,
                              win, ScrollView::GOLDENROD, pix);
    MarkAndDeleteNonTextBlobs(&blob_block->blobs, -1,
                              win, ScrollView::WHITE, pix);
    if (debug) {
#ifndef GRAPHICS_DISABLED
        win->Update();
#endif  // GRAPHICS_DISABLED
        pixWrite("junkccphotomask.png", pix, IFF_PNG);
#ifndef GRAPHICS_DISABLED
        delete win->AwaitEvent(SVET_DESTROY);
        delete win;
#endif  // GRAPHICS_DISABLED
    }
    return pix;
}