📄 recognize.c
字号:
/*cellwriter -- a character recognition input methodCopyright (C) 2007 Michael Levin <risujin@risujin.org>This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.*/#include "config.h"#include <stdlib.h>#include <string.h>#include <math.h>#include <gtk/gtk.h>#include "common.h"#include "recognize.h"/* preprocess.c */int prep_examined;void engine_prep(void);/* Engines*/Engine engines[] = { /* Preprocessor engine must run first */ { "Key-point distance", engine_prep, MAX_RANGE, TRUE, -1, 0, 0 }, /* Averaging engines */ { "Average distance", engine_average, MAX_RANGE, TRUE, -1, 0, 0 }, { "Average angle", NULL, MAX_RANGE, TRUE, 0, 0, 0 },#ifndef DISABLE_WORDFREQ /* Word frequency engine */ { "Word context", engine_wordfreq, MAX_RANGE / 3, FALSE, -1, 0, 0 },#endif};static int engine_rating(const Sample *sample, int j)/* Get the processed rating for engine j on a sample */{ int value; if (!engines[j].range || engines[j].max < 1) return 0; value = ((int)sample->ratings[j] - engines[j].average) * engines[j].range / engines[j].max; if (engines[j].scale >= 0) value = value * engines[j].scale / ENGINE_SCALE; return value;}/* Sample chain wrapper*/typedef struct SampleLink { Sample sample; struct SampleLink *prev, *next;} SampleLink;static SampleLink *samplelink_root = NULL, *samplelink_iter = NULL;static int current = 1;static Sample *sample_new(void)/* Allocate a link in the sample linked list */{ SampleLink *link; link = g_malloc0(sizeof (*link)); link->next = samplelink_root; if (samplelink_root) samplelink_root->prev = link; samplelink_root = link; return &link->sample;}void sampleiter_reset(void)/* Reset the sample linked list iterator */{ samplelink_iter = samplelink_root;}Sample *sampleiter_next(void)/* Get the next sample link from the sample linked list iterator */{ SampleLink *link; if (!samplelink_iter) return NULL; link = samplelink_iter; samplelink_iter = samplelink_iter->next; return &link->sample;}int samples_loaded(void){ return samplelink_root != NULL;}/* Samples*/int samples_max = 5, no_latin_alpha = FALSE;void clear_sample(Sample *sample)/* Free stroke data associated with a sample and reset its parameters */{ int i; for (i = 0; i < sample->len; i++) { stroke_free(sample->strokes[i]); stroke_free(sample->roughs[i]); } memset(sample, 0, sizeof (*sample));}void copy_sample(Sample *dest, const Sample *src)/* Copy a sample, cloing its strokes, overwriting dest */{ int i; *dest = *src; for (i = 0; i < src->len; i++) { dest->strokes[i] = stroke_clone(src->strokes[i], FALSE); dest->roughs[i] = stroke_clone(src->roughs[i], FALSE); }}static void process_gluable(const Sample *sample, int stroke_num)/* Calculates the lowest distance between the start or end of one stroke and any other point on each other stroke in the sample */{ Point point; Stroke *s1; int i, start; /* Dots cannot be glued */ s1 = sample->strokes[stroke_num]; memset(s1->gluable_start, -1, sizeof (s1->gluable_start)); memset(s1->gluable_end, -1, sizeof (s1->gluable_end)); if (s1->spread < DOT_SPREAD) return; start = TRUE;scan: point = start ? s1->points[0] : s1->points[s1->len - 1]; for (i = 0; i < sample->len; i++) { Vec2 v; Stroke *s2; float dist, min = GLUE_DIST; int j; char gluable; s2 = sample->strokes[i]; if (i == stroke_num || s2->spread < DOT_SPREAD) continue; /* Check the distance to the first point */ vec2_set(&v, s2->points[0].x - point.x, s2->points[0].y - point.y); dist = vec2_mag(&v); if (dist < min) min = dist; /* Find the lowest distance from the glue point to any other point on the other stroke */ for (j = 0; j < s2->len - 1; j++) { Vec2 l, w; double dist, mag, dot; /* Vector l is a unit vector from point j to j + 1 */ vec2_set(&l, s2->points[j].x - s2->points[j + 1].x, s2->points[j].y - s2->points[j + 1].y); mag = vec2_norm(&l, &l); /* Vector w is a vector from point j to our point */ vec2_set(&w, s2->points[j].x - point.x, s2->points[j].y - point.y); /* For points that are not in between a segment, get the distance from the points themselves, otherwise get the distance from the segment line */ dot = vec2_dot(&l, &w); if (dot < 0. || dot > mag) { vec2_set(&v, s2->points[j + 1].x - point.x, s2->points[j + 1].y - point.y); dist = vec2_mag(&v); } else { dist = vec2_cross(&w, &l); if (dist < 0) dist = -dist; } if (dist < min) min = dist; } gluable = min * GLUABLE_MAX / GLUE_DIST; if (start) s1->gluable_start[i] = gluable; else s1->gluable_end[i] = gluable; } if (start) { start = FALSE; goto scan; }}void process_sample(Sample *sample)/* Generate cached properties of a sample */{ int i; float distance; if (sample->processed) return; sample->processed = TRUE; /* Make sure all strokes have been processed first */ for (i = 0; i < sample->len; i++) process_stroke(sample->strokes[i]); /* Compute properties for each stroke */ vec2_set(&sample->center, 0., 0.); for (i = 0, distance = 0.; i < sample->len; i++) { Vec2 v; Stroke *stroke; float weight; int points; stroke = sample->strokes[i]; /* Add the stroke center to the center vector, weighted by length */ vec2_copy(&v, &stroke->center); weight = stroke->spread < DOT_SPREAD ? DOT_SPREAD : stroke->distance; vec2_scale(&v, &v, weight); vec2_sum(&sample->center, &sample->center, &v); distance += weight; /* Get gluing distances */ process_gluable(sample, i); /* Create a rough-sampled version */ points = stroke->distance / ROUGH_RESOLUTION + 0.5; if (points < 4) points = 4; sample->roughs[i] = sample_stroke(NULL, stroke, points, points); } vec2_scale(&sample->center, &sample->center, 1.f / distance); sample->distance = distance;}void center_samples(Vec2 *ac_to_bc, Sample *a, Sample *b)/* Adjust for the difference between two sample centers */{ vec2_sub(ac_to_bc, &b->center, &a->center);}int char_disabled(int ch)/* Returns TRUE if a character is not renderable or is explicity disabled by a setting (not counting disabled Unicode blocks) */{ return (no_latin_alpha && ch >= unicode_blocks[0].start && ch <= unicode_blocks[0].end && g_ascii_isalpha(ch)) || !g_unichar_isgraph(ch);}int sample_disqualified(const Sample *sample)/* Check disqualification conditions for a sample during recognition. The preprocessor engine must run before any calls to this or disqualification will not work. */{ if ((!ignore_stroke_num && sample->len != input->len) || !sample->enabled) return 1; if (sample->disqualified) return 2; if (char_disabled(sample->ch)) return 3; return 0;}int sample_valid(const Sample *sample, int used)/* Check if this sample has changed since it was last referenced */{ if (!sample || !used) return FALSE; return sample->used == used;}static void sample_rating(Sample *sample)/* Get the composite processed rating on a sample */{ int i, rating; if (!sample->ch || sample_disqualified(sample) || sample->penalty >= 1.f) { sample->rating = RATING_MIN; return; } for (i = 0, rating = 0; i < ENGINES; i++) rating += engine_rating(sample, i); rating *= 1.f - sample->penalty; if (rating > RATING_MAX) rating = RATING_MAX; if (rating < RATING_MIN) rating = RATING_MIN; sample->rating = rating;}void update_enabled_samples(void)/* Run through the samples list and enable samples in enabled blocks */{ Sample *sample; sampleiter_reset(); while ((sample = sampleiter_next())) { UnicodeBlock *block; sample->enabled = FALSE; if (!sample->ch) continue; block = unicode_blocks; while (block->name) { if (sample->ch >= block->start && sample->ch <= block->end) { sample->enabled = block->enabled; break; } block++; } }}void promote_sample(Sample *sample)/* Update usage counter for a sample */{ sample->used = current++;}void demote_sample(Sample *sample)/* Remove the sample from our set if we can */{ if (char_trained(sample->ch) > 1) clear_sample(sample); else sample->used = 1;}Stroke *transform_stroke(Sample *src, Transform *tfm, int i)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -