/* GNU Ocrad - Optical Character Recognition program
Copyright (C) 2003, 2004, 2005, 2006, 2007 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 3 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 "common.h"
#include "rectangle.h"
#include "bitmap.h"
#include "blob.h"
namespace {
void delete_hole( std::vector< Bitmap * > & holep_vector,
std::vector< Bitmap * > & v1,
std::vector< Bitmap * > & v2, Bitmap * p, int i ) throw()
{
std::replace( v1.begin() + i, v1.end(), p, (Bitmap *) 0 );
std::replace( v2.begin(), v2.begin() + i, p, (Bitmap *) 0 );
i = holep_vector.size();
while( --i >= 0 && holep_vector[i] != p );
if( i < 0 ) Ocrad::internal_error( "delete_hole, lost hole." );
holep_vector.erase( holep_vector.begin() + i );
delete p;
}
inline void join_holes( std::vector< Bitmap * > & holep_vector,
std::vector< Bitmap * > & v1,
std::vector< Bitmap * > & v2,
Bitmap * p1, Bitmap * p2, int i ) throw()
{
if( p1->top() > p2->top() )
{
Bitmap *temp = p1; p1 = p2; p2 = temp;
std::replace( v2.begin(), v2.begin() + i + 1, p2, p1 );
}
else std::replace( v1.begin() + i, v1.end(), p2, p1 );
i = holep_vector.size();
while( --i >= 0 && holep_vector[i] != p2 );
if( i < 0 ) Ocrad::internal_error( "join_holes, lost hole" );
holep_vector.erase( holep_vector.begin() + i );
p1->add_bitmap( *p2 );
delete p2;
}
void delete_outer_holes( const Rectangle & re, std::vector< Bitmap * > & bpv ) throw()
{
for( int i = bpv.size() - 1; i >= 0; --i )
{
Bitmap & h = *bpv[i];
if( !re.strictly_includes( h ) )
{ delete &h; bpv.erase( bpv.begin() + i ); }
}
}
} // end namespace
Blob::Blob( const Blob & b ) throw()
: Bitmap( b ), bpv( b.bpv )
{
for( unsigned int i = 0; i < bpv.size(); ++i )
bpv[i] = new Bitmap( *b.bpv[i] );
}
Blob & Blob::operator=( const Blob & b ) throw()
{
if( this != &b )
{
Bitmap::operator=( b );
for( unsigned int i = 0; i < bpv.size(); ++i ) delete bpv[i];
bpv = b.bpv;
for( unsigned int i = 0; i < bpv.size(); ++i )
bpv[i] = new Bitmap( *b.bpv[i] );
}
return *this;
}
Blob::~Blob() throw()
{
for( unsigned int i = 0; i < bpv.size(); ++i ) delete bpv[i];
}
void Blob::left( const int l ) throw()
{
const int d = l - left();
if( d ) { Bitmap::left( l ); if( d > 0 ) delete_outer_holes( *this, bpv ); }
}
void Blob::top( const int t ) throw()
{
const int d = t - top();
if( d ) { Bitmap::top( t ); if( d > 0 ) delete_outer_holes( *this, bpv ); }
}
void Blob::right( const int r ) throw()
{
const int d = r - right();
if( d ) { Bitmap::right( r ); if( d < 0 ) delete_outer_holes( *this, bpv ); }
}
void Blob::bottom( const int b ) throw()
{
const int d = b - bottom();
if( d ) { Bitmap::bottom( b ); if( d < 0 ) delete_outer_holes( *this, bpv ); }
}
const Bitmap & Blob::hole( const int i ) const throw()
{
if( i < 0 || i >= holes() )
Ocrad::internal_error( "hole, index out of bounds" );
return *bpv[i];
}
int Blob::id( const int row, const int col ) const throw()
{
if( this->includes( row, col ) )
{
if( get_bit( row, col ) ) return 1;
for( int i = 0; i < holes(); ++i )
if( bpv[i]->includes( row, col ) && bpv[i]->get_bit( row, col ) )
return -( i + 1 );
}
return 0;
}
void Blob::print( FILE * outfile ) const throw()
{
for( int row = top(); row <= bottom(); ++row )
{
for( int col = left(); col <= right(); ++col )
{
if( get_bit( row, col ) ) std::fprintf( outfile, " O" );
else std::fprintf( outfile, " ·" );
}
std::fputs( "\n", outfile );
}
std::fputs( "\n", outfile );
}
void Blob::fill_hole( const int i ) throw()
{
if( i < 0 || i >= holes() )
Ocrad::internal_error( "fill_hole, index out of bounds" );
add_bitmap( *bpv[i] );
delete bpv[i];
bpv.erase( bpv.begin() + i );
}
void Blob::find_holes() throw()
{
for( unsigned int i = 0; i < bpv.size(); ++i ) delete bpv[i];
bpv.clear();
if( height() < 3 || width() < 3 ) return;
std::vector< Bitmap * > old_data( width(), (Bitmap *) 0 );
std::vector< Bitmap * > new_data( width(), (Bitmap *) 0 );
for( int row = top(); row <= bottom(); ++row )
{
old_data.swap( new_data );
new_data[0] = get_bit( row, left() ) ? this : 0;
for( int col = left() + 1; col < right(); ++col )
{
const int dcol = col - left();
if( get_bit( row, col ) ) new_data[dcol] = this; // black point
else // white point
{
Bitmap *p;
Bitmap *lp = new_data[dcol-1];
Bitmap *tp = old_data[dcol];
if( lp == 0 || tp == 0 )
{
p = 0;
if( lp && lp != this )
delete_hole( bpv, old_data, new_data, lp, dcol );
else if( tp && tp != this )
delete_hole( bpv, old_data, new_data, tp, dcol );
}
else if( lp != this ) { p = lp; p->add_point( row, col ); }
else if( tp != this ) { p = tp; p->add_point( row, col ); }
else
{
p = new Bitmap( col, row, col, row );
p->set_bit( row, col, true );
bpv.push_back( p );
}
new_data[dcol] = p;
if( p && lp != tp && lp != this && tp != this )
join_holes( bpv, old_data, new_data, lp, tp, dcol );
}
}
if( !get_bit( row, right() ) )
{
Bitmap *lp = new_data[width()-2];
if( lp && lp != this )
delete_hole( bpv, old_data, new_data, lp, width() - 1 );
}
}
for( int i = bpv.size() - 1; i >= 0; --i ) // FIXME noise holes removal
{
Bitmap & h = *bpv[i];
if( this->strictly_includes( h ) &&
( h.height() > 4 || h.width() > 4 ||
( ( h.height() > 2 || h.width() > 2 ) && h.area() > 3 ) ) )
continue;
delete &h; bpv.erase( bpv.begin() + i );
}
/*
for( int i = bpv.size() - 1; i >= 0; --i )
{
Bitmap & h = *bpv[i];
if( !this->strictly_includes( h ) )
{ delete &h; bpv.erase( bpv.begin() + i ); }
if( 20 * h.height() < height() && 16 * h.width() < width() ) fill_hole( i );
// else if( h.height() < 2 && h.width() < 2 && h.area() < 2 )
// { delete &h; bpv.erase( bpv.begin() + i ); }
}
*/
}