tesseract/ccstruct/normalis.cpp
theraysmith 1c38920f9d Rewrote DENORM
git-svn-id: https://tesseract-ocr.googlecode.com/svn/trunk@530 d0cd1f9f-072b-0410-8dd7-cf729c803f20
2010-11-30 00:56:08 +00:00

365 lines
12 KiB
C++

/**********************************************************************
* File: normalis.cpp (Formerly denorm.c)
* Description: Code for the DENORM class.
* Author: Ray Smith
* Created: Thu Apr 23 09:22:43 BST 1992
*
* (C) Copyright 1992, 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.
*
**********************************************************************/
#include "mfcpch.h" // Precompiled header include must be first.
#include "normalis.h"
#include <stdlib.h>
#include "allheaders.h"
#include "blobs.h"
#include "ocrblock.h"
#include "werd.h"
DENORM::DENORM() {
Init();
}
// TODO(rays) Abolish all non-default constructors.
DENORM::DENORM(float x, float scaling, ROW *src) {
Init();
x_origin_ = x; // just copy
y_origin_ = 0.0f;
x_scale_ = y_scale_ = scaling;
row_ = src;
}
DENORM::DENORM(float x, // from same pieces
float scaling,
double line_m, // default line: y = mx + c
double line_c,
inT16 seg_count, // no of segments
DENORM_SEG *seg_pts, // actual segments
BOOL8 using_row, // as baseline
ROW *src) {
Init();
x_origin_ = x; // just copy
y_origin_ = line_c;
ASSERT_HOST(line_m == 0.0);
x_scale_ = y_scale_ = scaling;
SetSegments(seg_pts, seg_count);
}
DENORM::DENORM(const DENORM &src) {
num_segs_ = 0;
segs_ = NULL;
rotation_ = NULL;
*this = src;
}
DENORM & DENORM::operator=(const DENORM & src) {
Clear();
inverse_ = src.inverse_;
pix_ = src.pix_;
block_ = src.block_;
row_ = src.row_;
if (src.rotation_ == NULL)
rotation_ = NULL;
else
rotation_ = new FCOORD(*src.rotation_);
predecessor_ = src.predecessor_;
SetSegments(src.segs_, src.num_segs_);
x_origin_ = src.x_origin_;
y_origin_ = src.y_origin_;
x_scale_ = src.x_scale_;
y_scale_ = src.y_scale_;
final_xshift_ = src.final_xshift_;
final_yshift_ = src.final_yshift_;
return *this;
}
DENORM::~DENORM() {
Clear();
}
// Setup for a baseline normalization. If there are segs, then they
// are used, otherwise, if there is a row, that is used, otherwise the
// bottom of the word_box is used for the baseline.
void DENORM::SetupBLNormalize(const BLOCK* block, const ROW* row,
float x_height, const TBOX& word_box,
int num_segs, const DENORM_SEG* segs) {
float scale = kBlnXHeight / x_height;
float x_origin = (word_box.left() + word_box.right()) / 2.0f;
float y_origin = 0.0f;
if (num_segs == 0 && row == NULL) {
y_origin = word_box.bottom();
}
SetupNormalization(block, row, NULL, NULL, segs, num_segs,
x_origin, y_origin, scale, scale,
0.0f, static_cast<float>(kBlnBaselineOffset));
}
// Initializes the denorm for a transformation. For details see the large
// comment in normalis.h.
// Arguments:
// block: if not NULL, then this is the first transformation, and
// block->re_rotation() needs to be used after the Denorm
// transformation to get back to the image coords.
// row: if not NULL, then row->baseline(x) is added to the y_origin, unless
// segs is not NULL and num_segs > 0, in which case they are used.
// rotation: if not NULL, apply this rotation after translation to the
// origin and scaling. (Usually a classify rotation.)
// predecessor: if not NULL, then predecessor has been applied to the
// input space and needs to be undone to complete the inverse.
// segs: if not NULL and num_segs > 0, then the segs provide the y_origin
// and the y_scale at a given source x.
// num_segs: the number of segs.
// The above pointers are not owned by this DENORM and are assumed to live
// longer than this denorm, except rotation, which is deep copied on input.
//
// x_origin: The x origin which will be mapped to final_xshift in the result.
// y_origin: The y origin which will be mapped to final_yshift in the result.
// Added to result of row->baseline(x) if not NULL.
//
// x_scale: scale factor for the x-coordinate.
// y_scale: scale factor for the y-coordinate. Ignored if segs is given.
// Note that these scale factors apply to the same x and y system as the
// x-origin and y-origin apply, ie after any block rotation, but before
// the rotation argument is applied.
//
// final_xshift: The x component of the final translation.
// final_yshift: The y component of the final translation.
void DENORM::SetupNormalization(const BLOCK* block,
const ROW* row,
const FCOORD* rotation,
const DENORM* predecessor,
const DENORM_SEG* segs, int num_segs,
float x_origin, float y_origin,
float x_scale, float y_scale,
float final_xshift, float final_yshift) {
Clear();
block_ = block;
row_ = row;
if (rotation == NULL)
rotation_ = NULL;
else
rotation_ = new FCOORD(*rotation);
predecessor_ = predecessor;
SetSegments(segs, num_segs);
x_origin_ = x_origin;
y_origin_ = y_origin;
x_scale_ = x_scale;
y_scale_ = y_scale;
final_xshift_ = final_xshift;
final_yshift_ = final_yshift;
}
// Transforms the given coords one step forward to normalized space, without
// using any block rotation or predecessor.
void DENORM::LocalNormTransform(const TPOINT& pt, TPOINT* transformed) const {
FCOORD src_pt(pt.x, pt.y);
FCOORD float_result;
LocalNormTransform(src_pt, &float_result);
transformed->x = IntCastRounded(float_result.x());
transformed->y = IntCastRounded(float_result.y());
}
void DENORM::LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const {
FCOORD translated(pt.x() - x_origin_, pt.y() - YOriginAtOrigX(pt.x()));
translated.set_x(translated.x() * x_scale_);
translated.set_y(translated.y() * YScaleAtOrigX(pt.x()));
if (rotation_ != NULL)
translated.rotate(*rotation_);
transformed->set_x(translated.x() + final_xshift_);
transformed->set_y(translated.y() + final_yshift_);
}
// Transforms the given coords forward to normalized space using the
// full transformation sequence defined by the block rotation, the
// predecessors, deepest first, and finally this.
void DENORM::NormTransform(const TPOINT& pt, TPOINT* transformed) const {
FCOORD src_pt(pt.x, pt.y);
FCOORD float_result;
NormTransform(src_pt, &float_result);
transformed->x = IntCastRounded(float_result.x());
transformed->y = IntCastRounded(float_result.y());
}
void DENORM::NormTransform(const FCOORD& pt, FCOORD* transformed) const {
FCOORD src_pt(pt);
if (predecessor_ != NULL) {
predecessor_->NormTransform(pt, &src_pt);
} else if (block_ != NULL) {
FCOORD fwd_rotation(block_->re_rotation().x(), -block_->re_rotation().y());
src_pt.rotate(fwd_rotation);
}
LocalNormTransform(src_pt, transformed);
}
// Transforms the given coords one step back to source space, without
// using to any block rotation or predecessor.
void DENORM::LocalDenormTransform(const TPOINT& pt, TPOINT* original) const {
FCOORD src_pt(pt.x, pt.y);
FCOORD float_result;
LocalDenormTransform(src_pt, &float_result);
original->x = IntCastRounded(float_result.x());
original->y = IntCastRounded(float_result.y());
}
void DENORM::LocalDenormTransform(const FCOORD& pt, FCOORD* original) const {
FCOORD rotated(pt.x() - final_xshift_, pt.y() - final_yshift_);
if (rotation_ != NULL) {
FCOORD inverse_rotation(rotation_->x(), -rotation_->y());
rotated.rotate(inverse_rotation);
}
original->set_x(rotated.x() / x_scale_ + x_origin_);
float y_scale = y_scale_;
if (num_segs_ > 0)
y_scale = YScaleAtOrigX(original->x());
original->set_y(rotated.y() / y_scale + YOriginAtOrigX(original->x()));
}
// Transforms the given coords all the way back to source image space using
// the full transformation sequence defined by this and its predecesors
// recursively, shallowest first, and finally any block re_rotation.
void DENORM::DenormTransform(const TPOINT& pt, TPOINT* original) const {
FCOORD src_pt(pt.x, pt.y);
FCOORD float_result;
DenormTransform(src_pt, &float_result);
original->x = IntCastRounded(float_result.x());
original->y = IntCastRounded(float_result.y());
}
void DENORM::DenormTransform(const FCOORD& pt, FCOORD* original) const {
LocalDenormTransform(pt, original);
if (predecessor_ != NULL) {
predecessor_->DenormTransform(*original, original);
} else if (block_ != NULL) {
original->rotate(block_->re_rotation());
}
}
// Normalize a blob using blob transformations. Less accurate, but
// more accurately copies the old way.
void DENORM::LocalNormBlob(TBLOB* blob) const {
TBOX blob_box = blob->bounding_box();
float x_center = (blob_box.left() + blob_box.right()) / 2.0f;
ICOORD translation(-IntCastRounded(x_origin_),
-IntCastRounded(YOriginAtOrigX(x_center)));
blob->Move(translation);
// Note that the old way of scaling only allowed for a single
// scale factor.
blob->Scale(YScaleAtOrigX(x_center));
if (rotation_ != NULL)
blob->Rotate(*rotation_);
translation.set_x(IntCastRounded(final_xshift_));
translation.set_y(IntCastRounded(final_yshift_));
blob->Move(translation);
}
// ============== Private Code ======================
// Free allocated memory and clear pointers.
void DENORM::Clear() {
if (segs_ != NULL) {
delete [] segs_;
segs_ = NULL;
num_segs_ = 0;
}
if (rotation_ != NULL) {
delete rotation_;
rotation_ = NULL;
}
}
// Setup default values.
void DENORM::Init() {
inverse_ = false;
pix_ = NULL;
block_ = NULL;
row_ = NULL;
rotation_ = NULL;
predecessor_ = NULL;
segs_ = NULL;
num_segs_ = 0;
x_origin_ = 0.0f;
y_origin_ = 0.0f;
x_scale_ = 1.0f;
y_scale_ = 1.0f;
final_xshift_ = 0.0f;
final_yshift_ = static_cast<float>(kBlnBaselineOffset);
}
// Returns the y-origin at the original (un-normalized) x.
float DENORM::YOriginAtOrigX(float orig_x) const {
if (num_segs_ > 0) {
const DENORM_SEG* seg = BinarySearchSegment(orig_x);
if (seg->ycoord != -MAX_INT32) {
return seg->ycoord;
}
}
if (row_ != NULL)
return row_->base_line(orig_x) + y_origin_;
else
return y_origin_;
}
// Returns the y-scale at the original (un-normalized) x.
float DENORM::YScaleAtOrigX(float orig_x) const {
if (num_segs_ > 0) {
const DENORM_SEG* seg = BinarySearchSegment(orig_x);
if (seg->scale_factor > 0.0)
return seg->scale_factor;
}
return y_scale_;
}
// Compare two segments by xstart for use with qsort(3) and bsearch(3)
static int CompareSegByXStart(const DENORM_SEG* a, const DENORM_SEG* b) {
if (a->xstart < b->xstart)
return -1;
else if (a->xstart > b->xstart)
return 1;
return 0;
}
// Deep copy the array of segments for use as a y_origin and y_scale.
void DENORM::SetSegments(const DENORM_SEG* new_segs, int seg_count) {
if (segs_ != NULL)
delete [] segs_;
if (seg_count > 0) {
segs_ = new DENORM_SEG[seg_count];
memcpy(segs_, new_segs, seg_count * sizeof(new_segs[0]));
// It is possible, if infrequent that the segments may be out of order.
// since we are searching with a binary search, keep them in order.
qsort(segs_, num_segs_, sizeof(segs_[0]),
reinterpret_cast<int(*)(const void*, const void*)>(
&CompareSegByXStart));
} else {
num_segs_ = 0;
segs_ = NULL;
}
}
// Finds the appropriate segment for a given original x-coord
const DENORM_SEG* DENORM::BinarySearchSegment(float orig_x) const {
int bottom, top, middle; // binary search
bottom = 0;
top = num_segs_;
do {
middle = (bottom + top) / 2;
if (segs_[middle].xstart > orig_x)
top = middle;
else
bottom = middle;
}
while (top - bottom > 1);
return &segs_[bottom];
}