Logo Search packages:      
Sourcecode: ocrad version File versions  Download package

bitmap.cc

/*  GNU Ocrad - Optical Character Recognition program
    Copyright (C) 2003, 2004, 2005 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, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <vector>

#include "common.h"
#include "rectangle.h"
#include "bitmap.h"


namespace {

void enlarge_2( std::vector< std::vector< char > > & data ) throw()
  {
  const int height = data.size(), width = data[0].size();
  std::vector< std::vector< char > > new_data( 2 * height );

  for( unsigned int row = 0; row < new_data.size(); ++row )
    new_data[row].resize( 2 * width, false );

  for( int row = 0; row < height; ++row )
    {
    const std::vector< char > & datarow = data[row];
    std::vector< char > & new_datarow0 = new_data[2*row];
    std::vector< char > & new_datarow1 = new_data[2*row+1];
    for( int col = 0; col < width; ++col )
      if( datarow[col] )
        {
        const bool l = col > 0 && datarow[col-1];
        const bool t = row > 0 && data[row-1][col];
        const bool r = col < width - 1 && datarow[col+1];
        const bool b = row < height - 1 && data[row+1][col];
        const bool lt = row > 0 && col > 0 && data[row-1][col-1];
        const bool rt = row > 0 && col < width - 1 && data[row-1][col+1];
        const bool lb = row < height - 1 && col > 0 && data[row+1][col-1];
        const bool rb = row < height - 1 && col < width - 1 && data[row+1][col+1];

        if( l || t || lt || ( !rt && !lb ) ) new_datarow0[2*col] = true;
        if( r || t || rt || ( !lt && !rb ) ) new_datarow0[2*col+1] = true;
        if( l || b || lb || ( !lt && !rb ) ) new_datarow1[2*col] = true;
        if( r || b || rb || ( !rt && !lb ) ) new_datarow1[2*col+1] = true;
        }
    }
  data.swap( new_data );
  }


void enlarge_3( std::vector< std::vector< char > > & data ) throw()
  {
  const int height = data.size(), width = data[0].size();
  std::vector< std::vector< char > > new_data( 3 * height );

  for( unsigned int row = 0; row < new_data.size(); ++row )
    new_data[row].resize( 3 * width, false );

  for( int row = 0; row < height; ++row )
    {
    const int row3 = 3 * row;
    const std::vector< char > & datarow = data[row];
    std::vector< char > & new_datarow0 = new_data[row3];
    std::vector< char > & new_datarow1 = new_data[row3+1];
    std::vector< char > & new_datarow2 = new_data[row3+2];
    for( int col = 0; col < width; ++col )
      {
      const int col3 = 3 * col;
      const bool l = col > 0 && datarow[col-1];
      const bool t = row > 0 && data[row-1][col];
      const bool r = col < width - 1 && datarow[col+1];
      const bool b = row < height - 1 && data[row+1][col];
      const bool lt = row > 0 && col > 0 && data[row-1][col-1];
      const bool rt = row > 0 && col < width - 1 && data[row-1][col+1];
      const bool lb = row < height - 1 && col > 0 && data[row+1][col-1];
      const bool rb = row < height - 1 && col < width - 1 && data[row+1][col+1];
      if( datarow[col] )
        {
        if( l || t || lt || ( !rt && !lb ) ) new_datarow0[col3] = true;
        new_datarow0[col3+1] = true;
        if( r || t || rt || ( !lt && !rb ) ) new_datarow0[col3+2] = true;
        new_datarow1[col3] = new_datarow1[col3+1] = new_datarow1[col3+2] = true;
        if( l || b || lb || ( !lt && !rb ) ) new_datarow2[col3] = true;
        new_datarow2[col3+1] = true;
        if( r || b || rb || ( !rt && !lb ) ) new_datarow2[col3+2] = true;
        }
      else
        {
        if( l && t && lt && ( !rt || !lb ) ) new_datarow0[col3] = true;
        if( r && t && rt && ( !lt || !rb ) ) new_datarow0[col3+2] = true;
        if( l && b && lb && ( !lt || !rb ) ) new_datarow2[col3] = true;
        if( r && b && rb && ( !rt || !lb ) ) new_datarow2[col3+2] = true;
        }
      }
    }
  data.swap( new_data );
  }


void enlarge_n( std::vector< std::vector< char > > & data, const int n ) throw()
  {
  if( n < 2 ) return;
  const int height = data.size(), width = data[0].size();
  std::vector< std::vector< char > > new_data;
  new_data.reserve( n * height );

  for( int row = 0; row < height; ++row )
    {
    const std::vector< char > & datarow = data[row];
    new_data.push_back( std::vector< char >() );
    for( int col = 0; col < width; ++col )
      {
      bool d = datarow[col];
      for( int i = 0; i < n; ++i ) new_data.back().push_back( d );
      }
    for( int i = 1; i < n; ++i ) new_data.push_back( new_data.back() );
    }
  data.swap( new_data );
  }


void mirror_left_right( std::vector< std::vector< char > > & data ) throw()
  {
  const int height = data.size();
  for( int row = 0; row < height; ++row )
    std::reverse( data[row].begin(), data[row].end() );
  }


void mirror_top_bottom( std::vector< std::vector< char > > & data ) throw()
  {
  for( int u = 0, d = data.size() - 1; u < d; ++u, --d )
    data[u].swap( data[d] );
  }


void mirror_diagonal( std::vector< std::vector< char > > & data,
                      Rectangle & re ) throw()
  {
  const int size = std::max( re.height(), re.width() );

  if( re.height() < size )
    {
    data.resize( size );
    for( int row = re.height(); row < size; ++row )
      data[row].resize( size );
    }
  else if( re.width() < size )
    for( int row = 0; row < re.height(); ++row )
      data[row].resize( size );

  for( int row = 0; row < size; ++row )
    {
    std::vector< char > & datarow = data[row];
    for( int col = 0; col < row; ++col )
      {
      bool b = datarow[col];
      datarow[col] = data[col][row]; data[col][row] = b;
      }
    }

  const int h = re.height(), w = re.width();
  re.height( w ); re.width( h );
  if( re.height() < size ) data.resize( re.height() );
  else if( re.width() < size )
    for( int row = 0; row < re.height(); ++row )
      data[row].resize( re.width() );
  }

} // end namespace


// Creates a blank Bitmap
//
Bitmap::Bitmap( const int l, const int t, const int r, const int b ) throw()
  : Rectangle( l, t, r, b ), data( height() )
  {
  for( int row = 0; row < height(); ++row )
    data[row].resize( width(), false );
  }


// Creates a Bitmap from part of another Bitmap
//
Bitmap::Bitmap( const Bitmap & source, const Rectangle & re ) throw()
  : Rectangle( re ), data( re.height() )
  {
  if( !source.includes( re ) )
    Ocrad::internal_error( "bad parameter building a Bitmap from part of another one" );

  const int ldiff = left()-source.left();
  const int tdiff = top()-source.top();

  for( int row = 0; row < height(); ++row )
    {
    data[row].resize( width() );
    std::vector< char > & datarow = data[row];
    const std::vector< char > & datarow2 = source.data[row+tdiff];
    for( int col = 0; col < width(); ++col )
      datarow[col] = datarow2[col+ldiff];
    }
  }


void Bitmap::left( const int l ) throw()
  {
  if( l == left() ) return;
  if( l < left() )
    for( int row = height() - 1; row >= 0 ; --row )
      data[row].insert( data[row].begin(), left() - l, false );
  else
    for( int row = height() - 1; row >= 0 ; --row )
      data[row].erase( data[row].begin(), data[row].begin() + l - left() );
  Rectangle::left( l );
  }


void Bitmap::top( const int t ) throw()
  {
  if( t == top() ) return;
  if( t < top() )
    data.insert( data.begin(), top() - t, std::vector< char >( width(), false ) );
  else
    data.erase( data.begin(), data.begin() + t - top() );
  Rectangle::top( t );
  }


void Bitmap::right( const int r ) throw()
  {
  if( r == right() ) return;
  Rectangle::right( r );
  for( int row = height() - 1; row >= 0 ; --row )
    data[row].resize( width(), false );
  }


void Bitmap::bottom( const int b ) throw()
  {
  if( b == bottom() ) return;
  int old_height = height();
  Rectangle::bottom( b );
  data.resize( height() );
  for( int row = old_height; row < height(); ++row )
    data[row].resize( width(), false );
  }


void Bitmap::add_bitmap( const Bitmap & bm ) throw()
  {
  add_rectangle( bm );
  for( int row = bm.top(); row <= bm.bottom(); ++row )
    for( int col = bm.left(); col <= bm.right(); ++col )
      if( bm.get_bit( row, col ) ) set_bit( row, col, true );
  }


void Bitmap::add_point( const int row, const int col ) throw()
  {
  if( col > right() ) right( col ); else if( col < left() ) left( col );
  if( row > bottom() ) bottom( row ); else if( row < top() ) top( row );
  set_bit( row, col, true );
  }


void Bitmap::add_rectangle( const Rectangle & re ) throw()
  {
  if( re.left() < left() )     left( re.left() );
  if( re.top() < top() )       top( re.top() );
  if( re.right() > right() )   right( re.right() );
  if( re.bottom() > bottom() ) bottom( re.bottom() );
  }


// Return the total filled area of this Bitmap
//
int Bitmap::area() const throw()
  {
  int a = 0;

  for( int row = top(); row <= bottom(); ++row )
    for( int col = left(); col <= right(); ++col )
      if( get_bit( row, col ) ) ++a;

  return a;
  }


// Return the central octagon filled area of this Bitmap
//
int Bitmap::area_octagon() const throw()
  {
  int a = 0;
  int bevel = ( 29 * std::min( height(), width() ) ) / 100;
  int l =  left() + bevel;
  int r = right() - bevel;

  for( int i = 0; i < bevel; ++i )
    for( int row = top() + i, col = l - i; col <= r + i; ++col )
      if( get_bit( row, col ) ) ++a;

  for( int row = top() + bevel; row <= bottom() - bevel; ++row )
    for( int col = left(); col <= right(); ++col )
      if( get_bit( row, col ) ) ++a;

  for( int i = bevel - 1; i >= 0; --i )
    for( int row = bottom() - i, col = l - i; col <= r + i; ++col )
      if( get_bit( row, col ) ) ++a;

  return a;
  }


// Return the size of the central octagon of this block
//
int Bitmap::size_octagon() const throw()
  {
  int bevel = ( 29 * std::min( height(), width() ) ) / 100;
  return size() - ( 2 * bevel * ( bevel + 1 ) );
  }


int Bitmap::seek_left( const int row, const int col, const bool black ) const throw()
  {
  int c = col;
  while( c > left() && get_bit( row, c - 1 ) != black ) --c;
  return c;
  }


int Bitmap::seek_top( const int row, const int col, const bool black ) const throw()
  {
  int r = row;
  while( r > top() && get_bit( r - 1, col ) != black ) --r;
  return r;
  }


int Bitmap::seek_right( const int row, const int col, const bool black ) const throw()
  {
  int c = col;
  while( c < right() && get_bit( row, c + 1 ) != black ) ++c;
  return c;
  }


int Bitmap::seek_bottom( const int row, const int col, const bool black ) const throw()
  {
  int r = row;
  while( r < bottom() && get_bit( r + 1, col ) != black ) ++r;
  return r;
  }


bool Bitmap::scale( int n, const Rational & th ) throw( Bitmap::Error )
  {
  if( n <= -2 )
    { Bitmap reduced( *this, -n, th ); *this = reduced; return true; }
  if( n >= 2 )
    {
    if( (long long)width() * height() * n > (long long)INT_MAX )
      throw Error( "scale factor too big. `int' will overflow." );
    if( n && ( n % 2 ) == 0 ) { enlarge_2( data ); n /= 2; }
    else if( n && ( n % 3 ) == 0 ) { enlarge_3( data ); n /= 3; }
    if( n > 1 ) enlarge_n( data, n );
    Rectangle::height( data.size() );
    Rectangle::width( data[0].size() );
    return true;
    }
  return false;
  }


void Bitmap::transform( const Transformation & t ) throw()
  {
  switch( t.type() )
    {
    case Transformation::none:
      break;
    case Transformation::rotate90:
      mirror_diagonal( data, *this ); mirror_top_bottom( data ); break;
    case Transformation::rotate180:
      mirror_left_right( data ); mirror_top_bottom( data ); break;
    case Transformation::rotate270:
      mirror_diagonal( data, *this ); mirror_left_right( data ); break;
    case Transformation::mirror_lr:
      mirror_left_right( data ); break;
    case Transformation::mirror_tb:
      mirror_top_bottom( data ); break;
    case Transformation::mirror_d1:
      mirror_diagonal( data, *this ); break;
    case Transformation::mirror_d2:
      mirror_diagonal( data, *this );
      mirror_left_right( data ); mirror_top_bottom( data ); break;
    }
  }

Generated by  Doxygen 1.6.0   Back to index