• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**********************************************************************
2  * File:        tessedit.cpp  (Formerly tessedit.c)
3  * Description: Main program for merge of tess and editor.
4  * Author:                  Ray Smith
5  * Created:                 Tue Jan 07 15:21:46 GMT 1992
6  *
7  * (C) Copyright 1992, Hewlett-Packard Ltd.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include "mfcpch.h"
21 #include "applybox.h"
22 #include "control.h"
23 #include "tessvars.h"
24 #include "tessedit.h"
25 #include "baseapi.h"
26 #include "thresholder.h"
27 #include "pageres.h"
28 #include "imgs.h"
29 #include "varabled.h"
30 #include "tprintf.h"
31 #include "tesseractmain.h"
32 #include "stderr.h"
33 #include "notdll.h"
34 #include "mainblk.h"
35 #include "output.h"
36 #include "globals.h"
37 #include "helpers.h"
38 #include "blread.h"
39 #include "tfacep.h"
40 #include "callnet.h"
41 
42 // Include automatically generated configuration file if running autoconf
43 #ifdef HAVE_CONFIG_H
44 #include "config_auto.h"
45 #endif
46 #ifdef HAVE_LIBTIFF
47 #include "tiffio.h"
48 #endif
49 #ifdef HAVE_LIBLEPT
50 #include "allheaders.h"
51 #else
52 class Pix;
53 #endif
54 
55 #ifdef _TIFFIO_
56 void read_tiff_image(TIFF* tif, IMAGE* image);
57 #endif
58 
59 #define VARDIR        "configs/" /*variables files */
60                                  //config under api
61 #define API_CONFIG      "configs/api_config"
62 #define EXTERN
63 
64 BOOL_VAR(tessedit_create_boxfile, FALSE, "Output text with boxes");
65 BOOL_VAR(tessedit_read_image, TRUE, "Ensure the image is read");
66 INT_VAR(tessedit_serial_unlv, 0,
67         "0->Whole page, 1->serial no adapt, 2->serial with adapt");
68 INT_VAR(tessedit_page_number, -1,
69         "-1 -> All pages, else specifc page to process");
70 BOOL_VAR(tessedit_write_images, FALSE, "Capture the image from the IPE");
71 BOOL_VAR(tessedit_debug_to_screen, FALSE, "Dont use debug file");
72 
73 const int kMaxIntSize = 22;
74 const ERRCODE USAGE = "Usage";
75 char szAppName[] = "Tessedit";   //app name
76 
77 // Recognize a single page, given by the (const) image, and output the text,
78 // as controlled by global flag variables into the output text_out STRING:
79 // tessedit_serial_unlv is the top-level control, and provides 3 ways of
80 // treating the UNLV zones with the adaptive classifier:
81 // case 0: if there is a unlv zone file present, use it to segment the page
82 // and process the zones in parallel (pass 1 on all, then pass2 on all),
83 // otherwise, treat the whole page as a single zone.
84 // Independently of the existence of the unlv zone file:
85 // if tessedit_create_boxfile, output text in ".box" training file format, with
86 // one recognizable unit (as UTF8 characters) per line and its bounding box
87 // coded in UTF8(equivalent to ascii) for generating training data by hand.
88 // else if tessedit_write_unlv, output text in Latin-1, with a few special
89 // hacks for the UNLV test environment. Only works for latin!
90 // else (default mode) write plain text in UTF-8.
91 // case 1:(tessedit_serial_unlv) Read a unlv zone file (and fail if not found)
92 // and treat each zone as an independent "page", including resetting the
93 // adaptive classifier between zones.
94 // case 2: Read a unlv zone file (fail if not found) and treat each zone as
95 // a page of a document, i.e. DON'T reset the adaptive classifier between
96 // zones.
97 // In case 1 and 2, the UNLV zone file name is derived from input_file, by
98 // replacing the last 4 characters with ".uzn". In case 0, the unlv zone
99 // file name is derived from the 2nd parameter to InitWithLanguage, and
100 // the value of input_file is ignored - ugly, but true - a consequence of
101 // the way that unlv zone file reading takes the place of a page layout
102 // analyzer.
TesseractImage(const char * input_file,IMAGE * image,Pix * pix,tesseract::TessBaseAPI * api,STRING * text_out)103 void TesseractImage(const char* input_file, IMAGE* image, Pix* pix,
104                     tesseract::TessBaseAPI* api, STRING* text_out) {
105   api->SetInputName(input_file);
106 #ifdef HAVE_LIBLEPT
107   if (pix != NULL) {
108     api->SetImage(pix);
109   } else {
110 #endif
111     int bytes_per_line = check_legal_image_size(image->get_xsize(),
112                                                 image->get_ysize(),
113                                                 image->get_bpp());
114     api->SetImage(image->get_buffer(), image->get_xsize(), image->get_ysize(),
115                   image->get_bpp() / 8, bytes_per_line);
116 #ifdef HAVE_LIBLEPT
117   }
118 #endif
119   if (tessedit_serial_unlv == 0) {
120     char* text;
121     if (tessedit_create_boxfile)
122       text = api->GetBoxText();
123     else if (tessedit_write_unlv)
124       text = api->GetUNLVText();
125     else
126       text = api->GetUTF8Text();
127     *text_out += text;
128     delete [] text;
129   } else {
130     BLOCK_LIST blocks;
131     STRING filename = input_file;
132     const char* lastdot = strrchr(filename.string(), '.');
133     if (lastdot != NULL) {
134       filename[lastdot - filename.string()] = '\0';
135     }
136     if (!read_unlv_file(filename, image->get_xsize(), image->get_ysize(),
137                         &blocks)) {
138       fprintf(stderr, "Error: Must have a unlv zone file %s to read!\n",
139               filename.string());
140       return;
141     }
142     BLOCK_IT b_it = &blocks;
143     for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
144       BLOCK* block = b_it.data();
145       TBOX box = block->bounding_box();
146       api->SetRectangle(box.left(), image->get_ysize() - box.top(),
147                         box.width(), box.height());
148       char* text = api->GetUNLVText();
149       *text_out += text;
150       delete [] text;
151       if (tessedit_serial_unlv == 1)
152         api->ClearAdaptiveClassifier();
153     }
154   }
155   if (tessedit_write_images) {
156     page_image.write("tessinput.tif");
157   }
158 }
159 
160 /**********************************************************************
161  *  main()
162  *
163  **********************************************************************/
164 
main(int argc,char ** argv)165 int main(int argc, char **argv) {
166   STRING outfile;               //output file
167 
168   if (argc < 3) {
169     USAGE.error (argv[0], EXIT,
170       "%s imagename outputbase [-l lang] [configfile [[+|-]varfile]...]\n"
171 #if !defined(HAVE_LIBLEPT) && !defined(_TIFFIO_)
172       "Warning - no liblept or libtiff - cannot read compressed tiff files.\n"
173 #endif
174       , argv[0]);
175   }
176   // Find the required language.
177   const char* lang = "eng";
178   int arg = 3;
179   if (argc >= 5 && strcmp(argv[3], "-l") == 0) {
180     lang = argv[4];
181     arg = 5;
182   }
183 
184   tesseract::TessBaseAPI  api;
185 
186   api.SetOutputName(argv[2]);
187   api.Init(argv[0], lang, &(argv[arg]), argc-arg, false);
188   api.SetPageSegMode(tesseract::PSM_AUTO);
189 
190   tprintf ("Tesseract Open Source OCR Engine %s\n",
191 #if defined(HAVE_LIBLEPT)
192            "with Leptonica");
193 #elif defined(_TIFFIO_)
194            "with LibTiff");
195 #else
196            "");
197 #endif
198 
199   IMAGE image;
200   STRING text_out;
201 #ifdef HAVE_LIBLEPT
202   // Use leptonica to read images.
203   // If the image fails to read, try it as a list of filenames.
204   PIX* pix = pixRead(argv[1]);
205   if (pix == NULL) {
206     FILE* fp = fopen(argv[1], "r");
207     if (fp == NULL)
208       READFAILED.error(argv[0], EXIT, argv[1]);
209     char filename[MAX_PATH];
210     while (fgets(filename, sizeof(filename), fp) != NULL) {
211       chomp_string(filename);
212       pix = pixRead(filename);
213       if (pix == NULL)
214         READFAILED.error(argv[0], EXIT, argv[1]);
215       TesseractImage(argv[1], NULL, pix, &api, &text_out);
216       pixDestroy(&pix);
217     }
218     fclose(fp);
219   } else {
220     TesseractImage(argv[1], NULL, pix, &api, &text_out);
221     pixDestroy(&pix);
222   }
223 #else
224 #ifdef _TIFFIO_
225   int len = strlen(argv[1]);
226   if (len > 3 && strcmp("tif", argv[1] + len - 3) == 0) {
227     // Use libtiff to read a tif file so multi-page can be handled.
228     // The page number so the tiff file can be closed and reopened.
229     int page_number = tessedit_page_number;
230     if (page_number < 0)
231       page_number = 0;
232     TIFF* archive = NULL;
233     do {
234       // Since libtiff keeps all read images in memory we have to close the
235       // file and reopen it for every page, and seek to the appropriate page.
236       if (archive != NULL)
237         TIFFClose(archive);
238       archive = TIFFOpen(argv[1], "r");
239       if (archive == NULL) {
240         READFAILED.error (argv[0], EXIT, argv[1]);
241         return 1;
242       }
243       if (page_number > 0)
244         tprintf("Page %d\n", page_number);
245 
246       // Seek to the appropriate page.
247       for (int i = 0; i < page_number; ++i) {
248         TIFFReadDirectory(archive);
249       }
250       char page_str[kMaxIntSize];
251       snprintf(page_str, kMaxIntSize - 1, "%d", page_number);
252       api.SetVariable("applybox_page", page_str);
253       ++page_number;
254       // Read the current page into the Tesseract image.
255       IMAGE image;
256       read_tiff_image(archive, &image);
257 
258       // Run tesseract on the page!
259       TesseractImage(argv[1], &image, NULL, &api, &text_out);
260     // Do this while there are more pages in the tiff file.
261     } while (TIFFReadDirectory(archive) &&
262              (page_number <= tessedit_page_number || tessedit_page_number < 0));
263     TIFFClose(archive);
264   } else {
265 #endif
266     // Using built-in image library to read bmp, or tiff without libtiff.
267     if (image.read_header(argv[1]) < 0)
268       READFAILED.error (argv[0], EXIT, argv[1]);
269     if (image.read(image.get_ysize ()) < 0)
270       MEMORY_OUT.error(argv[0], EXIT, "Read of image %s", argv[1]);
271     TesseractImage(argv[1], &image, NULL, &api, &text_out);
272 #ifdef _TIFFIO_
273   }
274 #endif
275 #endif  // HAVE_LIBLEPT
276 
277   outfile = argv[2];
278   outfile += ".txt";
279   FILE* fp = fopen(outfile.string(), "w");
280   if (fp != NULL) {
281     fwrite(text_out.string(), 1, text_out.length(), fp);
282     fclose(fp);
283   }
284 
285   return 0;                      //Normal exit
286 }
287 
288 #ifdef __MSW32__
289 int initialized = 0;
290 
291 /**********************************************************************
292  * WinMain
293  *
294  * Main function for a windows program.
295  **********************************************************************/
296 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)297 int WINAPI WinMain(  //main for windows //command line
298                    HINSTANCE hInstance,
299                    HINSTANCE hPrevInstance,
300                    LPSTR lpszCmdLine,
301                    int nCmdShow) {
302   WNDCLASS wc;
303   HWND hwnd;
304   MSG msg;
305 
306   char **argv;
307   char *argsin[2];
308   int argc;
309   int exit_code;
310 
311   wc.style = CS_NOCLOSE | CS_OWNDC;
312   wc.lpfnWndProc = (WNDPROC) WndProc;
313   wc.cbClsExtra = 0;
314   wc.cbWndExtra = 0;
315   wc.hInstance = hInstance;
316   wc.hIcon = NULL;               //LoadIcon (NULL, IDI_APPLICATION);
317   wc.hCursor = NULL;             //LoadCursor (NULL, IDC_ARROW);
318   wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
319   wc.lpszMenuName = NULL;
320   wc.lpszClassName = szAppName;
321 
322   RegisterClass(&wc);
323 
324   hwnd = CreateWindow (szAppName, szAppName,
325     WS_OVERLAPPEDWINDOW | WS_DISABLED,
326     CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
327     CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
328 
329   argsin[0] = strdup (szAppName);
330   argsin[1] = strdup (lpszCmdLine);
331   /*allocate memory for the args. There can never be more than half*/
332   /*the total number of characters in the arguments.*/
333   argv =
334     (char **) malloc (((strlen (argsin[0]) + strlen (argsin[1])) / 2 + 1) *
335     sizeof (char *));
336 
337   /*now construct argv as it should be for C.*/
338   argc = parse_args (2, argsin, argv);
339 
340   //  ShowWindow (hwnd, nCmdShow);
341   //  UpdateWindow (hwnd);
342 
343   if (initialized) {
344     exit_code = main (argc, argv);
345     free (argsin[0]);
346     free (argsin[1]);
347     free(argv);
348     return exit_code;
349   }
350   while (GetMessage (&msg, NULL, 0, 0)) {
351     TranslateMessage(&msg);
352     DispatchMessage(&msg);
353     if (initialized) {
354       exit_code = main (argc, argv);
355       break;
356     }
357     else
358       exit_code = msg.wParam;
359   }
360   free (argsin[0]);
361   free (argsin[1]);
362   free(argv);
363   return exit_code;
364 }
365 
366 
367 /**********************************************************************
368  * WndProc
369  *
370  * Function to respond to messages.
371  **********************************************************************/
372 
WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)373 LONG WINAPI WndProc(            //message handler
374                     HWND hwnd,  //window with message
375                     UINT msg,   //message typ
376                     WPARAM wParam,
377                     LPARAM lParam) {
378   HDC hdc;
379 
380   if (msg == WM_CREATE) {
381     //
382     // Create a rendering context.
383     //
384     hdc = GetDC (hwnd);
385     ReleaseDC(hwnd, hdc);
386     initialized = 1;
387     return 0;
388   }
389   return DefWindowProc (hwnd, msg, wParam, lParam);
390 }
391 
392 
393 /**********************************************************************
394  * parse_args
395  *
396  * Turn a list of args into a new list of args with each separate
397  * whitespace spaced string being an arg.
398  **********************************************************************/
399 
400 int
parse_args(int argc,char * argv[],char * arglist[])401 parse_args (                     /*refine arg list */
402 int argc,                        /*no of input args */
403 char *argv[],                    /*input args */
404 char *arglist[]                  /*output args */
405 ) {
406   int argcount;                  /*converted argc */
407   char *testchar;                /*char in option string */
408   int arg;                       /*current argument */
409 
410   argcount = 0;                  /*no of options */
411   for (arg = 0; arg < argc; arg++) {
412     testchar = argv[arg];        /*start of arg */
413     do {
414       while (*testchar
415         && (*testchar == ' ' || *testchar == '\n'
416         || *testchar == '\t'))
417         testchar++;              /*skip white space */
418       if (*testchar) {
419                                  /*new arg */
420         arglist[argcount++] = testchar;
421                                  /*skip to white space */
422         for (testchar++; *testchar && *testchar != ' ' && *testchar != '\n' && *testchar != '\t'; testchar++);
423         if (*testchar)
424           *testchar++ = '\0';    /*turn to separate args */
425       }
426     }
427     while (*testchar);
428   }
429   return argcount;               /*new number of args */
430 }
431 #endif
432