void Preprocessor::correctSlanting () { boost::timer timer; timer.restart(); const double PI = 3.1415926535; for ( std::vector<Pattern>::iterator i = patterns_.begin(); i != patterns_.end(); ++i ) { const unsigned int rotationLimit = 20; double shearingFactor;; // This vector stores the greatest number of ink pixels counted in any column of a rotated pattern. std::vector<unsigned int> columnPixelMaximalCounts(rotationLimit); // Rotate the pattern and count the number of ink pixels per column for ( double angle = 0; angle < rotationLimit; ++angle ) { std::map<unsigned int, std::set<unsigned int> > pixelsPerColumn; shearingFactor = tan(-angle * PI / 180.0); // Store the columns of rotated pixels for ( unsigned int j = 0; j < i->height(); ++j ) { for ( unsigned int k = 0; k < i->width(); ++k ) { if ( i->at(j,k) == 1 ) // Ink pixel { double jp = j; double kp = k; kp = round(kp - jp * shearingFactor); if ( kp < i->width() && kp > 0 ) pixelsPerColumn[kp].insert(j); } } } unsigned int columnPixelMaximalCount = 0; for ( std::map<unsigned int, std::set<unsigned int> >::iterator row = pixelsPerColumn.begin(); row != pixelsPerColumn.end(); ++row ) { if ( row->second.size() > columnPixelMaximalCount ) columnPixelMaximalCount = row->second.size(); } columnPixelMaximalCounts.at(angle) = columnPixelMaximalCount; } // Get the angle to rotate selecting the rotation that generates the maximal number of pixels in a column double targetAngle = distance( columnPixelMaximalCounts.begin(), std::max_element(columnPixelMaximalCounts.begin(), columnPixelMaximalCounts.end()) ); shearingFactor = tan(-targetAngle * PI / 180.0); // Correct slanting of the actual pattern if ( targetAngle != 0 ) { Pattern rotatedPattern; rotatedPattern.clean(); for ( unsigned int j = 0; j < i->height(); ++j ) { for ( unsigned int k = 0; k < i->width(); ++k ) { if ( i->at(j,k) == 1 ) { double jp = j; double kp = k; kp = round(kp - jp * shearingFactor); if ( kp < i->width() && kp > 0 ) rotatedPattern.at(jp, kp) = 1; } } } *i = rotatedPattern; } } statistics_.slantingCorrectionTime(timer.elapsed());; }
void Preprocessor::buildPatterns () { boost::timer timer; timer.restart(); patterns_.clear(); patterns_.reserve(regions_.size()); Pattern pattern; // Traverse the list of iterators to regions creating a Pattern object for each region for( RegionLines::iterator k = inlineRegions_.begin(); k != inlineRegions_.end(); ++k ) { std::list<RegionIterator> line(k->second); for( std::list<RegionIterator>::iterator i = line.begin(); i != line.end(); ++i ) { (*i)->normalizeCoordinates(); // Normalize region using Magick++ facilities Magick::Image image( Magick::Geometry((*i)->width(), (*i)->height()), Magick::ColorGray(1.0) ); image.type( Magick::BilevelType ); Magick::Pixels view(image); Magick::PixelPacket *originPixel = view.get(0, 0, (*i)->width(), (*i)->height()); Magick::PixelPacket *pixel; for ( unsigned int j = 0; j < (*i)->size(); ++j ) { pixel = originPixel + ((*i)->at(j).first * view.columns() + (*i)->at(j).second); *pixel = Magick::ColorGray (0.0); } view.sync(); image.syncPixels(); image.scale( Magick::Geometry(Pattern::planeSize(), Pattern::planeSize()) ); // Preprocess the normalized region Preprocessor temporalPreprocessor (image, 0, 0, image.rows(), image.columns()); temporalPreprocessor.applyGlobalThresholding(); temporalPreprocessor.isolateRegions(); Region normalizedRegion; if ( ! temporalPreprocessor.regions_.empty() ) { // Merge subregions if preprocessing split the original region if ( temporalPreprocessor.regions_.size() > 1 ) { for ( RegionIterator j = temporalPreprocessor.regions_.begin(); j != temporalPreprocessor.regions_.end(); ++j ) normalizedRegion = normalizedRegion + *j; temporalPreprocessor.regions_.clear(); temporalPreprocessor.regions_.push_back(normalizedRegion); } else normalizedRegion = temporalPreprocessor.regions_.front(); } // Build the pattern pattern.clean(); for ( unsigned int i = 0; i < normalizedRegion.size(); ++i ) pattern.at(normalizedRegion.at(i).first, normalizedRegion.at(i).second) = 1; // Correct shifting if ( image.rows() < Pattern::planeSize() ) // Shift rows from top to the center { unsigned int offset = (Pattern::planeSize() - image.rows()) / 2; while ( offset != 0 ) { for ( int i = Pattern::planeSize()-2; i >= 0; --i ) { for ( unsigned int j = 0; j < Pattern::planeSize(); ++j ) pattern.at(i+1, j) = pattern.at(i, j); } for ( unsigned int j = 0; j < Pattern::planeSize(); ++j ) pattern.at(0, j) = 0; --offset; } } if ( image.columns() < Pattern::planeSize() ) // Shift columns from left to center { unsigned int offset = (Pattern::planeSize() - image.columns()) / 2; while ( offset != 0 ) { for ( unsigned int i = 0; i < Pattern::planeSize(); ++i ) { for ( int j = Pattern::planeSize()-2; j >= 0; --j ) pattern.at(i, j+1) = pattern.at(i, j); } for ( unsigned int i = 0; i < Pattern::planeSize(); ++i ) pattern.at(i, 0) = 0; --offset; } } patterns_.push_back( pattern ); } } statistics_.patternsBuildingTime(timer.elapsed()); }