1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27 /*!
28 * \file recogbasic.c
29 * <pre>
30 *
31 * Recog creation, destruction and access
32 * L_RECOG *recogCreateFromRecog()
33 * L_RECOG *recogCreateFromPixa()
34 * L_RECOG *recogCreateFromPixaNoFinish()
35 * L_RECOG *recogCreate()
36 * void recogDestroy()
37 * l_int32 recogGetCount()
38 * l_int32 recogSetParams()
39 * static l_int32 recogGetCharsetSize()
40 *
41 * Character/index lookup
42 * l_int32 recogGetClassIndex()
43 * l_int32 recogStringToIndex()
44 * l_int32 recogGetClassString()
45 * l_int32 l_convertCharstrToInt()
46 *
47 * Serialization
48 * L_RECOG *recogRead()
49 * L_RECOG *recogReadStream()
50 * L_RECOG *recogReadMem()
51 * l_int32 recogWrite()
52 * l_int32 recogWriteStream()
53 * l_int32 recogWriteMem()
54 * PIXA *recogExtractPixa()
55 * static l_int32 recogAddCharstrLabels()
56 * static l_int32 recogAddAllSamples()
57 *
58 * The recognizer functionality is split into four files:
59 * recogbasic.c: create, destroy, access, serialize
60 * recogtrain.c: training on labeled and unlabeled data
61 * recogident.c: running the recognizer(s) on input
62 * recogdid.c: running the recognizer(s) on input using a
63 * document image decoding (DID) hidden markov model
64 *
65 * This is a content-adapted (or book-adapted) recognizer (BAR) application.
66 * The recognizers here are typically assembled from data that has
67 * been labeled by a generic recognition system, such as Tesseract.
68 * The general procedure to create a recognizer (recog) from labeled data is
69 * to add the labeled character bitmaps, either one at a time or
70 * all together from a pixa with labeled pix.
71 *
72 * The suggested use for a BAR that consists of labeled templates drawn
73 * from a single source (e.g., a book) is to identify unlabeled samples
74 * by using unscaled character templates in the BAR, picking the
75 * template closest to the unlabeled sample.
76 *
77 * Outliers can be removed from a pixa of labeled pix. This is one of
78 * two methods that use averaged templates (the other is greedy splitting
79 * of characters). See recogtrain.c for a discussion and the implementation.
80 *
81 * A special bootstrap recognizer (BSR) can be used to make a BAR from
82 * unlabeled book data. This is done by comparing character images
83 * from the book with labeled templates in the BSR, where all images
84 * are scaled to h = 40. The templates can be either the scanned images
85 * or images consisting of width-normalized strokes derived from
86 * the skeleton of the character bitmaps.
87 *
88 * Two BARs of labeled character data, that have been made by
89 * different recognizers, can be joined by extracting a pixa of the
90 * labeled templates from each, joining the two pixa, and then
91 * and regenerating a BAR from the joined set of templates.
92 * If all the labeled character data is from a single source (e.g, a book),
93 * identification can proceed using unscaled templates (either the input
94 * image or width-normalized lines). But if the labeled data comes from
95 * more than one source, (a "hybrid" recognizer), the templates should
96 * be scaled, and we recommend scaling to a fixed height.
97 *
98 * Suppose it is not possible to generate a BAR with a sufficient number
99 * of templates of each class taken from a single source. In that case,
100 * templates from the BSR itself can be added. This is the condition
101 * described above, where the labeled templates come from multiple
102 * sources, and it is necessary to do all character matches using
103 * templates that have been scaled to a fixed height (e.g., 40).
104 * Likewise, the samples to be identified using this hybrid recognizer
105 * must be modified in the same way. See prog/recogtest3.c for an
106 * example of the steps that can be taken in the construction of a BAR
107 * using a BSR.
108 *
109 * For training numeric input, an example set of calls that scales
110 * each training input to fixed h and will use the line templates of
111 * width linew for identifying unknown characters is:
112 * L_Recog *rec = recogCreate(0, h, linew, 128, 1);
113 * for (i = 0; i < n; i++) { // read in n training digits
114 * Pix *pix = ...
115 * recogTrainLabeled(rec, pix, NULL, text[i], 0);
116 * }
117 * recogTrainingFinished(&rec, 1, -1, -1.0); // required
118 *
119 * It is an error if any function that computes averages, removes
120 * outliers or requests identification of an unlabeled character,
121 * such as:
122 * (1) computing the sample averages: recogAverageSamples()
123 * (2) removing outliers: recogRemoveOutliers1() or recogRemoveOutliers2()
124 * (3) requesting identification of an unlabeled character:
125 * recogIdentifyPix()
126 * is called before an explicit call to finish training. Note that
127 * to do further training on a "finished" recognizer, you can set
128 * recog->train_done = FALSE;
129 * add the new training samples, and again call
130 * recogTrainingFinished(&rec, 1, -1, -1.0); // required
131 *
132 * If not scaling, using the images directly for identification, and
133 * removing outliers, do something like this:
134 * L_Recog *rec = recogCreate(0, 0, 0, 128, 1);
135 * for (i = 0; i < n; i++) { // read in n training characters
136 * Pix *pix = ...
137 * recogTrainLabeled(rec, pix, NULL, text[i], 0);
138 * }
139 * recogTrainingFinished(&rec, 1, -1, -1.0);
140 * if (!rec) ... [return]
141 * // remove outliers
142 * recogRemoveOutliers1(&rec, 0.7, 2, NULL, NULL);
143 *
144 * You can generate a recognizer from a pixa where the text field in
145 * each pix is the character string label for the pix. For example,
146 * the following recognizer will store unscaled line images:
147 * L_Recog *rec = recogCreateFromPixa(pixa, 0, 0, linew, 128, 1);
148 * and in use, it is fed unscaled line images to identify.
149 *
150 * For the following, assume that you have a pixa of labeled templates.
151 * If it is likely that some of the input templates are mislabeled,
152 * there are several things that can be done to remove them.
153 * The first is to put a size and quantity filter on them; e.g.
154 * Pixa *pixa2 = recogFilterPixaBySize(pixa1, 10, 15, 2.6);
155 * Then you can remove outliers; e.g.,
156 * Pixa *pixa3 = pixaRemoveOutliers2(pixa2, -1.0, -1, NULL, NULL);
157 *
158 * To this point, all templates are from a single source, so you
159 * can make a recognizer that uses the unscaled templates and optionally
160 * attempts to split touching characters:
161 * L_Recog *recog1 = recogCreateFromPixa(pixa3, ...);
162 * Alternatively, if you need more templates for some of the classes,
163 * you can pad with templates from a "bootstrap" recognizer (BSR).
164 * If you pad, it is necessary to scale the templates and input
165 * samples to a fixed height, and no attempt will be made to split
166 * the input sample connected components:
167 * L_Recog *recog1 = recogCreateFromPixa(pixa3, 0, 40, 0, 128, 0);
168 * recogPadDigitTrainingSet(&recog1, 40, 0);
169 *
170 * A special case is a pure BSR, that contains images scaled to a fixed
171 * height (we use 40 in these examples).
172 * For this,use either the scanned bitmap:
173 * L_Recog *recboot = recogCreateFromPixa(pixa, 0, 40, 0, 128, 1);
174 * or width-normalized lines (use width of 5 here):
175 * L_Recog *recboot = recogCreateFromPixa(pixa, 0, 40, 5, 128, 1);
176 *
177 * This can be used to train a new book adapted recognizer (BAC), on
178 * unlabeled data from, e.g., a book. To do this, the following is required:
179 * (1) the input images from the book must be scaled in the same
180 * way as those in the BSR, and
181 * (2) both the BSR and the input images must be set up to be either
182 * input scanned images or width-normalized lines.
183 *
184 * </pre>
185 */
186
187 #include <string.h>
188 #include "allheaders.h"
189
190 static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */
191 static const l_int32 MAX_EXAMPLES_IN_CLASS = 256;
192
193 /* Default recog parameters that can be changed */
194 static const l_int32 DEFAULT_CHARSET_TYPE = L_ARABIC_NUMERALS;
195 static const l_int32 DEFAULT_MIN_NOPAD = 1;
196 static const l_float32 DEFAULT_MAX_WH_RATIO = 3.0; /* max allowed w/h
197 ratio for a component to be split */
198 static const l_float32 DEFAULT_MAX_HT_RATIO = 2.6; /* max allowed ratio of
199 max/min unscaled averaged template heights */
200 static const l_int32 DEFAULT_THRESHOLD = 150; /* for binarization */
201 static const l_int32 DEFAULT_MAXYSHIFT = 1; /* for identification */
202
203 /* Static functions */
204 static l_int32 recogGetCharsetSize(l_int32 type);
205 static l_int32 recogAddCharstrLabels(L_RECOG *recog);
206 static l_int32 recogAddAllSamples(L_RECOG **precog, PIXAA *paa, l_int32 debug);
207
208
209 /*------------------------------------------------------------------------*
210 * Recog: initialization and destruction *
211 *------------------------------------------------------------------------*/
212 /*!
213 * \brief recogCreateFromRecog()
214 *
215 * \param[in] recs source recog with arbitrary input parameters
216 * \param[in] scalew scale all widths to this; use 0 otherwise
217 * \param[in] scaleh scale all heights to this; use 0 otherwise
218 * \param[in] linew width of normalized strokes; use 0 to skip
219 * \param[in] threshold for binarization; typically ~128
220 * \param[in] maxyshift from nominal centroid alignment; default is 1
221 * \return recd, or NULL on error
222 *
223 * <pre>
224 * Notes:
225 * (1) This is a convenience function that generates a recog using
226 * the unscaled training data in an existing recog.
227 * (2) It is recommended to use %maxyshift = 1 (the default value)
228 * (3) See recogCreate() for use of %scalew, %scaleh and %linew.
229 * </pre>
230 */
231 L_RECOG *
recogCreateFromRecog(L_RECOG * recs,l_int32 scalew,l_int32 scaleh,l_int32 linew,l_int32 threshold,l_int32 maxyshift)232 recogCreateFromRecog(L_RECOG *recs,
233 l_int32 scalew,
234 l_int32 scaleh,
235 l_int32 linew,
236 l_int32 threshold,
237 l_int32 maxyshift)
238 {
239 L_RECOG *recd;
240 PIXA *pixa;
241
242 PROCNAME("recogCreateFromRecog");
243
244 if (!recs)
245 return (L_RECOG *)ERROR_PTR("recs not defined", procName, NULL);
246
247 pixa = recogExtractPixa(recs);
248 recd = recogCreateFromPixa(pixa, scalew, scaleh, linew, threshold,
249 maxyshift);
250 pixaDestroy(&pixa);
251 return recd;
252 }
253
254
255 /*!
256 * \brief recogCreateFromPixa()
257 *
258 * \param[in] pixa of labeled, 1 bpp images
259 * \param[in] scalew scale all widths to this; use 0 otherwise
260 * \param[in] scaleh scale all heights to this; use 0 otherwise
261 * \param[in] linew width of normalized strokes; use 0 to skip
262 * \param[in] threshold for binarization; typically ~150
263 * \param[in] maxyshift from nominal centroid alignment; default is 1
264 * \return recog, or NULL on error
265 *
266 * <pre>
267 * Notes:
268 * (1) This is a convenience function for training from labeled data.
269 * The pixa can be read from file.
270 * (2) The pixa should contain the unscaled bitmaps used for training.
271 * (3) See recogCreate() for use of %scalew, %scaleh and %linew.
272 * (4) It is recommended to use %maxyshift = 1 (the default value)
273 * (5) All examples in the same class (i.e., with the same character
274 * label) should be similar. They can be made similar by invoking
275 * recogRemoveOutliers[1,2]() on %pixa before calling this function.
276 * </pre>
277 */
278 L_RECOG *
recogCreateFromPixa(PIXA * pixa,l_int32 scalew,l_int32 scaleh,l_int32 linew,l_int32 threshold,l_int32 maxyshift)279 recogCreateFromPixa(PIXA *pixa,
280 l_int32 scalew,
281 l_int32 scaleh,
282 l_int32 linew,
283 l_int32 threshold,
284 l_int32 maxyshift)
285 {
286 L_RECOG *recog;
287
288 PROCNAME("recogCreateFromPixa");
289
290 if (!pixa)
291 return (L_RECOG *)ERROR_PTR("pixa not defined", procName, NULL);
292
293 recog = recogCreateFromPixaNoFinish(pixa, scalew, scaleh, linew,
294 threshold, maxyshift);
295 if (!recog)
296 return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL);
297
298 recogTrainingFinished(&recog, 1, -1, -1.0);
299 if (!recog)
300 return (L_RECOG *)ERROR_PTR("bad templates", procName, NULL);
301 return recog;
302 }
303
304
305 /*!
306 * \brief recogCreateFromPixaNoFinish()
307 *
308 * \param[in] pixa of labeled, 1 bpp images
309 * \param[in] scalew scale all widths to this; use 0 otherwise
310 * \param[in] scaleh scale all heights to this; use 0 otherwise
311 * \param[in] linew width of normalized strokes; use 0 to skip
312 * \param[in] threshold for binarization; typically ~150
313 * \param[in] maxyshift from nominal centroid alignment; default is 1
314 * \return recog, or NULL on error
315 *
316 * <pre>
317 * Notes:
318 * (1) See recogCreateFromPixa() for details.
319 * (2) This is also used to generate a pixaa with templates
320 * in each class within a pixa. For that, all args except for
321 * %pixa are ignored.
322 * </pre>
323 */
324 L_RECOG *
recogCreateFromPixaNoFinish(PIXA * pixa,l_int32 scalew,l_int32 scaleh,l_int32 linew,l_int32 threshold,l_int32 maxyshift)325 recogCreateFromPixaNoFinish(PIXA *pixa,
326 l_int32 scalew,
327 l_int32 scaleh,
328 l_int32 linew,
329 l_int32 threshold,
330 l_int32 maxyshift)
331 {
332 char *text;
333 l_int32 full, n, i, ntext, same, maxd;
334 PIX *pix;
335 L_RECOG *recog;
336
337 PROCNAME("recogCreateFromPixaNoFinish");
338
339 if (!pixa)
340 return (L_RECOG *)ERROR_PTR("pixa not defined", procName, NULL);
341 pixaVerifyDepth(pixa, &same, &maxd);
342 if (maxd > 1)
343 return (L_RECOG *)ERROR_PTR("not all pix are 1 bpp", procName, NULL);
344
345 pixaIsFull(pixa, &full, NULL);
346 if (!full)
347 return (L_RECOG *)ERROR_PTR("not all pix are present", procName, NULL);
348
349 n = pixaGetCount(pixa);
350 pixaCountText(pixa, &ntext);
351 if (ntext == 0)
352 return (L_RECOG *)ERROR_PTR("no pix have text strings", procName, NULL);
353 if (ntext < n)
354 L_ERROR("%d text strings < %d pix\n", procName, ntext, n);
355
356 recog = recogCreate(scalew, scaleh, linew, threshold, maxyshift);
357 if (!recog)
358 return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL);
359 for (i = 0; i < n; i++) {
360 pix = pixaGetPix(pixa, i, L_CLONE);
361 text = pixGetText(pix);
362 if (!text || strlen(text) == 0) {
363 L_ERROR("pix[%d] has no text\n", procName, i);
364 pixDestroy(&pix);
365 continue;
366 }
367 recogTrainLabeled(recog, pix, NULL, text, 0);
368 pixDestroy(&pix);
369 }
370
371 return recog;
372 }
373
374
375 /*!
376 * \brief recogCreate()
377 *
378 * \param[in] scalew scale all widths to this; use 0 otherwise
379 * \param[in] scaleh scale all heights to this; use 0 otherwise
380 * \param[in] linew width of normalized strokes; use 0 to skip
381 * \param[in] threshold for binarization; typically ~128; 0 for default
382 * \param[in] maxyshift from nominal centroid alignment; default is 1
383 * \return recog, or NULL on error
384 *
385 * <pre>
386 * Notes:
387 * (1) If %scalew == 0 and %scaleh == 0, no scaling is done.
388 * If one of these is 0 and the other is > 0, scaling is isotropic
389 * to the requested size. We typically do not set both > 0.
390 * (2) Use linew > 0 to convert the templates to images with fixed
391 * width strokes. linew == 0 skips the conversion.
392 * (3) The only valid values for %maxyshift are 0, 1 and 2.
393 * It is recommended to use %maxyshift == 1 (default value).
394 * Using %maxyshift == 0 is much faster than %maxyshift == 1, but
395 * it is much less likely to find the template with the best
396 * correlation. Use of anything but 1 results in a warning.
397 * (4) Scaling is used for finding outliers and for training a
398 * book-adapted recognizer (BAR) from a bootstrap recognizer (BSR).
399 * Scaling the height to a fixed value and scaling the width
400 * accordingly (e.g., %scaleh = 40, %scalew = 0) is recommended.
401 * (5) The storage for most of the arrays is allocated when training
402 * is finished.
403 * </pre>
404 */
405 L_RECOG *
recogCreate(l_int32 scalew,l_int32 scaleh,l_int32 linew,l_int32 threshold,l_int32 maxyshift)406 recogCreate(l_int32 scalew,
407 l_int32 scaleh,
408 l_int32 linew,
409 l_int32 threshold,
410 l_int32 maxyshift)
411 {
412 L_RECOG *recog;
413
414 PROCNAME("recogCreate");
415
416 if (scalew < 0 || scaleh < 0)
417 return (L_RECOG *)ERROR_PTR("invalid scalew or scaleh", procName, NULL);
418 if (linew > 10)
419 return (L_RECOG *)ERROR_PTR("invalid linew > 10", procName, NULL);
420 if (threshold == 0) threshold = DEFAULT_THRESHOLD;
421 if (threshold < 0 || threshold > 255) {
422 L_WARNING("invalid threshold; using default\n", procName);
423 threshold = DEFAULT_THRESHOLD;
424 }
425 if (maxyshift < 0 || maxyshift > 2) {
426 L_WARNING("invalid maxyshift; using default value\n", procName);
427 maxyshift = DEFAULT_MAXYSHIFT;
428 } else if (maxyshift == 0) {
429 L_WARNING("Using maxyshift = 0; faster, worse correlation results\n",
430 procName);
431 } else if (maxyshift == 2) {
432 L_WARNING("Using maxyshift = 2; slower\n", procName);
433 }
434
435 if ((recog = (L_RECOG *)LEPT_CALLOC(1, sizeof(L_RECOG))) == NULL)
436 return (L_RECOG *)ERROR_PTR("rec not made", procName, NULL);
437 recog->templ_use = L_USE_ALL_TEMPLATES; /* default */
438 recog->threshold = threshold;
439 recog->scalew = scalew;
440 recog->scaleh = scaleh;
441 recog->linew = linew;
442 recog->maxyshift = maxyshift;
443 recogSetParams(recog, 1, -1, -1.0, -1.0);
444 recog->bmf = bmfCreate(NULL, 6);
445 recog->bmf_size = 6;
446 recog->maxarraysize = MAX_EXAMPLES_IN_CLASS;
447
448 /* Generate the LUTs */
449 recog->centtab = makePixelCentroidTab8();
450 recog->sumtab = makePixelSumTab8();
451 recog->sa_text = sarrayCreate(0);
452 recog->dna_tochar = l_dnaCreate(0);
453
454 /* Input default values for min component size for splitting.
455 * These are overwritten when pixTrainingFinished() is called. */
456 recog->min_splitw = 6;
457 recog->max_splith = 60;
458
459 /* Allocate the paa for the unscaled training bitmaps */
460 recog->pixaa_u = pixaaCreate(recog->maxarraysize);
461
462 /* Generate the storage for debugging */
463 recog->pixadb_boot = pixaCreate(2);
464 recog->pixadb_split = pixaCreate(2);
465 return recog;
466 }
467
468
469 /*!
470 * \brief recogDestroy()
471 *
472 * \param[in,out] precog will be set to null before returning
473 * \return void
474 */
475 void
recogDestroy(L_RECOG ** precog)476 recogDestroy(L_RECOG **precog)
477 {
478 L_RECOG *recog;
479
480 PROCNAME("recogDestroy");
481
482 if (!precog) {
483 L_WARNING("ptr address is null\n", procName);
484 return;
485 }
486
487 if ((recog = *precog) == NULL) return;
488
489 LEPT_FREE(recog->centtab);
490 LEPT_FREE(recog->sumtab);
491 sarrayDestroy(&recog->sa_text);
492 l_dnaDestroy(&recog->dna_tochar);
493 pixaaDestroy(&recog->pixaa_u);
494 pixaDestroy(&recog->pixa_u);
495 ptaaDestroy(&recog->ptaa_u);
496 ptaDestroy(&recog->pta_u);
497 numaDestroy(&recog->nasum_u);
498 numaaDestroy(&recog->naasum_u);
499 pixaaDestroy(&recog->pixaa);
500 pixaDestroy(&recog->pixa);
501 ptaaDestroy(&recog->ptaa);
502 ptaDestroy(&recog->pta);
503 numaDestroy(&recog->nasum);
504 numaaDestroy(&recog->naasum);
505 pixaDestroy(&recog->pixa_tr);
506 pixaDestroy(&recog->pixadb_ave);
507 pixaDestroy(&recog->pixa_id);
508 pixDestroy(&recog->pixdb_ave);
509 pixDestroy(&recog->pixdb_range);
510 pixaDestroy(&recog->pixadb_boot);
511 pixaDestroy(&recog->pixadb_split);
512 bmfDestroy(&recog->bmf);
513 rchDestroy(&recog->rch);
514 rchaDestroy(&recog->rcha);
515 recogDestroyDid(recog);
516 LEPT_FREE(recog);
517 *precog = NULL;
518 return;
519 }
520
521
522 /*!
523 * \brief recogGetCount()
524 *
525 * \param[in] recog
526 * \return count of classes in recog; 0 if no recog or on error
527 */
528 l_int32
recogGetCount(L_RECOG * recog)529 recogGetCount(L_RECOG *recog)
530 {
531 PROCNAME("recogGetCount");
532
533 if (!recog)
534 return ERROR_INT("recog not defined", procName, 0);
535 return recog->setsize;
536 }
537
538
539 /*!
540 * \brief recogSetParams()
541 *
542 * \param[in] recog to be padded, if necessary
543 * \param[in] type type of char set; -1 for default;
544 * see enum in recog.h
545 * \param[in] min_nopad min number in a class without padding;
546 * use -1 for default
547 * \param[in] max_wh_ratio max width/height ratio allowed for splitting;
548 * use -1.0 for default
549 * \param[in] max_ht_ratio max of max/min averaged template height ratio;
550 * use -1.0 for default
551 * \return 0 if OK, 1 on error
552 *
553 * <pre>
554 * Notes:
555 * (1) This is called when a recog is created.
556 * (2) Default %min_nopad value allows for some padding.
557 * To disable padding, set %min_nopad = 0. To pad only when
558 * no samples are available for the class, set %min_nopad = 1.
559 * (3) The %max_wh_ratio limits the width/height ratio for components
560 * that we attempt to split. Splitting long components is expensive.
561 * (4) The %max_ht_ratio is a quality requirement on the training data.
562 * The recognizer will not run if the averages are computed and
563 * the templates do not satisfy it.
564 * </pre>
565 */
566 l_int32
recogSetParams(L_RECOG * recog,l_int32 type,l_int32 min_nopad,l_float32 max_wh_ratio,l_float32 max_ht_ratio)567 recogSetParams(L_RECOG *recog,
568 l_int32 type,
569 l_int32 min_nopad,
570 l_float32 max_wh_ratio,
571 l_float32 max_ht_ratio)
572 {
573
574 PROCNAME("recogSetParams");
575
576 if (!recog)
577 return ERROR_INT("recog not defined", procName, 1);
578
579 recog->charset_type = (type >= 0) ? type : DEFAULT_CHARSET_TYPE;
580 recog->charset_size = recogGetCharsetSize(recog->charset_type);
581 recog->min_nopad = (min_nopad >= 0) ? min_nopad : DEFAULT_MIN_NOPAD;
582 recog->max_wh_ratio = (max_wh_ratio > 0.0) ? max_wh_ratio :
583 DEFAULT_MAX_WH_RATIO;
584 recog->max_ht_ratio = (max_ht_ratio > 1.0) ? max_ht_ratio :
585 DEFAULT_MAX_HT_RATIO;
586 return 0;
587 }
588
589
590 /*!
591 * \brief recogGetCharsetSize()
592 *
593 * \param[in] type of charset
594 * \return size of charset, or 0 if unknown or on error
595 */
596 static l_int32
recogGetCharsetSize(l_int32 type)597 recogGetCharsetSize(l_int32 type)
598 {
599 PROCNAME("recogGetCharsetSize");
600
601 switch (type) {
602 case L_UNKNOWN:
603 return 0;
604 case L_ARABIC_NUMERALS:
605 return 10;
606 case L_LC_ROMAN_NUMERALS:
607 return 7;
608 case L_UC_ROMAN_NUMERALS:
609 return 7;
610 case L_LC_ALPHA:
611 return 26;
612 case L_UC_ALPHA:
613 return 26;
614 default:
615 L_ERROR("invalid charset_type %d\n", procName, type);
616 return 0;
617 }
618 return 0; /* shouldn't happen */
619 }
620
621
622 /*------------------------------------------------------------------------*
623 * Character/index lookup *
624 *------------------------------------------------------------------------*/
625 /*!
626 * \brief recogGetClassIndex()
627 *
628 * \param[in] recog with LUT's pre-computed
629 * \param[in] val integer value; can be up to 3 bytes for UTF-8
630 * \param[in] text text from which %val was derived; used if not found
631 * \param[out] pindex index into dna_tochar
632 * \return 0 if found; 1 if not found and added; 2 on error.
633 *
634 * <pre>
635 * Notes:
636 * (1) This is used during training. There is one entry in
637 * recog->dna_tochar (integer value, e.g., ascii) and
638 * one in recog->sa_text (e.g, ascii letter in a string)
639 * for each character class.
640 * (2) This searches the dna character array for %val. If it is
641 * not found, the template represents a character class not
642 * already seen: it increments setsize (the number of character
643 * classes) by 1, and augments both the index (dna_tochar)
644 * and text (sa_text) arrays.
645 * (3) Returns the index in &index, except on error.
646 * (4) Caller must check the function return value.
647 * </pre>
648 */
649 l_int32
recogGetClassIndex(L_RECOG * recog,l_int32 val,char * text,l_int32 * pindex)650 recogGetClassIndex(L_RECOG *recog,
651 l_int32 val,
652 char *text,
653 l_int32 *pindex)
654 {
655 l_int32 i, n, ival;
656
657 PROCNAME("recogGetClassIndex");
658
659 if (!pindex)
660 return ERROR_INT("&index not defined", procName, 2);
661 *pindex = -1;
662 if (!recog)
663 return ERROR_INT("recog not defined", procName, 2);
664 if (!text)
665 return ERROR_INT("text not defined", procName, 2);
666
667 /* Search existing characters */
668 n = l_dnaGetCount(recog->dna_tochar);
669 for (i = 0; i < n; i++) {
670 l_dnaGetIValue(recog->dna_tochar, i, &ival);
671 if (val == ival) { /* found */
672 *pindex = i;
673 return 0;
674 }
675 }
676
677 /* If not found... */
678 l_dnaAddNumber(recog->dna_tochar, val);
679 sarrayAddString(recog->sa_text, text, L_COPY);
680 recog->setsize++;
681 *pindex = n;
682 return 1;
683 }
684
685
686 /*!
687 * \brief recogStringToIndex()
688 *
689 * \param[in] recog
690 * \param[in] text text string for some class
691 * \param[out] pindex index for that class; -1 if not found
692 * \return 0 if OK, 1 on error not finding the string is an error
693 */
694 l_int32
recogStringToIndex(L_RECOG * recog,char * text,l_int32 * pindex)695 recogStringToIndex(L_RECOG *recog,
696 char *text,
697 l_int32 *pindex)
698 {
699 char *charstr;
700 l_int32 i, n, diff;
701
702 PROCNAME("recogStringtoIndex");
703
704 if (!pindex)
705 return ERROR_INT("&index not defined", procName, 1);
706 *pindex = -1;
707 if (!recog)
708 return ERROR_INT("recog not defined", procName, 1);
709 if (!text)
710 return ERROR_INT("text not defined", procName, 1);
711
712 /* Search existing characters */
713 n = recog->setsize;
714 for (i = 0; i < n; i++) {
715 recogGetClassString(recog, i, &charstr);
716 if (!charstr) {
717 L_ERROR("string not found for index %d\n", procName, i);
718 continue;
719 }
720 diff = strcmp(text, charstr);
721 LEPT_FREE(charstr);
722 if (diff) continue;
723 *pindex = i;
724 return 0;
725 }
726
727 return 1; /* not found */
728 }
729
730
731 /*!
732 * \brief recogGetClassString()
733 *
734 * \param[in] recog
735 * \param[in] index into array of char types
736 * \param[out] pcharstr string representation;
737 * returns an empty string on error
738 * \return 0 if found, 1 on error
739 *
740 * <pre>
741 * Notes:
742 * (1) Extracts a copy of the string from sa_text, which
743 * the caller must free.
744 * (2) Caller must check the function return value.
745 * </pre>
746 */
747 l_int32
recogGetClassString(L_RECOG * recog,l_int32 index,char ** pcharstr)748 recogGetClassString(L_RECOG *recog,
749 l_int32 index,
750 char **pcharstr)
751 {
752 PROCNAME("recogGetClassString");
753
754 if (!pcharstr)
755 return ERROR_INT("&charstr not defined", procName, 1);
756 *pcharstr = stringNew("");
757 if (!recog)
758 return ERROR_INT("recog not defined", procName, 2);
759
760 if (index < 0 || index >= recog->setsize)
761 return ERROR_INT("invalid index", procName, 1);
762 LEPT_FREE(*pcharstr);
763 *pcharstr = sarrayGetString(recog->sa_text, index, L_COPY);
764 return 0;
765 }
766
767
768 /*!
769 * \brief l_convertCharstrToInt()
770 *
771 * \param[in] str input string representing one UTF-8 character;
772 * not more than 4 bytes
773 * \param[out] pval integer value for the input. Think of it
774 * as a 1-to-1 hash code.
775 * \return 0 if OK, 1 on error
776 */
777 l_int32
l_convertCharstrToInt(const char * str,l_int32 * pval)778 l_convertCharstrToInt(const char *str,
779 l_int32 *pval)
780 {
781 l_int32 size, val;
782
783 PROCNAME("l_convertCharstrToInt");
784
785 if (!pval)
786 return ERROR_INT("&val not defined", procName, 1);
787 *pval = 0;
788 if (!str)
789 return ERROR_INT("str not defined", procName, 1);
790 size = strlen(str);
791 if (size == 0)
792 return ERROR_INT("empty string", procName, 1);
793 if (size > 4)
794 return ERROR_INT("invalid string: > 4 bytes", procName, 1);
795
796 val = (l_int32)str[0];
797 if (size > 1)
798 val = (val << 8) + (l_int32)str[1];
799 if (size > 2)
800 val = (val << 8) + (l_int32)str[2];
801 if (size > 3)
802 val = (val << 8) + (l_int32)str[3];
803 *pval = val;
804 return 0;
805 }
806
807
808 /*------------------------------------------------------------------------*
809 * Serialization *
810 *------------------------------------------------------------------------*/
811 /*!
812 * \brief recogRead()
813 *
814 * \param[in] filename
815 * \return recog, or NULL on error
816 *
817 * <pre>
818 * Notes:
819 * (1) When a recog is serialized, a pixaa of the templates that are
820 * actually used for correlation is saved in the pixaa_u array
821 * of the recog. These can be different from the templates that
822 * were used to generate the recog, because those original templates
823 * can be scaled and turned into normalized lines. When recog1
824 * is deserialized to recog2, these templates are put in both the
825 * unscaled array (pixaa_u) and the modified array (pixaa) in recog2.
826 * Why not put it in only the unscaled array and let
827 * recogTrainingFinalized() regenerate the modified templates?
828 * The reason is that with normalized lines, the operation of
829 * thinning to a skeleton and dilating back to a fixed width
830 * is not idempotent. Thinning to a skeleton saves pixels at
831 * the end of a line segment, and thickening the skeleton puts
832 * additional pixels at the end of the lines. This tends to
833 * close gaps.
834 * </pre>
835 */
836 L_RECOG *
recogRead(const char * filename)837 recogRead(const char *filename)
838 {
839 FILE *fp;
840 L_RECOG *recog;
841
842 PROCNAME("recogRead");
843
844 if (!filename)
845 return (L_RECOG *)ERROR_PTR("filename not defined", procName, NULL);
846 if ((fp = fopenReadStream(filename)) == NULL)
847 return (L_RECOG *)ERROR_PTR("stream not opened", procName, NULL);
848
849 if ((recog = recogReadStream(fp)) == NULL) {
850 fclose(fp);
851 return (L_RECOG *)ERROR_PTR("recog not read", procName, NULL);
852 }
853
854 fclose(fp);
855 return recog;
856 }
857
858
859 /*!
860 * \brief recogReadStream()
861 *
862 * \param[in] fp file stream
863 * \return recog, or NULL on error
864 */
865 L_RECOG *
recogReadStream(FILE * fp)866 recogReadStream(FILE *fp)
867 {
868 l_int32 version, setsize, threshold, scalew, scaleh, linew;
869 l_int32 maxyshift, nc;
870 L_DNA *dna_tochar;
871 PIXAA *paa;
872 L_RECOG *recog;
873 SARRAY *sa_text;
874
875 PROCNAME("recogReadStream");
876
877 if (!fp)
878 return (L_RECOG *)ERROR_PTR("stream not defined", procName, NULL);
879
880 if (fscanf(fp, "\nRecog Version %d\n", &version) != 1)
881 return (L_RECOG *)ERROR_PTR("not a recog file", procName, NULL);
882 if (version != RECOG_VERSION_NUMBER)
883 return (L_RECOG *)ERROR_PTR("invalid recog version", procName, NULL);
884 if (fscanf(fp, "Size of character set = %d\n", &setsize) != 1)
885 return (L_RECOG *)ERROR_PTR("setsize not read", procName, NULL);
886 if (fscanf(fp, "Binarization threshold = %d\n", &threshold) != 1)
887 return (L_RECOG *)ERROR_PTR("binary thresh not read", procName, NULL);
888 if (fscanf(fp, "Maxyshift = %d\n", &maxyshift) != 1)
889 return (L_RECOG *)ERROR_PTR("maxyshift not read", procName, NULL);
890 if (fscanf(fp, "Scale to width = %d\n", &scalew) != 1)
891 return (L_RECOG *)ERROR_PTR("width not read", procName, NULL);
892 if (fscanf(fp, "Scale to height = %d\n", &scaleh) != 1)
893 return (L_RECOG *)ERROR_PTR("height not read", procName, NULL);
894 if (fscanf(fp, "Normalized line width = %d\n", &linew) != 1)
895 return (L_RECOG *)ERROR_PTR("line width not read", procName, NULL);
896 if ((recog = recogCreate(scalew, scaleh, linew, threshold,
897 maxyshift)) == NULL)
898 return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL);
899
900 if (fscanf(fp, "\nLabels for character set:\n") != 0) {
901 recogDestroy(&recog);
902 return (L_RECOG *)ERROR_PTR("label intro not read", procName, NULL);
903 }
904 l_dnaDestroy(&recog->dna_tochar);
905 if ((dna_tochar = l_dnaReadStream(fp)) == NULL) {
906 recogDestroy(&recog);
907 return (L_RECOG *)ERROR_PTR("dna_tochar not read", procName, NULL);
908 }
909 recog->dna_tochar = dna_tochar;
910 sarrayDestroy(&recog->sa_text);
911 if ((sa_text = sarrayReadStream(fp)) == NULL) {
912 recogDestroy(&recog);
913 return (L_RECOG *)ERROR_PTR("sa_text not read", procName, NULL);
914 }
915 recog->sa_text = sa_text;
916
917 if (fscanf(fp, "\nPixaa of all samples in the training set:\n") != 0) {
918 recogDestroy(&recog);
919 return (L_RECOG *)ERROR_PTR("pixaa intro not read", procName, NULL);
920 }
921 if ((paa = pixaaReadStream(fp)) == NULL) {
922 recogDestroy(&recog);
923 return (L_RECOG *)ERROR_PTR("pixaa not read", procName, NULL);
924 }
925 recog->setsize = setsize;
926 nc = pixaaGetCount(paa, NULL);
927 if (nc != setsize) {
928 recogDestroy(&recog);
929 pixaaDestroy(&paa);
930 L_ERROR("(setsize = %d) != (paa count = %d)\n", procName,
931 setsize, nc);
932 return NULL;
933 }
934
935 recogAddAllSamples(&recog, paa, 0); /* this finishes */
936 pixaaDestroy(&paa);
937 if (!recog)
938 return (L_RECOG *)ERROR_PTR("bad templates", procName, NULL);
939 return recog;
940 }
941
942
943 /*!
944 * \brief recogReadMem()
945 *
946 * \param[in] data serialization of recog (not ascii)
947 * \param[in] size of data in bytes
948 * \return recog, or NULL on error
949 */
950 L_RECOG *
recogReadMem(const l_uint8 * data,size_t size)951 recogReadMem(const l_uint8 *data,
952 size_t size)
953 {
954 FILE *fp;
955 L_RECOG *recog;
956
957 PROCNAME("recogReadMem");
958
959 if (!data)
960 return (L_RECOG *)ERROR_PTR("data not defined", procName, NULL);
961 if ((fp = fopenReadFromMemory(data, size)) == NULL)
962 return (L_RECOG *)ERROR_PTR("stream not opened", procName, NULL);
963
964 recog = recogReadStream(fp);
965 fclose(fp);
966 if (!recog) L_ERROR("recog not read\n", procName);
967 return recog;
968 }
969
970
971 /*!
972 * \brief recogWrite()
973 *
974 * \param[in] filename
975 * \param[in] recog
976 * \return 0 if OK, 1 on error
977 *
978 * <pre>
979 * Notes:
980 * (1) The pixaa of templates that is written is the modified one
981 * in the pixaa field. It is the pixaa that is actually used
982 * for correlation. This is not the unscaled array of labeled
983 * bitmaps, in pixaa_u, that was used to generate the recog in the
984 * first place. See the notes in recogRead() for the rationale.
985 * </pre>
986 */
987 l_int32
recogWrite(const char * filename,L_RECOG * recog)988 recogWrite(const char *filename,
989 L_RECOG *recog)
990 {
991 l_int32 ret;
992 FILE *fp;
993
994 PROCNAME("recogWrite");
995
996 if (!filename)
997 return ERROR_INT("filename not defined", procName, 1);
998 if (!recog)
999 return ERROR_INT("recog not defined", procName, 1);
1000
1001 if ((fp = fopenWriteStream(filename, "wb")) == NULL)
1002 return ERROR_INT("stream not opened", procName, 1);
1003 ret = recogWriteStream(fp, recog);
1004 fclose(fp);
1005 if (ret)
1006 return ERROR_INT("recog not written to stream", procName, 1);
1007 return 0;
1008 }
1009
1010
1011 /*!
1012 * \brief recogWriteStream()
1013 *
1014 * \param[in] fp file stream opened for "wb"
1015 * \param[in] recog
1016 * \return 0 if OK, 1 on error
1017 */
1018 l_int32
recogWriteStream(FILE * fp,L_RECOG * recog)1019 recogWriteStream(FILE *fp,
1020 L_RECOG *recog)
1021 {
1022 PROCNAME("recogWriteStream");
1023
1024 if (!fp)
1025 return ERROR_INT("stream not defined", procName, 1);
1026 if (!recog)
1027 return ERROR_INT("recog not defined", procName, 1);
1028
1029 fprintf(fp, "\nRecog Version %d\n", RECOG_VERSION_NUMBER);
1030 fprintf(fp, "Size of character set = %d\n", recog->setsize);
1031 fprintf(fp, "Binarization threshold = %d\n", recog->threshold);
1032 fprintf(fp, "Maxyshift = %d\n", recog->maxyshift);
1033 fprintf(fp, "Scale to width = %d\n", recog->scalew);
1034 fprintf(fp, "Scale to height = %d\n", recog->scaleh);
1035 fprintf(fp, "Normalized line width = %d\n", recog->linew);
1036 fprintf(fp, "\nLabels for character set:\n");
1037 l_dnaWriteStream(fp, recog->dna_tochar);
1038 sarrayWriteStream(fp, recog->sa_text);
1039 fprintf(fp, "\nPixaa of all samples in the training set:\n");
1040 pixaaWriteStream(fp, recog->pixaa);
1041
1042 return 0;
1043 }
1044
1045
1046 /*!
1047 * \brief recogWriteMem()
1048 *
1049 * \param[out] pdata data of serialized recog (not ascii)
1050 * \param[out] psize size of returned data
1051 * \param[in] recog
1052 * \return 0 if OK, 1 on error
1053 *
1054 * <pre>
1055 * Notes:
1056 * (1) Serializes a recog in memory and puts the result in a buffer.
1057 * </pre>
1058 */
1059 l_int32
recogWriteMem(l_uint8 ** pdata,size_t * psize,L_RECOG * recog)1060 recogWriteMem(l_uint8 **pdata,
1061 size_t *psize,
1062 L_RECOG *recog)
1063 {
1064 l_int32 ret;
1065 FILE *fp;
1066
1067 PROCNAME("recogWriteMem");
1068
1069 if (pdata) *pdata = NULL;
1070 if (psize) *psize = 0;
1071 if (!pdata)
1072 return ERROR_INT("&data not defined", procName, 1);
1073 if (!psize)
1074 return ERROR_INT("&size not defined", procName, 1);
1075 if (!recog)
1076 return ERROR_INT("recog not defined", procName, 1);
1077
1078 #if HAVE_FMEMOPEN
1079 if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1080 return ERROR_INT("stream not opened", procName, 1);
1081 ret = recogWriteStream(fp, recog);
1082 #else
1083 L_INFO("work-around: writing to a temp file\n", procName);
1084 #ifdef _WIN32
1085 if ((fp = fopenWriteWinTempfile()) == NULL)
1086 return ERROR_INT("tmpfile stream not opened", procName, 1);
1087 #else
1088 if ((fp = tmpfile()) == NULL)
1089 return ERROR_INT("tmpfile stream not opened", procName, 1);
1090 #endif /* _WIN32 */
1091 ret = recogWriteStream(fp, recog);
1092 rewind(fp);
1093 *pdata = l_binaryReadStream(fp, psize);
1094 #endif /* HAVE_FMEMOPEN */
1095 fclose(fp);
1096 return ret;
1097 }
1098
1099
1100 /*!
1101 * \brief recogExtractPixa()
1102 *
1103 * \param[in] recog
1104 * \return pixa if OK, NULL on error
1105 *
1106 * <pre>
1107 * Notes:
1108 * (1) This generates a pixa of all the unscaled images in the
1109 * recognizer, where each one has its character class label in
1110 * the pix text field, by flattening pixaa_u to a pixa.
1111 * </pre>
1112 */
1113 PIXA *
recogExtractPixa(L_RECOG * recog)1114 recogExtractPixa(L_RECOG *recog)
1115 {
1116 PROCNAME("recogExtractPixa");
1117
1118 if (!recog)
1119 return (PIXA *)ERROR_PTR("recog not defined", procName, NULL);
1120
1121 recogAddCharstrLabels(recog);
1122 return pixaaFlattenToPixa(recog->pixaa_u, NULL, L_CLONE);
1123 }
1124
1125
1126 /*!
1127 * \brief recogAddCharstrLabels()
1128 *
1129 * \param[in] recog
1130 * \return 0 if OK, 1 on error
1131 */
1132 static l_int32
recogAddCharstrLabels(L_RECOG * recog)1133 recogAddCharstrLabels(L_RECOG *recog)
1134 {
1135 char *text;
1136 l_int32 i, j, n1, n2;
1137 PIX *pix;
1138 PIXA *pixa;
1139 PIXAA *paa;
1140
1141 PROCNAME("recogAddCharstrLabels");
1142
1143 if (!recog)
1144 return ERROR_INT("recog not defined", procName, 1);
1145
1146 /* Add the labels to each unscaled pix */
1147 paa = recog->pixaa_u;
1148 n1 = pixaaGetCount(paa, NULL);
1149 for (i = 0; i < n1; i++) {
1150 pixa = pixaaGetPixa(paa, i, L_CLONE);
1151 text = sarrayGetString(recog->sa_text, i, L_NOCOPY);
1152 n2 = pixaGetCount(pixa);
1153 for (j = 0; j < n2; j++) {
1154 pix = pixaGetPix(pixa, j, L_CLONE);
1155 pixSetText(pix, text);
1156 pixDestroy(&pix);
1157 }
1158 pixaDestroy(&pixa);
1159 }
1160
1161 return 0;
1162 }
1163
1164
1165 /*!
1166 * \brief recogAddAllSamples()
1167 *
1168 * \param[in] precog addr of recog
1169 * \param[in] paa pixaa from previously trained recog
1170 * \param[in] debug
1171 * \return 0 if OK, 1 on error
1172 *
1173 * <pre>
1174 * Notes:
1175 * (1) On error, the input recog is destroyed.
1176 * (2) This is used with the serialization routine recogRead(),
1177 * where each pixa in the pixaa represents a set of characters
1178 * in a different class. Before calling this function, we have
1179 * verified that the number of character classes, given by the
1180 * setsize field in %recog, equals the number of pixa in the paa.
1181 * The character labels for each set are in the sa_text field.
1182 * </pre>
1183 */
1184 static l_int32
recogAddAllSamples(L_RECOG ** precog,PIXAA * paa,l_int32 debug)1185 recogAddAllSamples(L_RECOG **precog,
1186 PIXAA *paa,
1187 l_int32 debug)
1188 {
1189 char *text;
1190 l_int32 i, j, nc, ns;
1191 PIX *pix;
1192 PIXA *pixa, *pixa1;
1193 L_RECOG *recog;
1194
1195 PROCNAME("recogAddAllSamples");
1196
1197 if (!precog)
1198 return ERROR_INT("&recog not defined", procName, 1);
1199 if ((recog = *precog) == NULL)
1200 return ERROR_INT("recog not defined", procName, 1);
1201 if (!paa) {
1202 recogDestroy(&recog);
1203 return ERROR_INT("paa not defined", procName, 1);
1204 }
1205
1206 nc = pixaaGetCount(paa, NULL);
1207 for (i = 0; i < nc; i++) {
1208 pixa = pixaaGetPixa(paa, i, L_CLONE);
1209 ns = pixaGetCount(pixa);
1210 text = sarrayGetString(recog->sa_text, i, L_NOCOPY);
1211 pixa1 = pixaCreate(ns);
1212 pixaaAddPixa(recog->pixaa_u, pixa1, L_INSERT);
1213 for (j = 0; j < ns; j++) {
1214 pix = pixaGetPix(pixa, j, L_CLONE);
1215 if (debug) fprintf(stderr, "pix[%d,%d]: text = %s\n", i, j, text);
1216 pixaaAddPix(recog->pixaa_u, i, pix, NULL, L_INSERT);
1217 }
1218 pixaDestroy(&pixa);
1219 }
1220
1221 recogTrainingFinished(&recog, 0, -1, -1.0); /* For second parameter,
1222 see comment in recogRead() */
1223 if (!recog)
1224 return ERROR_INT("bad templates; recog destroyed", procName, 1);
1225 return 0;
1226 }
1227