📄 topitch.cpp
字号:
INT32 &corr_prop, INT32 &dunno) { TO_ROW *row; //current row TO_ROW_IT row_it = block->get_rows (); for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { row = row_it.data (); switch (row->pitch_decision) { case PITCH_DUNNO: dunno++; break; case PITCH_DEF_PROP: def_prop++; break; case PITCH_MAYBE_PROP: maybe_prop++; break; case PITCH_DEF_FIXED: def_fixed++; break; case PITCH_MAYBE_FIXED: maybe_fixed++; break; case PITCH_CORR_PROP: corr_prop++; break; case PITCH_CORR_FIXED: corr_fixed++; break; } }}/********************************************************************** * row_pitch_stats * * Decide whether each row is fixed pitch individually. **********************************************************************/BOOL8 row_pitch_stats( //find line stats TO_ROW *row, //current row INT32 maxwidth, //of spaces BOOL8 testing_on //correct orientation ) { BLOBNBOX *blob; //current blob int gap_index; //current gap INT32 prev_x; //end of prev blob INT32 cluster_count; //no of clusters INT32 prev_count; //of clusters INT32 smooth_factor; //for smoothing stats BOX blob_box; //bounding box float lower, upper; //cluster thresholds //gap sizes float gaps[BLOCK_STATS_CLUSTERS]; //blobs BLOBNBOX_IT blob_it = row->blob_list (); STATS gap_stats (0, maxwidth); STATS cluster_stats[BLOCK_STATS_CLUSTERS + 1]; //clusters smooth_factor = (INT32) (row->xheight * textord_wordstats_smooth_factor + 1.5); if (!blob_it.empty ()) { prev_x = blob_it.data ()->bounding_box ().right (); blob_it.forward (); while (!blob_it.at_first ()) { blob = blob_it.data (); if (!blob->joined_to_prev ()) { blob_box = blob->bounding_box (); if (blob_box.left () - prev_x < maxwidth) gap_stats.add (blob_box.left () - prev_x, 1); prev_x = blob_box.right (); } blob_it.forward (); } } if (gap_stats.get_total () == 0) { return FALSE; } cluster_count = 0; lower = row->xheight * words_initial_lower; upper = row->xheight * words_initial_upper; gap_stats.smooth (smooth_factor); do { prev_count = cluster_count; cluster_count = gap_stats.cluster (lower, upper, textord_spacesize_ratioprop, BLOCK_STATS_CLUSTERS, cluster_stats); } while (cluster_count > prev_count && cluster_count < BLOCK_STATS_CLUSTERS); if (cluster_count < 1) { return FALSE; } for (gap_index = 0; gap_index < cluster_count; gap_index++) gaps[gap_index] = cluster_stats[gap_index + 1].ile (0.5); //get medians if (testing_on) { tprintf ("cluster_count=%d:", cluster_count); for (gap_index = 0; gap_index < cluster_count; gap_index++) tprintf (" %g(%d)", gaps[gap_index], cluster_stats[gap_index + 1].get_total ()); tprintf ("\n"); } qsort (gaps, cluster_count, sizeof (float), sort_floats2); //Try to find proportional non-space and space for row. lower = row->xheight * words_default_prop_nonspace; upper = row->xheight * textord_words_min_minspace; for (gap_index = 0; gap_index < cluster_count && gaps[gap_index] < lower; gap_index++); if (gap_index == 0) { if (testing_on) tprintf ("No clusters below nonspace threshold!!\n"); if (cluster_count > 1) { row->pr_nonsp = gaps[0]; row->pr_space = gaps[1]; } else { row->pr_nonsp = lower; row->pr_space = gaps[0]; } } else { row->pr_nonsp = gaps[gap_index - 1]; while (gap_index < cluster_count && gaps[gap_index] < upper) gap_index++; if (gap_index == cluster_count) { if (testing_on) tprintf ("No clusters above nonspace threshold!!\n"); row->pr_space = lower * textord_spacesize_ratioprop; } else row->pr_space = gaps[gap_index]; } //Now try to find the fixed pitch space and non-space. upper = row->xheight * words_default_fixed_space; for (gap_index = 0; gap_index < cluster_count && gaps[gap_index] < upper; gap_index++); if (gap_index == 0) { if (testing_on) tprintf ("No clusters below space threshold!!\n"); row->fp_nonsp = upper; row->fp_space = gaps[0]; } else { row->fp_nonsp = gaps[gap_index - 1]; if (gap_index == cluster_count) { if (testing_on) tprintf ("No clusters above space threshold!!\n"); row->fp_space = row->xheight; } else row->fp_space = gaps[gap_index]; } if (testing_on) { tprintf ("Initial estimates:pr_nonsp=%g, pr_space=%g, fp_nonsp=%g, fp_space=%g\n", row->pr_nonsp, row->pr_space, row->fp_nonsp, row->fp_space); } return TRUE; //computed some stats}/********************************************************************** * find_row_pitch * * Check to see if this row could be fixed pitch using the given spacings. * Blobs with gaps smaller than the lower threshold are assumed to be one. * The larger threshold is the word gap threshold. **********************************************************************/BOOL8 find_row_pitch( //find lines TO_ROW *row, //row to do INT32 maxwidth, //max permitted space INT32 dm_gap, //ignorable gaps TO_BLOCK *block, //block of row INT32 block_index, //block_number INT32 row_index, //number of row BOOL8 testing_on //correct orientation ) { BOOL8 used_dm_model; //looks lik dot matrix float min_space; //estimate threshold float non_space; //gap size float gap_iqr; //interquartile range float pitch_iqr; float dm_gap_iqr; //interquartile range float dm_pitch_iqr; float dm_pitch; //pitch with dm on float pitch; //revised estimate float initial_pitch; //guess at pitch STATS gap_stats (0, maxwidth); //centre-centre STATS pitch_stats (0, maxwidth); row->fixed_pitch = 0.0f; initial_pitch = row->fp_space; if (initial_pitch > row->xheight * (1 + words_default_fixed_limit)) initial_pitch = row->xheight;//keep pitch decent non_space = row->fp_nonsp; if (non_space > initial_pitch) non_space = initial_pitch; min_space = (initial_pitch + non_space) / 2; if (!count_pitch_stats (row, &gap_stats, &pitch_stats, initial_pitch, min_space, TRUE, FALSE, dm_gap)) { dm_gap_iqr = 0.0001; dm_pitch_iqr = maxwidth * 2.0f; dm_pitch = initial_pitch; } else { dm_gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); dm_pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); dm_pitch = pitch_stats.ile (0.5); } gap_stats.clear (); pitch_stats.clear (); if (!count_pitch_stats (row, &gap_stats, &pitch_stats, initial_pitch, min_space, TRUE, FALSE, 0)) { gap_iqr = 0.0001; pitch_iqr = maxwidth * 3.0f; } else { gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); if (testing_on) tprintf ("First fp iteration:initial_pitch=%g, gap_iqr=%g, pitch_iqr=%g, pitch=%g\n", initial_pitch, gap_iqr, pitch_iqr, pitch_stats.ile (0.5)); initial_pitch = pitch_stats.ile (0.5); if (min_space > initial_pitch && count_pitch_stats (row, &gap_stats, &pitch_stats, initial_pitch, initial_pitch, TRUE, FALSE, 0)) { min_space = initial_pitch; gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); if (testing_on) tprintf ("Revised fp iteration:initial_pitch=%g, gap_iqr=%g, pitch_iqr=%g, pitch=%g\n", initial_pitch, gap_iqr, pitch_iqr, pitch_stats.ile (0.5)); initial_pitch = pitch_stats.ile (0.5); } } if (textord_debug_pitch_metric) tprintf ("Blk=%d:Row=%d:%c:p_iqr=%g:g_iqr=%g:dm_p_iqr=%g:dm_g_iqr=%g:%c:", block_index, row_index, block->block->text_region () != NULL ? (block->block->text_region ()->is_prop ()? 'P' : 'F') : 'X', pitch_iqr, gap_iqr, dm_pitch_iqr, dm_gap_iqr, pitch_iqr > maxwidth && dm_pitch_iqr > maxwidth ? 'D' : (pitch_iqr * dm_gap_iqr <= dm_pitch_iqr * gap_iqr ? 'S' : 'M')); if (pitch_iqr > maxwidth && dm_pitch_iqr > maxwidth) { row->pitch_decision = PITCH_DUNNO; if (textord_debug_pitch_metric) tprintf ("\n"); return FALSE; //insufficient data } if (pitch_iqr * dm_gap_iqr <= dm_pitch_iqr * gap_iqr) { if (testing_on) tprintf ("Choosing non dm version:pitch_iqr=%g, gap_iqr=%g, dm_pitch_iqr=%g, dm_gap_iqr=%g\n", pitch_iqr, gap_iqr, dm_pitch_iqr, dm_gap_iqr); gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); pitch = pitch_stats.ile (0.5); used_dm_model = FALSE; } else { if (testing_on) tprintf ("Choosing dm version:pitch_iqr=%g, gap_iqr=%g, dm_pitch_iqr=%g, dm_gap_iqr=%g\n", pitch_iqr, gap_iqr, dm_pitch_iqr, dm_gap_iqr); gap_iqr = dm_gap_iqr; pitch_iqr = dm_pitch_iqr; pitch = dm_pitch; used_dm_model = TRUE; } if (textord_debug_pitch_metric) { tprintf ("rev_p_iqr=%g:rev_g_iqr=%g:pitch=%g:", pitch_iqr, gap_iqr, pitch); tprintf ("p_iqr/g=%g:p_iqr/x=%g:iqr_res=%c:", pitch_iqr / gap_iqr, pitch_iqr / block->xheight, pitch_iqr < gap_iqr * textord_fpiqr_ratio && pitch_iqr < block->xheight * textord_max_pitch_iqr && pitch < block->xheight * textord_words_default_maxspace ? 'F' : 'P'); } if (pitch_iqr < gap_iqr * textord_fpiqr_ratio && pitch_iqr < block->xheight * textord_max_pitch_iqr && pitch < block->xheight * textord_words_default_maxspace) row->pitch_decision = PITCH_MAYBE_FIXED; else row->pitch_decision = PITCH_MAYBE_PROP; row->fixed_pitch = pitch; row->kern_size = gap_stats.ile (0.5); row->min_space = (INT32) (row->fixed_pitch + non_space) / 2; if (row->min_space > row->fixed_pitch) row->min_space = (INT32) row->fixed_pitch; row->max_nonspace = row->min_space; row->space_size = row->fixed_pitch; row->space_threshold = (row->max_nonspace + row->min_space) / 2; row->used_dm_model = used_dm_model; return TRUE;}/********************************************************************** * fixed_pitch_row * * Check to see if this row could be fixed pitch using the given spacings. * Blobs with gaps smaller than the lower threshold are assumed to be one. * The larger threshold is the word gap threshold. **********************************************************************/BOOL8 fixed_pitch_row( //find lines TO_ROW *row, //row to do INT32 block_index //block_number ) { const char *res_string; //pitch result INT16 mid_cuts; //no of cheap cuts float non_space; //gap size float pitch_sd; //error on pitch float sp_sd; //space sd non_space = row->fp_nonsp; if (non_space > row->fixed_pitch) non_space = row->fixed_pitch; if (textord_all_prop) { // Set the decision to definitely proportional. pitch_sd = textord_words_def_prop * row->fixed_pitch; row->pitch_decision = PITCH_DEF_PROP; } else { pitch_sd = tune_row_pitch (row, &row->projection, row->projection_left, row->projection_right, (row->fixed_pitch + non_space * 3) / 4, row->fixed_pitch, sp_sd, mid_cuts, &row->char_cells, block_index == textord_debug_block); if (pitch_sd < textord_words_pitchsd_threshold * row->fixed_pitch && ((pitsync_linear_version & 3) < 3 || (pitsync_linear_version & 3) >= 3 && (row->used_dm_model || sp_sd > 20 || pitch_sd == 0 && sp_sd > 10))) { if (pitch_sd < textord_words_def_fixed * row->fixed_pitch && !row->all_caps && ((pitsync_linear_version & 3) < 3 || sp_sd > 20)) row->pitch_decision = PITCH_DEF_FIXED; else row->pitch_decision = PITCH_MAYBE_FIXED; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -