mirror of
https://github.com/tesseract-ocr/tesseract.git
synced 2024-12-01 07:59:05 +08:00
485 lines
16 KiB
C++
485 lines
16 KiB
C++
/**********************************************************************
|
|
* File: rect.h (Formerly box.h)
|
|
* Description: Bounding box class definition.
|
|
* Author: Phil Cheatle
|
|
* Created: Wed Oct 16 15:18:45 BST 1991
|
|
*
|
|
* (C) Copyright 1991, Hewlett-Packard Ltd.
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
** you may not use this file except in compliance with the License.
|
|
** You may obtain a copy of the License at
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*
|
|
**********************************************************************/
|
|
|
|
#ifndef RECT_H
|
|
#define RECT_H
|
|
|
|
#include <math.h>
|
|
#include "points.h"
|
|
#include "ndminx.h"
|
|
#include "scrollview.h"
|
|
#include "strngs.h"
|
|
#include "tprintf.h"
|
|
|
|
class DLLSYM TBOX { // bounding box
|
|
public:
|
|
TBOX (): // empty constructor making a null box
|
|
bot_left (MAX_INT16, MAX_INT16), top_right (-MAX_INT16, -MAX_INT16) {
|
|
}
|
|
|
|
TBOX( // constructor
|
|
const ICOORD pt1, // one corner
|
|
const ICOORD pt2); // the other corner
|
|
|
|
TBOX( // constructor
|
|
inT16 left, inT16 bottom, inT16 right, inT16 top);
|
|
|
|
TBOX( // box around FCOORD
|
|
const FCOORD pt);
|
|
|
|
bool null_box() const { // Is box null
|
|
return ((left () >= right ()) || (top () <= bottom ()));
|
|
}
|
|
|
|
bool operator==(const TBOX& other) const {
|
|
return bot_left == other.bot_left && top_right == other.top_right;
|
|
}
|
|
|
|
inT16 top() const { // coord of top
|
|
return top_right.y ();
|
|
}
|
|
void set_top(int y) {
|
|
top_right.set_y(y);
|
|
}
|
|
|
|
inT16 bottom() const { // coord of bottom
|
|
return bot_left.y ();
|
|
}
|
|
void set_bottom(int y) {
|
|
bot_left.set_y(y);
|
|
}
|
|
|
|
inT16 left() const { // coord of left
|
|
return bot_left.x ();
|
|
}
|
|
void set_left(int x) {
|
|
bot_left.set_x(x);
|
|
}
|
|
|
|
inT16 right() const { // coord of right
|
|
return top_right.x ();
|
|
}
|
|
void set_right(int x) {
|
|
top_right.set_x(x);
|
|
}
|
|
int x_middle() const {
|
|
return (bot_left.x() + top_right.x()) / 2;
|
|
}
|
|
int y_middle() const {
|
|
return (bot_left.y() + top_right.y()) / 2;
|
|
}
|
|
|
|
const ICOORD &botleft() const { // access function
|
|
return bot_left;
|
|
}
|
|
|
|
ICOORD botright() const { // ~ access function
|
|
return ICOORD (top_right.x (), bot_left.y ());
|
|
}
|
|
|
|
ICOORD topleft() const { // ~ access function
|
|
return ICOORD (bot_left.x (), top_right.y ());
|
|
}
|
|
|
|
const ICOORD &topright() const { // access function
|
|
return top_right;
|
|
}
|
|
|
|
inT16 height() const { // how high is it?
|
|
if (!null_box ())
|
|
return top_right.y () - bot_left.y ();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
inT16 width() const { // how high is it?
|
|
if (!null_box ())
|
|
return top_right.x () - bot_left.x ();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
inT32 area() const { // what is the area?
|
|
if (!null_box ())
|
|
return width () * height ();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Pads the box on either side by the supplied x,y pad amounts.
|
|
// NO checks for exceeding any bounds like 0 or an image size.
|
|
void pad(int xpad, int ypad) {
|
|
ICOORD pad(xpad, ypad);
|
|
bot_left -= pad;
|
|
top_right += pad;
|
|
}
|
|
|
|
void move_bottom_edge( // move one edge
|
|
const inT16 y) { // by +/- y
|
|
bot_left += ICOORD (0, y);
|
|
}
|
|
|
|
void move_left_edge( // move one edge
|
|
const inT16 x) { // by +/- x
|
|
bot_left += ICOORD (x, 0);
|
|
}
|
|
|
|
void move_right_edge( // move one edge
|
|
const inT16 x) { // by +/- x
|
|
top_right += ICOORD (x, 0);
|
|
}
|
|
|
|
void move_top_edge( // move one edge
|
|
const inT16 y) { // by +/- y
|
|
top_right += ICOORD (0, y);
|
|
}
|
|
|
|
void move( // move box
|
|
const ICOORD vec) { // by vector
|
|
bot_left += vec;
|
|
top_right += vec;
|
|
}
|
|
|
|
void move( // move box
|
|
const FCOORD vec) { // by float vector
|
|
bot_left.set_x ((inT16) floor (bot_left.x () + vec.x ()));
|
|
// round left
|
|
bot_left.set_y ((inT16) floor (bot_left.y () + vec.y ()));
|
|
// round down
|
|
top_right.set_x ((inT16) ceil (top_right.x () + vec.x ()));
|
|
// round right
|
|
top_right.set_y ((inT16) ceil (top_right.y () + vec.y ()));
|
|
// round up
|
|
}
|
|
|
|
void scale( // scale box
|
|
const float f) { // by multiplier
|
|
bot_left.set_x ((inT16) floor (bot_left.x () * f)); // round left
|
|
bot_left.set_y ((inT16) floor (bot_left.y () * f)); // round down
|
|
top_right.set_x ((inT16) ceil (top_right.x () * f)); // round right
|
|
top_right.set_y ((inT16) ceil (top_right.y () * f)); // round up
|
|
}
|
|
void scale( // scale box
|
|
const FCOORD vec) { // by float vector
|
|
bot_left.set_x ((inT16) floor (bot_left.x () * vec.x ()));
|
|
bot_left.set_y ((inT16) floor (bot_left.y () * vec.y ()));
|
|
top_right.set_x ((inT16) ceil (top_right.x () * vec.x ()));
|
|
top_right.set_y ((inT16) ceil (top_right.y () * vec.y ()));
|
|
}
|
|
|
|
// rotate doesn't enlarge the box - it just rotates the bottom-left
|
|
// and top-right corners. Use rotate_large if you want to guarantee
|
|
// that all content is contained within the rotated box.
|
|
void rotate(const FCOORD& vec) { // by vector
|
|
bot_left.rotate (vec);
|
|
top_right.rotate (vec);
|
|
*this = TBOX (bot_left, top_right);
|
|
}
|
|
// rotate_large constructs the containing bounding box of all 4
|
|
// corners after rotating them. It therefore guarantees that all
|
|
// original content is contained within, but also slightly enlarges the box.
|
|
void rotate_large(const FCOORD& vec);
|
|
|
|
bool contains( // is pt inside box
|
|
const FCOORD pt) const;
|
|
|
|
bool contains( // is box inside box
|
|
const TBOX &box) const;
|
|
|
|
bool overlap( // do boxes overlap
|
|
const TBOX &box) const;
|
|
|
|
bool major_overlap( // do boxes overlap more than half
|
|
const TBOX &box) const;
|
|
|
|
// Do boxes overlap on x axis.
|
|
bool x_overlap(const TBOX &box) const;
|
|
|
|
// Return the horizontal gap between the boxes. If the boxes
|
|
// overlap horizontally then the return value is negative, indicating
|
|
// the amount of the overlap.
|
|
int x_gap(const TBOX& box) const {
|
|
return MAX(bot_left.x(), box.bot_left.x()) -
|
|
MIN(top_right.x(), box.top_right.x());
|
|
}
|
|
|
|
// Return the vertical gap between the boxes. If the boxes
|
|
// overlap vertically then the return value is negative, indicating
|
|
// the amount of the overlap.
|
|
int y_gap(const TBOX& box) const {
|
|
return MAX(bot_left.y(), box.bot_left.y()) -
|
|
MIN(top_right.y(), box.top_right.y());
|
|
}
|
|
|
|
// Do boxes overlap on x axis by more than
|
|
// half of the width of the narrower box.
|
|
bool major_x_overlap(const TBOX &box) const;
|
|
|
|
// Do boxes overlap on y axis.
|
|
bool y_overlap(const TBOX &box) const;
|
|
|
|
// Do boxes overlap on y axis by more than
|
|
// half of the height of the shorter box.
|
|
bool major_y_overlap(const TBOX &box) const;
|
|
|
|
// fraction of current box's area covered by other
|
|
double overlap_fraction(const TBOX &box) const;
|
|
|
|
// fraction of the current box's projected area covered by the other's
|
|
double x_overlap_fraction(const TBOX& box) const;
|
|
|
|
// fraction of the current box's projected area covered by the other's
|
|
double y_overlap_fraction(const TBOX& box) const;
|
|
|
|
// Returns true if the boxes are almost equal on x axis.
|
|
bool x_almost_equal(const TBOX &box, int tolerance) const;
|
|
|
|
// Returns true if the boxes are almost equal
|
|
bool almost_equal(const TBOX &box, int tolerance) const;
|
|
|
|
TBOX intersection( // shared area box
|
|
const TBOX &box) const;
|
|
|
|
TBOX bounding_union( // box enclosing both
|
|
const TBOX &box) const;
|
|
|
|
// Sets the box boundaries to the given coordinates.
|
|
void set_to_given_coords(int x_min, int y_min, int x_max, int y_max) {
|
|
bot_left.set_x(x_min);
|
|
bot_left.set_y(y_min);
|
|
top_right.set_x(x_max);
|
|
top_right.set_y(y_max);
|
|
}
|
|
|
|
void print() const { // print
|
|
tprintf("Bounding box=(%d,%d)->(%d,%d)\n",
|
|
left(), bottom(), right(), top());
|
|
}
|
|
// Appends the bounding box as (%d,%d)->(%d,%d) to a STRING.
|
|
void print_to_str(STRING *str) const;
|
|
|
|
#ifndef GRAPHICS_DISABLED
|
|
void plot( // use current settings
|
|
ScrollView* fd) const { // where to paint
|
|
fd->Rectangle(bot_left.x (), bot_left.y (), top_right.x (),
|
|
top_right.y ());
|
|
}
|
|
|
|
void plot( // paint box
|
|
ScrollView* fd, // where to paint
|
|
ScrollView::Color fill_colour, // colour for inside
|
|
ScrollView::Color border_colour) const; // colour for border
|
|
#endif
|
|
// Writes to the given file. Returns false in case of error.
|
|
bool Serialize(FILE* fp) const;
|
|
// Reads from the given file. Returns false in case of error.
|
|
// If swap is true, assumes a big/little-endian swap is needed.
|
|
bool DeSerialize(bool swap, FILE* fp);
|
|
|
|
friend TBOX& operator+=(TBOX&, const TBOX&);
|
|
// in place union
|
|
friend TBOX& operator&=(TBOX&, const TBOX&);
|
|
// in place intersection
|
|
|
|
private:
|
|
ICOORD bot_left; // bottom left corner
|
|
ICOORD top_right; // top right corner
|
|
};
|
|
|
|
/**********************************************************************
|
|
* TBOX::TBOX() Constructor from 1 FCOORD
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline TBOX::TBOX( // constructor
|
|
const FCOORD pt // floating centre
|
|
) {
|
|
bot_left = ICOORD ((inT16) floor (pt.x ()), (inT16) floor (pt.y ()));
|
|
top_right = ICOORD ((inT16) ceil (pt.x ()), (inT16) ceil (pt.y ()));
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* TBOX::contains() Is point within box
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::contains(const FCOORD pt) const {
|
|
return ((pt.x () >= bot_left.x ()) &&
|
|
(pt.x () <= top_right.x ()) &&
|
|
(pt.y () >= bot_left.y ()) && (pt.y () <= top_right.y ()));
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* TBOX::contains() Is box within box
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::contains(const TBOX &box) const {
|
|
return (contains (box.bot_left) && contains (box.top_right));
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* TBOX::overlap() Do two boxes overlap?
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::overlap( // do boxes overlap
|
|
const TBOX &box) const {
|
|
return ((box.bot_left.x () <= top_right.x ()) &&
|
|
(box.top_right.x () >= bot_left.x ()) &&
|
|
(box.bot_left.y () <= top_right.y ()) &&
|
|
(box.top_right.y () >= bot_left.y ()));
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::major_overlap() Do two boxes overlap by at least half of the smallest?
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::major_overlap( // Do boxes overlap more that half.
|
|
const TBOX &box) const {
|
|
int overlap = MIN(box.top_right.x(), top_right.x());
|
|
overlap -= MAX(box.bot_left.x(), bot_left.x());
|
|
overlap += overlap;
|
|
if (overlap < MIN(box.width(), width()))
|
|
return false;
|
|
overlap = MIN(box.top_right.y(), top_right.y());
|
|
overlap -= MAX(box.bot_left.y(), bot_left.y());
|
|
overlap += overlap;
|
|
if (overlap < MIN(box.height(), height()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::overlap_fraction() Fraction of area covered by the other box
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline double TBOX::overlap_fraction(const TBOX &box) const {
|
|
double fraction = 0.0;
|
|
if (this->area()) {
|
|
fraction = this->intersection(box).area() * 1.0 / this->area();
|
|
}
|
|
return fraction;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::x_overlap() Do two boxes overlap on x-axis
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::x_overlap(const TBOX &box) const {
|
|
return ((box.bot_left.x() <= top_right.x()) &&
|
|
(box.top_right.x() >= bot_left.x()));
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::major_x_overlap() Do two boxes overlap by more than half the
|
|
* width of the narrower box on the x-axis
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::major_x_overlap(const TBOX &box) const {
|
|
inT16 overlap = box.width();
|
|
if (this->left() > box.left()) {
|
|
overlap -= this->left() - box.left();
|
|
}
|
|
if (this->right() < box.right()) {
|
|
overlap -= box.right() - this->right();
|
|
}
|
|
return (overlap >= box.width() / 2 || overlap >= this->width() / 2);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::y_overlap() Do two boxes overlap on y-axis
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::y_overlap(const TBOX &box) const {
|
|
return ((box.bot_left.y() <= top_right.y()) &&
|
|
(box.top_right.y() >= bot_left.y()));
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::major_y_overlap() Do two boxes overlap by more than half the
|
|
* height of the shorter box on the y-axis
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline bool TBOX::major_y_overlap(const TBOX &box) const {
|
|
inT16 overlap = box.height();
|
|
if (this->bottom() > box.bottom()) {
|
|
overlap -= this->bottom() - box.bottom();
|
|
}
|
|
if (this->top() < box.top()) {
|
|
overlap -= box.top() - this->top();
|
|
}
|
|
return (overlap >= box.height() / 2 || overlap >= this->height() / 2);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::x_overlap_fraction() Calculates the horizontal overlap of the
|
|
* given boxes as a fraction of this boxes
|
|
* width.
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline double TBOX::x_overlap_fraction(const TBOX& other) const {
|
|
int low = MAX(left(), other.left());
|
|
int high = MIN(right(), other.right());
|
|
int width = right() - left();
|
|
if (width == 0) {
|
|
int x = left();
|
|
if (other.left() <= x && x <= other.right())
|
|
return 1.0;
|
|
else
|
|
return 0.0;
|
|
} else {
|
|
return MAX(0, static_cast<double>(high - low) / width);
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* TBOX::y_overlap_fraction() Calculates the vertical overlap of the
|
|
* given boxes as a fraction of this boxes
|
|
* height.
|
|
*
|
|
**********************************************************************/
|
|
|
|
inline double TBOX::y_overlap_fraction(const TBOX& other) const {
|
|
int low = MAX(bottom(), other.bottom());
|
|
int high = MIN(top(), other.top());
|
|
int height = top() - bottom();
|
|
if (height == 0) {
|
|
int y = bottom();
|
|
if (other.bottom() <= y && y <= other.top())
|
|
return 1.0;
|
|
else
|
|
return 0.0;
|
|
} else {
|
|
return MAX(0, static_cast<double>(high - low) / height);
|
|
}
|
|
}
|
|
|
|
#endif
|