/* 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
#include "common.h"
#include "rectangle.h"
#include "segment.h"
#include "ucs.h"
#include "bitmap.h"
#include "blob.h"
#include "profile.h"
#include "feats.h"
// Tests if the lower half of character is open to the left, to the right,
// and/or to the bottom
//
int Features::test_49ARegpq( const Rectangle & charbox ) const
{
const Bitmap & h = b.hole( 0 );
if( bp.minima( b.height() / 10 + 1 ) == 2 && bp.isctip() && tp.minima() == 1 )
{
if( tp.isvpit() || rp.decreasing() ||
( rp.decreasing( 1, rp.pos( 20 ) ) && lp.decreasing( 1, lp.pos( 20 ) ) ) )
return 'A';
if( hbars() == 2 && hbar(1).width() >= b.width() )
{
const int i = hbar(1).top() - b.top();
const int j = hbar(1).bottom() - b.top();
if( rp.area( i, j ) <= lp.area( i, j ) ) return 'A';
}
return 'R';
}
int col = h.hcenter();
int row = b.seek_bottom( h.bottom(), col, false ) + 1;
if( row >= b.vpos( 90 ) )
{ col = h.left(); row = b.seek_bottom( h.bottom(), col, false ) + 1; }
if( row >= b.bottom() ) return 0;
if( b.escape_right( row, col ) )
{
if( ( lp.ispit() && b.seek_bottom( row, h.right() ) < b.bottom() ) ||
( lp.isconvex() && b.seek_bottom( row, h.hcenter() ) < b.bottom() ) )
return 'e';
if( bp.ispit() )
{
int row2 = b.seek_bottom( row, h.right() );
if( row2 < b.vpos( 75 ) ) return 'g';
if( row2 < b.bottom() ) return 'e';
}
return 'p';
}
else if( b.escape_left( row, col ) )
{
Profile hlp( h, Profile::left );
Profile htp( h, Profile::top );
Profile hwp( h, Profile::width );
if( vbars() == 1 && vbar(0).hcenter() > b.hcenter() &&
hlp.decreasing() && htp.decreasing() &&
hwp[hwp.pos(30)] < hwp[hwp.pos(70)] )
return '4';
if( rp.ispit() && rp.minima() == 1 && rp.iminimum() < rp.pos( 70 ) &&
tp.ispit() && charbox.bottom() > b.vpos( rp.isconvex() ? 80 : 90 ) )
return '9';
int hdiff;
if( b.bottom_hook( &hdiff ) && hdiff > 0 )
{
if( h.bottom() < b.vcenter() && h.right() + 2 <= b.right() &&
( !b.get_bit( h.bottom() + 1, h.right() + 1 ) ||
!b.get_bit( h.bottom() + 1, h.right() + 2 ) || rp.isctip() ) )
return 's';
else return 'g';
}
if( row > b.vpos( 85 ) && tp.ispit() ) return 'Q';
int row2 = b.seek_bottom( row, col );
if( row2 < b.bottom() &&
rp.increasing( ( ( row + ( 2 * row2 ) ) / 3 ) - b.top() ) )
return 'g';
if( bp.minima() == 1 )
{
if( h.height() >= charbox.height() ) return 'Q';
if( h.right() < b.hcenter() && h.bottom() < b.vcenter() ) return '2';
return 'q';
}
}
return 0;
}
int Features::test_4ADQao( const Charset & charset, const Rectangle & charbox ) const
{
const Bitmap & h = b.hole( 0 );
int left_delta = h.left() - b.left(), right_delta = b.right() - h.right();
if( !lp.ispit() && lp.isflats() && rp.ispit() ) return 'D';
if( Ocrad::similar( left_delta, right_delta, 40 ) &&
tp.minima() == 2 && bp.minima() == 2 && !rp.isconvex() ) return '#';
if( tp.minima() == 1 && bp.minima() == 1 )
{
int row = b.seek_bottom( h.bottom(), h.hcenter(), false );
if( charset.enabled( Charset::iso_8859_15 ) ||
charset.enabled( Charset::iso_8859_9 ) )
if( !lp.isconvex() && bp.isconvex() && !rp.isconvex() &&
b.seek_bottom( row, h.hcenter() ) < b.bottom() )
return UCS::SEACUTE;
row = ( row + b.seek_bottom( row, h.hcenter() ) ) / 2;
if( row < b.bottom() - 1 && !lp.isflats() &&
b.seek_left( row, h.hcenter() ) <= b.left() )
{
if( ( 2 * h.height() <= b.height() || 2 * h.width() <= b.width() ) &&
wp[h.top()-b.top()] < wp[h.bottom()-b.top()] ) return '4';
if( !rp.ispit() && !rp.isconvex() ) return 'Q';
}
}
if( 2 * b.width() > 5 * h.width() && !rp.isconvex() )
{
const int c = segments_in_row( h.vcenter() );
const int m = bp.minima();
if( c == 3 && h.top() < b.vcenter() && h.bottom() > b.vcenter() &&
3 * h.height() >= b.height() && ( m == 3 || m == 2 ) && !lp.ispit() )
return 'm';
if( c == 3 && left_delta > right_delta && lp.ispit() &&
segments_in_col( h.hcenter() ) == 4 )
return '@';
if( c == 4 && Ocrad::similar( left_delta, right_delta, 40 ) && lp.ispit() )
return '@';
}
if( tp.minima() == 1 && bp.istip() && !rp.isctip( 66 ) ) return 'A';
if( Ocrad::similar( left_delta, right_delta, 50 ) )
{
if( bp.minima() == 1 && rp.isconvex() && b.test_BD() ) return 'D';
if( bp.minima() > 1 || rp.minima() > 1 || b.test_Q() )
{ if( 4 * h.size() >= b.size() || tp.ispit() || lp.ispit() ) return 'Q';
else return 0; }
if( 3 * bp[bp.pos(100)] < b.height() && 5 * rp[rp.pos(55)] >= b.width() )
return 'a';
if( lp.istip() ) return 'n';
if( b.vpos( 80 ) < charbox.vcenter() ) return UCS::DEG;
return 'o';
}
if( left_delta > right_delta && rp.ispit() &&
tp.minima() == 1 && bp.minima() == 1 ) return 'D';
if( Ocrad::similar( left_delta, right_delta, 50 ) &&
( bp.minima() > 1 || rp.minima() > 1 ) ) return 'a';
return 0;
}
// Tests if the upper half of character is open to the left, to the right,
// and/or to the bottom
//
int Features::test_6abd( const Charset & charset ) const
{
const Bitmap & h = b.hole( 0 );
if( 3 * h.width() < b.width() &&
( bp.minima( b.height() / 4 ) != 1 || tp.minima( h.vcenter() - b.top() ) != 1 ) ) return 0;
int col = h.hcenter();
int row = b.seek_top( h.top(), col, false ) - 1;
if( row <= b.top() )
{
col = h.right(); if( b.right() - h.right() > h.width() ) ++col;
row = b.seek_top( h.top(), col, false ) - 1;
}
if( row <= b.top() ) return 0;
const int rcol = ( b.right() + h.right() ) / 2;
const int urow = h.top() - ( b.bottom() - h.bottom() );
const bool oacute1 = ( ( b.seek_right( urow - 1, h.right() ) >= b.right() ) ||
( b.seek_right( row, col ) >= b.right() ) );
if( b.escape_right( row, col ) )
{
const int noise = ( b.width() / 30 ) + 1;
const int c = lp[urow-b.top()];
const bool oacute2 = ( c > lp[h.top()-b.top()] + noise &&
urow <= b.top() + tp[std::min( c - 1, b.width() / 4 )] );
if( ( oacute1 && oacute2 ) && ( charset.enabled( Charset::iso_8859_15 ) ||
charset.enabled( Charset::iso_8859_9 ) ) )
{
const bool oacute3 = ( b.right() - rp[rp.pos(5)] >= h.right() ||
b.left() + lp[h.top()-b.top()] <= b.hpos( 5 ) );
if( oacute3 ) return UCS::SOACUTE;
}
if( !oacute2 && lp.ispit() && bp.ispit() )
{
int row2 = b.seek_top( h.top(), h.right() + 1, false ) - 1;
row2 = b.seek_top( row2, h.right() + 1 );
if( row2 > b.top() ) return '6';
}
int row2 = b.seek_top( h.top(), rcol, false ) - 1;
row2 = b.seek_top( row2, rcol );
if( row2 <= b.top() ) return 'b';
const int m = tp.minima( b.height() / 2 );
if( m == 1 && bp.minima() == 1 ) return 's';
if( m == 2 ) return 'k'; else return 0;
}
if( b.escape_left( row, col ) )
{
const int col2 = std::max( h.left(), h.hpos( 10 ) );
int row2 = b.seek_top( h.top(), col2, false ) - 1;
row2 = b.seek_top( row2, col2 );
if( row2 > b.top() )
{
if( charset.enabled( Charset::iso_8859_15 ) ||
charset.enabled( Charset::iso_8859_9 ) )
{
int row3 = b.seek_top( row, col );
if( row > b.vcenter() && row3 > b.vpos( 20 ) ) return UCS::SAACUTE;
if( oacute1 ) return UCS::SOGRAVE;
}
return 'a';
}
if( charset.enabled( Charset::iso_8859_15 ) ||
charset.enabled( Charset::iso_8859_9 ) )
if( oacute1 ) return UCS::SOACUTE;
return 'd';
}
if( b.width() > 3 * h.width() && h.top() < b.vcenter() &&
segments_in_row( b.vcenter() ) == 3 && !lp.isconvex() ) return 'm';
int hdiff; if( b.top_hook( &hdiff ) && hdiff > 0 ) return 's';
return 0;
}