📄 image.cpp
字号:
/****************************************************************************** * 光学字符识别程序 * 文件名:image.cpp * 功能 :图像处理相关函数实现 * modified by PRTsinghua@hotmail.com******************************************************************************/#include <stdlib.h>#include <assert.h>#include <math.h>#include <algorithm>#include <iostream>#include <qpixmap.h>#include <qimage.h>#include <qcolor.h>#include <klocale.h>#include <klineeditdlg.h>#include <kconfig.h>#include "common.h"#include "image.h"#include "segimage.h"#include "character.h"#include "page.h"#include "abstract_character.h"#include "global.h"#include <kmessagebox.h>using std::max;using std::ios;using std::istream;const float Image::cs_angle_treshold = degtorad( 30 );// 构造函数Image::Image( KognitionApp *app, const char *name ) : QDialog( app, name ), draw_flags( 0 ), border_list_number( 0 ), match_char_pos( 1 ), mark_cs( -1 ), pen_size( 1 ), avg_line_thickness( 0 ), segimage( NULL ), skeleton( NULL ), abstract_char( NULL ), app( app ){ setCaption( "character" ); setMouseTracking( true ); setBackgroundMode( NoBackground );}// 析构函数Image::~Image(){ clear();}// 清理函数void Image::clear(){ if ( segimage ) delete segimage; segimage = NULL; if ( skeleton ) delete skeleton; skeleton = NULL; if ( abstract_char ) delete abstract_char; abstract_char = NULL; char_group.clear(); bl.clear(); spl.clear(); cssl.clear(); border_list_number = 0; match_char_pos = 1; avg_line_thickness = 0;}// 向量化abstract_character Image::vectorize( const CharImage &char_scan, float base_line_pos, float reference_height, float angle ){ scan = char_scan; scan_bw = char_scan.convert_bw(); clear(); segimage = new Segimage( scan ); build_cssg(); skeleton = new character( *segimage, spl ); abstract_char = new abstract_character( *skeleton, base_line_pos, reference_height, angle ); repaint(); return *abstract_char;}// 识别recognized_char_group Image::recognize( const CharImage &char_scan, float base_line_pos, float reference_height, float angle ){ scan = char_scan; scan_bw = char_scan.convert_bw(); clear(); segimage = new Segimage( scan ); #ifdef SPEED_MEASURE struct timeval curtime; seconds_passed( curtime ); #endif build_cssg(); #ifdef SPEED_MEASURE cout << "build_cssg() total ( in s ): " << seconds_passed( curtime ) << endl; #endif skeleton = new character( *segimage, spl ); #ifdef SPEED_MEASURE seconds_passed( curtime ); #endif abstract_char = new abstract_character( *skeleton, base_line_pos, reference_height, angle ); #ifdef SPEED_MEASURE cout << "abstract_character() total ( in s ): " << seconds_passed( curtime ) << endl; #endif char_group = char_db.match( *abstract_char ); #ifdef SPEED_MEASURE cout << "match_character() total ( in s ): " << seconds_passed( curtime ) << endl; #endif repaint(); return char_group;}// 获得图像的最大高度float Image::get_max_height( const CharImage &char_scan, float angle ){ scan = char_scan; clear(); segimage = new Segimage( scan ); build_cssg(); skeleton = new character( *segimage, spl ); return abstract_character::get_real_height( *skeleton, angle );}// 点的方向double Image::direction( Segimage::point p ){ float Hx[3][3] = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } }; float Hy[3][3] = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } }; return atan2( -filter( p.x, p.y, scan, Hy ), filter( p.x, p.y, scan, Hx ) );}// 计算角度double Image::calc_dang( double prev, double curr ){ double dangle = normalize_angle_pi( curr - prev ); if ( fabs( dangle ) < degtorad( 2 ) ) dangle = 0; return dangle;}// 平滑void Image::smooth_points( ringlist_iterator< Segimage::point > first, ringlist_iterator< Segimage::point > last, int num_points ){ ringlist_iterator< Segimage::point > pre_first = first; --pre_first; ringlist_iterator< Segimage::point > post_last = last; ++post_last; ringlist_iterator< Segimage::point > post_post_last = post_last; ++post_post_last; if ( ( pre_first->dang < 0 ) == ( post_post_last->dang < 0 ) ) { double base_angle = pre_first->ang; double delta = normalize_angle_pi( post_last->ang-base_angle ) / ( num_points + 1 ); int n = 0; while( n++ < num_points ) { first->good_smoothed = true; first->oang = first->ang; // 记住初始角度 ( first++ )->ang = normalize_angle_pi( base_angle + delta * n ); } } else { // 迭代循环 for ( int i = 0; i < max_iter; i++ ) { ringlist_iterator< Segimage::point > f = first; int np = num_points; double prev_ang = pre_first->ang; while( np-- > 0 ) { Segimage::point &p = *( f++ ); p.dang = calc_dang( prev_ang, p.ang ); if ( p.oang == 0 ) // 记住初始角度 p.oang = p.ang; // ang( p(i) ) = ang( p(i-1) ) + ( dang( p(i) ) + dang( p(i+1) ) ) / 2 p.ang = normalize_angle_pi( prev_ang + ( p.dang + f->dang ) / 2 ); prev_ang = p.ang; } f->dang = calc_dang( prev_ang, f->ang ); } }}// 判断相对的边界是否标以同样的方向: => true = 同样的方向bool Image::opposite_same_direction( ringlist_iterator< Segimage::point > &start ){ bool same_dir = false; // 检查两个边界 // | | / / // +---+ / / // | | * / // +--s+/ / // * / // --------/ for ( int dir = 0; dir <= 1; dir++ ) // dir == 0 => 后向; dir == 1 => 前向 { ringlist_iterator< Segimage::point > i = start; ringlist_iterator< Segimage::point > j = start->opp; if ( dir ) { ++i; ++j; } else { --i; --j; } Segimage::point p = *i; p.set_opposite( j ); // 检测是否x、y都相等 if ( ( ( start->x >= p.x ) == ( start->opp->x >= p.opp->x ) ) && ( ( start->y >= p.y ) == ( start->opp->y >= p.opp->y ) ) ) { same_dir = true; break; } } return same_dir;}// 创建图像序列int Image::construct_sequence( ringlist_iterator< Segimage::point > &start, Segimage &simage, Segimage::cs_sequence &seq ){ // 计算阈值 avg_line_thickness = simage.avg_line_thickness(); int outer_threshold = max( static_cast<int>( ceil( avg_line_thickness ) ), 3 ); start->processed = true; seq.push_back( &( *start ) ); int num_sequences = 1; bool opp_same_dir = opposite_same_direction( start ); for ( int dir = 0; dir <= 1; dir++ ) // dir == false => 后向; dir == true => 前向 { ringlist_iterator< Segimage::point > done = start; // 上一个交叉片断的起点 ringlist_iterator< Segimage::point > p[2] = { start, start->opp }; ringlist_iterator< Segimage::point > stop_point[2] = { start.end(), start.end() }; bool found[2] = { true, true }; do { ringlist_iterator< Segimage::point > q[4] = { p[0], p[1] }; bool reset_found[2] = { false, false }; for ( int i = 0; i <= 1; i++ ) { if ( !found[i] ) // 没有更多的交叉区段 continue; int n = min( static_cast<int>( q[i].get_corresponding_list()->size() ), outer_threshold ); int n_old = n; bool pass = false; if ( q[i]->processed ) pass = true; // 不检查最后一个交叉区段的起点 // 检查起点的相反点方向是否正确 else if ( q[i] == start->opp && q[i]->length != -1 ) { ringlist_iterator< Segimage::point > forward = q[i]; if ( dir == ( !opp_same_dir && i ) ) --forward; else ++forward; Segimage::point pt( forward->x, forward->y ); pt.set_opposite( q[i]->opp ); if ( line_segment( start->x, start->y, start->opp->x, start->opp->y ).intersect( line_segment( pt.x, pt.y, pt.opp->x, pt.opp->y ), true ) ) pass = true; } //寻找下一个起点 while( n-- && q[i] != p[1-i] && ( pass || q[i]->length == -1 ) ) { pass = false; if ( dir == ( !opp_same_dir && i ) ) --q[i]; else ++q[i]; } bool stop_case = q[i]->processed || // 已经处理的交叉段 q[i] == p[1-i] || ( q[i]->length != -1 && q[i]->opp.get_corresponding_list() != p[1-i].get_corresponding_list() ); // cross section to other list if ( n <= 0 || // 列表中没有其它的交叉段了 stop_case ) { found[i] = false; if ( stop_case ) stop_point[i] = q[i]; // 定义终止点 else if ( n <= 0 && n_old == outer_threshold ) // 确信下一次搜索此边 reset_found[i] = true; // 该交叉段可能在阈值内 } } if ( !found[0] && !found[1] ) // 没有发现更多的交叉段 break; bool got_len[4] = { false, false, false, false }; int len[4] = { 0, 0, 0, 0 }; if ( !found[0] ) { got_len[0] = got_len[3] = true; len[0] = len[3] = outer_threshold+1; q[0] = q[1]; } if ( !found[1] ) { got_len[1] = got_len[2]=true; len[1] = len[2] = outer_threshold+1; q[1] = q[0]; } q[2] = q[1]->opp; // q[0] 和 q[2] 在 side 1 q[3] = q[0]->opp; // q[1] 和 q[3] 在 side 2 bool stop_now = false; for ( int i = 0; i <= 1; i++ ) // i == 0 => side 1; i == 1 => side 2 { for ( ringlist_iterator< Segimage::point > m = p[i]; !got_len[i] || !got_len[i+2]; dir == ( !opp_same_dir && i ) ? --m : ++m ) { for ( int j = 0; j <= 1; j++ ) { int pos = i+2*j; if ( !got_len[pos] ) { if ( m == stop_point[i] ) stop_now = true; if ( m == q[pos] ) got_len[pos] = true; else len[pos]++; } } } } if ( stop_now ) break; // 比较长度,判断该旋转哪个交叉段 int min_dist_q0_index = len[0] < len[3] ? 0 : 3; int min_dist_q0 = len[min_dist_q0_index]; int sum_q0 = len[0] + len[3]; int min_dist_q1_index = len[1] < len[2] ? 1 : 2; int min_dist_q1 = len[min_dist_q1_index]; int sum_q1 = len[1] + len[2]; if ( min_dist_q0<min_dist_q1 || ( min_dist_q0 == min_dist_q1 && sum_q0<sum_q1 ) ) { // 选择两个cs间距离大点的点 if ( len[min_dist_q0_index == 0 ? 3 : 0] > outer_threshold ) break; p[0] = q[0]; p[1] = q[3]; done = q[0]; } else { if ( len[min_dist_q1_index == 1 ? 2 : 1] > outer_threshold ) // 曲率太大 break; p[0] = q[2]; p[1] = q[1]; done = q[1]; } assert( !done->processed ); done->processed = true; // 确信交叉段在列表里只有一份 if ( dir ) seq.push_back( &( *done ) ); else seq.push_front( &( *done ) ); num_sequences++; for ( int i = 0; i <= 1; i++ ) if ( reset_found[i] ) found[i] = true; } while( true ); } return num_sequences;}void Image::build_cssg(){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -