feats.cc (ocrad-0.24) | : | feats.cc (ocrad-0.25) | ||
---|---|---|---|---|
/* GNU Ocrad - Optical Character Recognition program | /* GNU Ocrad - Optical Character Recognition program | |||
Copyright (C) 2003-2014 Antonio Diaz Diaz. | Copyright (C) 2003-2015 Antonio Diaz Diaz. | |||
This program is free software: you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 2 of the License, or | the Free Software Foundation, either version 2 of the License, or | |||
(at your option) any later version. | (at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | GNU General Public License for more details. | |||
skipping to change at line 40 | skipping to change at line 40 | |||
#include "profile.h" | #include "profile.h" | |||
#include "feats.h" | #include "feats.h" | |||
Features::Features( const Blob & b_ ) | Features::Features( const Blob & b_ ) | |||
: b( b_ ), hbar_initialized( false ), vbar_initialized( false ), | : b( b_ ), hbar_initialized( false ), vbar_initialized( false ), | |||
lp( b, Profile::left ), | lp( b, Profile::left ), | |||
tp( b, Profile::top ), rp( b, Profile::right ), bp( b, Profile::bottom ), | tp( b, Profile::top ), rp( b, Profile::right ), bp( b, Profile::bottom ), | |||
hp( b, Profile::height ), wp( b, Profile::width ) | hp( b, Profile::height ), wp( b, Profile::width ) | |||
{} | {} | |||
void Features::row_scan_init() const | ||||
{ | ||||
int l = -1; // begin of segment. -1 means no segment | ||||
row_scan.resize( b.height() ); | ||||
for( int row = b.top(); row <= b.bottom(); ++row ) | ||||
for( int col = b.left(); col <= b.right(); ++col ) | ||||
{ | ||||
bool black = b.get_bit( row, col ); | ||||
if( l < 0 && black ) l = col; // begin of segment | ||||
if( l >= 0 && ( !black || col == b.right() ) ) // end of segment | ||||
{ row_scan[row-b.top()].push_back( Csegment( l, col - !black ) ); | ||||
l = -1; } | ||||
} | ||||
} | ||||
void Features::col_scan_init() const | ||||
{ | ||||
int t = -1; // begin of segment. -1 means no segment | ||||
col_scan.resize( b.width() ); | ||||
for( int col = b.left(); col <= b.right(); ++col ) | ||||
for( int row = b.top(); row <= b.bottom(); ++row ) | ||||
{ | ||||
bool black = b.get_bit( row, col ); | ||||
if( t < 0 && black ) t = row; // begin of segment | ||||
if( t >= 0 && ( !black || row == b.bottom() ) ) // end of segment | ||||
{ col_scan[col-b.left()].push_back( Csegment( t, row - !black ) ); | ||||
t = -1; } | ||||
} | ||||
} | ||||
int Features::hbars() const | int Features::hbars() const | |||
{ | { | |||
if( !hbar_initialized ) | if( !hbar_initialized ) | |||
{ | { | |||
hbar_initialized = true; | hbar_initialized = true; | |||
const int limit = ( wp.max() + 1 ) / 2; | if( row_scan.empty() ) row_scan_init(); | |||
int state = 0, begin = 0, l = 0, r = 0; | std::vector< Csegment > segv; | |||
std::vector< int > count( b.height(), 0 ); | segv.reserve( b.height() ); | |||
for( int row = b.top(); row <= b.bottom(); ++row ) | for( unsigned i = 0; i < row_scan.size(); ++i ) | |||
{ | { | |||
int col, c = 0, lt = 0, rt = 0, x = 0; | if( row_scan[i].size() == 1 ) | |||
int & maxcount = count[row-b.top()]; | { segv.push_back( row_scan[i][0] ); continue; } | |||
for( col = b.left(); col <= b.right(); ++col ) | int maxsize = 0, jmax = -1; | |||
for( unsigned j = 0; j < row_scan[i].size(); ++j ) | ||||
{ | { | |||
if( b.get_bit( row, col ) ) | const int size = row_scan[i][j].size(); | |||
{ ++c; x = col; if( col < b.right() ) continue; } | if( maxsize < size ) { maxsize = size; jmax = j; } | |||
if( c > maxcount ) { maxcount = c; rt = x; lt = rt - c + 1; } | ||||
c = 0; | ||||
} | } | |||
if( jmax >= 0 ) segv.push_back( row_scan[i][jmax] ); | ||||
else segv.push_back( Csegment() ); | ||||
} | ||||
const int limit = ( wp.max() + 1 ) / 2; | ||||
int state = 0, begin = 0, l = 0, r = 0; | ||||
for( int i = 0; i < b.height(); ++i ) | ||||
{ | ||||
Csegment & seg = segv[i]; | ||||
switch( state ) | switch( state ) | |||
{ | { | |||
case 0: if( maxcount > limit ) | case 0: if( seg.size() <= limit ) break; | |||
{ state = 1; begin = row; l = lt; r = rt; } | state = 1; begin = i; l = seg.left; r = seg.right; | |||
else break; | if( i < b.height() - 1 ) break; | |||
case 1: if( maxcount > limit ) | case 1: if( seg.size() > limit && | |||
( i <= begin || seg.overlaps( segv[i-1] ) ) ) | ||||
{ | { | |||
if( lt < l ) l = lt; | if( seg.left < l ) l = seg.left; | |||
if( rt > r ) r = rt; | if( seg.right > r ) r = seg.right; | |||
if( row < b.bottom() ) break; | if( i < b.height() - 1 ) break; | |||
} | } | |||
state = 0; | state = 0; | |||
int end = ( maxcount <= limit ) ? row - 1 : row; | int end = ( seg.size() <= limit ) ? i - 1 : i; | |||
const int width = r - l + 1; | const int width = r - l + 1; | |||
while( begin <= end && 3 * count[begin-b.top()] < 2 * width ) | while( begin <= end && 3 * segv[begin].size() < 2 * width ) | |||
++begin; | ++begin; | |||
while( begin <= end && 3 * count[end-b.top()] < 2 * width ) | while( begin <= end && 3 * segv[end].size() < 2 * width ) | |||
--end; | --end; | |||
const int height = end - begin + 1; | const int height = end - begin + 1; | |||
if( height < 1 || 2 * height > 3 * width ) break; | if( height < 1 || height > width ) break; | |||
hbar_.push_back( Rectangle( l, begin, r, end ) ); | const int margin = std::max( height, ( b.height() / 10 ) + 1 ); | |||
if( begin >= margin ) | ||||
{ | ||||
bool good = false; | ||||
for( int j = margin; j > 0; --j ) | ||||
if( 3 * segv[begin-j].size() <= 2 * width ) | ||||
{ good = true; break; } | ||||
if( !good ) break; | ||||
} | ||||
if( end + margin < b.height() ) | ||||
{ | ||||
bool good = false; | ||||
for( int j = margin; j > 0; --j ) | ||||
if( 3 * segv[end+j].size() <= 2 * width ) | ||||
{ good = true; break; } | ||||
if( !good ) break; | ||||
} | ||||
hbar_.push_back( Rectangle( l, begin+b.top(), r, end+b.top() ) ) | ||||
; | ||||
break; | break; | |||
} | } | |||
} | } | |||
while( hbar_.size() > 3 ) // remove noise hbars | while( hbar_.size() > 3 ) // remove noise hbars | |||
{ | { | |||
int wmin = hbar_[0].width(); | int wmin = hbar_[0].width(); | |||
for( unsigned i = 1; i < hbar_.size(); ++i ) | for( unsigned i = 1; i < hbar_.size(); ++i ) | |||
if( hbar_[i].width() < wmin ) wmin = hbar_[i].width(); | if( hbar_[i].width() < wmin ) wmin = hbar_[i].width(); | |||
for( int i = hbar_.size() - 1; i >= 0; --i ) | for( int i = hbar_.size() - 1; i >= 0; --i ) | |||
if( hbar_[i].width() == wmin ) hbar_.erase( hbar_.begin() + i ); | if( hbar_[i].width() == wmin ) hbar_.erase( hbar_.begin() + i ); | |||
skipping to change at line 144 | skipping to change at line 202 | |||
int end = ( count * 3 < limit * 2 ) ? col - 1 : col; | int end = ( count * 3 < limit * 2 ) ? col - 1 : col; | |||
vbar_.push_back( Rectangle( begin, b.top(), end, b.bottom() ) ); | vbar_.push_back( Rectangle( begin, b.top(), end, b.bottom() ) ); | |||
state = 0; | state = 0; | |||
} | } | |||
} | } | |||
} | } | |||
} | } | |||
return vbar_.size(); | return vbar_.size(); | |||
} | } | |||
// return the number of vertical traces crossing every row | Csegment Features::v_segment( const int row, const int col ) const | |||
// | ||||
int Features::segments_in_row( const int row ) const | ||||
{ | ||||
if( row_scan.empty() ) | ||||
{ | ||||
int l = -1; // begin of segment. -1 means no segment | ||||
row_scan.resize( b.height() ); | ||||
for( int row = b.top(); row <= b.bottom(); ++row ) | ||||
for( int col = b.left(); col <= b.right(); ++col ) | ||||
{ | ||||
bool black = b.get_bit( row, col ); | ||||
if( l < 0 && black ) l = col; // begin of segment | ||||
if( l >= 0 && ( !black || col == b.right() ) ) // end of segment | ||||
{ row_scan[row-b.top()].push_back( Csegment( l, col - !black ) ); | ||||
l = -1; } | ||||
} | ||||
} | ||||
return row_scan[row-b.top()].size(); | ||||
} | ||||
// return the number of horizontal traces crossing every column | ||||
// | ||||
int Features::segments_in_col( const int col ) const | ||||
{ | ||||
if( col_scan.empty() ) | ||||
{ | ||||
int t = -1; // begin of segment. -1 means no segment | ||||
col_scan.resize( b.width() ); | ||||
for( int col = b.left(); col <= b.right(); ++col ) | ||||
for( int row = b.top(); row <= b.bottom(); ++row ) | ||||
{ | ||||
bool black = b.get_bit( row, col ); | ||||
if( t < 0 && black ) t = row; // begin of segment | ||||
if( t >= 0 && ( !black || row == b.bottom() ) ) // end of segment | ||||
{ col_scan[col-b.left()].push_back( Csegment( t, row - !black ) ); | ||||
t = -1; } | ||||
} | ||||
} | ||||
return col_scan[col-b.left()].size(); | ||||
} | ||||
// return the column segment containing the point (row,col) if any | ||||
// | ||||
Csegment Features::col_segment( const int row, const int col ) const | ||||
{ | { | |||
const int segments = segments_in_col( col ); | const int segments = segments_in_col( col ); | |||
for( int i = 0; i < segments; ++i ) | for( int i = 0; i < segments; ++i ) | |||
if( col_scan[col-b.left()][i].includes( row ) ) | if( col_scan[col-b.left()][i].includes( row ) ) | |||
return col_scan[col-b.left()][i]; | return col_scan[col-b.left()][i]; | |||
return Csegment(); | return Csegment(); | |||
} | } | |||
int Features::test_misc( const Rectangle & charbox ) const | int Features::test_misc( const Rectangle & charbox ) const | |||
{ | { | |||
End of changes. 14 change blocks. | ||||
71 lines changed or deleted | 84 lines changed or added |