/* GNU Ocrad - Optical Character Recognition program
Copyright (C) 2003-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include "common.h"
#include "rectangle.h"
#include "track.h"
namespace {
void error( const char * const msg )
{ Ocrad::internal_error( msg ); }
int good_reference( const Rectangle & r1, const Rectangle & r2, int & val,
const int mean_height, const int mean_width )
{
if( 4 * r1.height() >= 3 * mean_height &&
4 * r2.height() >= 3 * mean_height &&
( r1.width() >= mean_width || r2.width() >= mean_width ) && val > 0 )
{
if( 4 * r1.height() <= 5 * mean_height &&
4 * r2.height() <= 5 * mean_height )
{
if( 9 * r1.height() <= 10 * mean_height &&
9 * r2.height() <= 10 * mean_height &&
10 * std::abs( r1.bottom() - r2.bottom() ) <= mean_height )
{ val = 0; return ( r1.height() <= r2.height() ) ? 0 : 1; }
if( val > 1 && 10 * std::abs( r1.vcenter() - r2.vcenter() ) <= mean_height )
{ val = 1; return ( r1.bottom() <= r2.bottom() ) ? 0 : 1; }
}
if( val > 2 && 10 * std::abs( r1.vcenter() - r2.vcenter() ) <= mean_height )
{ val = 2; return ( r1.bottom() <= r2.bottom() ) ? 0 : 1; }
}
return -1;
}
int set_l( const std::vector< Rectangle > & rectangle_vector,
const int mean_height, const int mean_width )
{
const int rectangles = rectangle_vector.size();
const int imax = rectangles / 4;
int ibest = -1, val = 3;
for( int i1 = 0; i1 < imax && val > 0; ++i1 )
for( int i2 = i1 + 1; i2 <= imax && i2 <= i1 + 2; ++i2 )
{
int i = good_reference( rectangle_vector[i1], rectangle_vector[i2],
val, mean_height, mean_width );
if( i >= 0 ) { ibest = (i == 0) ? i1 : i2; if( val == 0 ) break; }
}
return ibest;
}
int set_r( const std::vector< Rectangle > & rectangle_vector,
const int mean_height, const int mean_width )
{
const int rectangles = rectangle_vector.size();
const int imin = rectangles - 1 - ( rectangles / 4 );
int ibest = -1, val = 3;
for( int i1 = rectangles - 1; i1 > imin && val > 0; --i1 )
for( int i2 = i1 - 1; i2 >= imin && i2 >= i1 - 2; --i2 )
{
int i = good_reference( rectangle_vector[i1], rectangle_vector[i2],
val, mean_height, mean_width );
if( i >= 0 ) { ibest = (i == 0) ? i1 : i2; if( val == 0 ) break; }
}
return ibest;
}
Vrhomboid set_partial_track( const std::vector< Rectangle > & rectangle_vector )
{
const int rectangles = rectangle_vector.size();
int mean_vcenter = 0, mean_height = 0, mean_width = 0;
for( int i = 0; i < rectangles; ++i )
{
mean_vcenter += rectangle_vector[i].vcenter();
mean_height += rectangle_vector[i].height();
mean_width += rectangle_vector[i].width();
}
if( rectangles )
{ mean_vcenter /= rectangles; mean_height /= rectangles; mean_width /= rectangles; }
// short line
if( rectangles < 8 )
return Vrhomboid( rectangle_vector.front().left(), mean_vcenter,
rectangle_vector.back().right(), mean_vcenter,
mean_height );
// look for reference rectangles (characters)
int l = set_l( rectangle_vector, mean_height, mean_width );
int r = set_r( rectangle_vector, mean_height, mean_width );
int lcol, lvc, rcol, rvc;
if( l >= 0 )
{
lcol = rectangle_vector[l].hcenter();
lvc = rectangle_vector[l].bottom() - ( mean_height / 2 );
}
else { lcol = rectangle_vector.front().hcenter(); lvc = mean_vcenter; }
if( r >= 0 )
{
rcol = rectangle_vector[r].hcenter();
rvc = rectangle_vector[r].bottom() - ( mean_height / 2 );
}
else { rcol = rectangle_vector.back().hcenter(); rvc = mean_vcenter; }
Vrhomboid tmp( lcol, lvc, rcol, rvc, mean_height );
tmp.extend_left( rectangle_vector.front().left() );
tmp.extend_right( rectangle_vector.back().right() );
return tmp;
}
} // end namespace
Vrhomboid::Vrhomboid( const int l, const int lc, const int r, const int rc,
const int h )
{
if( r < l || h <= 0 )
{
if( verbosity >= 0 )
std::fprintf( stderr, "l = %d, lc = %d, r = %d, rc = %d, h = %d\n",
l, lc, r, rc, h );
error( "bad parameter building a Vrhomboid." );
}
left_ = l; lvcenter_ = lc; right_ = r; rvcenter_ = rc; height_ = h;
}
void Vrhomboid::left( const int l )
{
if( l > right_ ) error( "left, bad parameter resizing a Vrhomboid." );
left_ = l;
}
void Vrhomboid::right( const int r )
{
if( r < left_ ) error( "right, bad parameter resizing a Vrhomboid." );
right_ = r;
}
void Vrhomboid::height( const int h )
{
if( h <= 0 ) error( "height, bad parameter resizing a Vrhomboid." );
height_ = h;
}
void Vrhomboid::extend_left( const int l )
{
if( l > right_ )
error( "extend_left, bad parameter resizing a Vrhomboid." );
lvcenter_ = vcenter( l ); left_ = l;
}
void Vrhomboid::extend_right( const int r )
{
if( r < left_ )
error( "extend_right, bad parameter resizing a Vrhomboid." );
rvcenter_ = vcenter( r ); right_ = r;
}
int Vrhomboid::vcenter( const int col ) const
{
const int dx = right_ - left_, dy = rvcenter_ - lvcenter_;
int vc = lvcenter_;
if( dx && dy ) vc += ( dy * ( col - left_ ) ) / dx;
return vc;
}
bool Vrhomboid::includes( const Rectangle & r ) const
{
if( r.left() < left_ || r.right() > right_ ) return false;
const int tl = top( r.left() ), bl = bottom( r.left() );
const int tr = top( r.right() ), br = bottom( r.left() );
const int t = std::max( tl, tr ), b = std::min( bl, br );
return ( t <= r.top() && b >= r.bottom() );
}
bool Vrhomboid::includes( const int row, const int col ) const
{
if( col < left_ || col > right_ ) return false;
const int t = top( col ), b = bottom( col );
return ( t <= row && b >= row );
}
// rectangle_vector must be ordered by increasing hcenter().
//
void Track::set_track( const std::vector< Rectangle > & rectangle_vector )
{
data.clear();
if( rectangle_vector.empty() ) return;
std::vector< Rectangle > tmp;
int max_gap = 0;
bool last = false;
{
int s1 = rectangle_vector[0].width(), s2 = 0;
for( unsigned i = 1; i < rectangle_vector.size(); ++i )
{
s1 += rectangle_vector[i].width();
s2 += ( rectangle_vector[i].left() - rectangle_vector[i-1].right() );
}
max_gap = ( 5 * std::max( s1, s2 ) ) / rectangle_vector.size();
}
for( unsigned i = 0; i < rectangle_vector.size(); ++i )
{
const Rectangle & r1 = rectangle_vector[i];
tmp.push_back( r1 );
if( i + 1 >= rectangle_vector.size() ) last = true;
else
{
const Rectangle & r2 = rectangle_vector[i+1];
if( r2.left() - r1.right() >= max_gap ) last = true;
}
if( last )
{ last = false; data.push_back( set_partial_track( tmp ) ); tmp.clear(); }
}
for( unsigned i = 0; i + 1 < data.size(); ++i )
{
const Vrhomboid & v1 = data[i];
const Vrhomboid & v2 = data[i+1];
if( v1.right() + 1 < v2.left() )
{
Vrhomboid v( v1.right() + 1, v1.rvcenter(), v2.left() - 1, v2.lvcenter(),
( v1.height() + v2.height() ) / 2 );
++i; data.insert( data.begin() + i, v );
}
}
}
int Track::bottom( const int col ) const
{
for( unsigned i = 0; i < data.size(); ++i )
{
const Vrhomboid & vr = data[i];
if( col <= vr.right() || i + 1 >= data.size() ) return vr.bottom( col );
}
return 0;
}
int Track::top( const int col ) const
{
for( unsigned i = 0; i < data.size(); ++i )
{
const Vrhomboid & vr = data[i];
if( col <= vr.right() || i + 1 >= data.size() ) return vr.top( col );
}
return 0;
}
int Track::vcenter( const int col ) const
{
for( unsigned i = 0; i < data.size(); ++i )
{
const Vrhomboid & vr = data[i];
if( col <= vr.right() || i + 1 >= data.size() ) return vr.vcenter( col );
}
return 0;
}
/*
bool Track::includes( const Rectangle & r ) const
{
for( unsigned i = 0; i < data.size(); ++i )
if( data[i].includes( r ) ) return true;
if( data.empty() ) return false;
if( r.right() > data.back().right() )
{
Vrhomboid tmp = data.back();
tmp.extend_right( r.right() );
return tmp.includes( r );
}
if( r.left() < data.front().left() )
{
Vrhomboid tmp = data.front();
tmp.extend_left( r.left() );
return tmp.includes( r );
}
return false;
}
bool Track::includes( const int row, const int col ) const
{
for( unsigned i = 0; i < data.size(); ++i )
if( data[i].includes( row, col ) ) return true;
if( data.empty() ) return false;
if( col > data.back().right() )
{
Vrhomboid tmp = data.back();
tmp.extend_right( col );
return tmp.includes( row, col );
}
if( col < data.front().left() )
{
Vrhomboid tmp = data.front();
tmp.extend_left( col );
return tmp.includes( row, col );
}
return false;
}*/