From 22cea20a907832c436f9c25524775ca36fece3e6 Mon Sep 17 00:00:00 2001 From: jendib Date: Thu, 5 Sep 2013 16:10:26 +0200 Subject: [PATCH] Hard coupling between tess4j and imageIO to avoid service registering --- docs-core/pom.xml | 5 - .../java/net/sourceforge/tess4j/TessAPI.java | 686 ++++++++++++++++++ .../net/sourceforge/tess4j/Tesseract.java | 258 +++++++ .../tess4j/TesseractException.java | 38 + .../net/sourceforge/vietocr/ImageHelper.java | 173 +++++ .../sourceforge/vietocr/ImageIOHelper.java | 128 ++++ docs-parent/TODO | 2 +- docs-parent/lib/tess4j.jar | Bin 66123 -> 0 bytes docs-parent/pom.xml | 22 - 9 files changed, 1284 insertions(+), 28 deletions(-) create mode 100644 docs-core/src/main/java/net/sourceforge/tess4j/TessAPI.java create mode 100644 docs-core/src/main/java/net/sourceforge/tess4j/Tesseract.java create mode 100644 docs-core/src/main/java/net/sourceforge/tess4j/TesseractException.java create mode 100644 docs-core/src/main/java/net/sourceforge/vietocr/ImageHelper.java create mode 100644 docs-core/src/main/java/net/sourceforge/vietocr/ImageIOHelper.java delete mode 100644 docs-parent/lib/tess4j.jar diff --git a/docs-core/pom.xml b/docs-core/pom.xml index 5777ab3f..f9d5932d 100644 --- a/docs-core/pom.xml +++ b/docs-core/pom.xml @@ -138,11 +138,6 @@ imageio - - tess4j - tess4j - - junit diff --git a/docs-core/src/main/java/net/sourceforge/tess4j/TessAPI.java b/docs-core/src/main/java/net/sourceforge/tess4j/TessAPI.java new file mode 100644 index 00000000..b48782da --- /dev/null +++ b/docs-core/src/main/java/net/sourceforge/tess4j/TessAPI.java @@ -0,0 +1,686 @@ +/** + * Copyright @ 2012 Quan Nguyen + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package net.sourceforge.tess4j; + +import com.sun.jna.*; +import com.sun.jna.ptr.*; +import java.nio.*; + +/** + * A Java wrapper for + * Tesseract OCR 3.02 API using + * JNA Interface Mapping. + */ +public interface TessAPI extends Library { + + static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().startsWith("windows"); + /** + * Native library name. + */ + public static final String LIB_NAME = "libtesseract302"; + public static final String LIB_NAME_NON_WIN = "tesseract"; + /** + * An instance of the class library. + */ + public static final TessAPI INSTANCE = (TessAPI) Native.loadLibrary(WINDOWS ? LIB_NAME : LIB_NAME_NON_WIN, TessAPI.class); + + /** + * When Tesseract/Cube is initialized we can choose to instantiate/load/run + * only the Tesseract part, only the Cube part or both along with the + * combiner. The preference of which engine to use is stored in + * tessedit_ocr_engine_mode.

ATTENTION: When + * modifying this enum, please make sure to make the appropriate changes to + * all the enums mirroring it (e.g. OCREngine in + * cityblock/workflow/detection/detection_storage.proto). Such enums will + * mention the connection to OcrEngineMode in the comments. + */ + public static interface TessOcrEngineMode { + + public static final int OEM_TESSERACT_ONLY = (int) 0; + public static final int OEM_CUBE_ONLY = (int) 1; + public static final int OEM_TESSERACT_CUBE_COMBINED = (int) 2; + public static final int OEM_DEFAULT = (int) 3; + }; + + /** + * Possible modes for page layout analysis. These *must* be kept in order of + * decreasing amount of layout analysis to be done, except for + * OSD_ONLY, so that the inequality test macros below work. + */ + public static interface TessPageSegMode { + + public static final int PSM_OSD_ONLY = (int) 0; + public static final int PSM_AUTO_OSD = (int) 1; + public static final int PSM_AUTO_ONLY = (int) 2; + public static final int PSM_AUTO = (int) 3; + public static final int PSM_SINGLE_COLUMN = (int) 4; + public static final int PSM_SINGLE_BLOCK_VERT_TEXT = (int) 5; + public static final int PSM_SINGLE_BLOCK = (int) 6; + public static final int PSM_SINGLE_LINE = (int) 7; + public static final int PSM_SINGLE_WORD = (int) 8; + public static final int PSM_CIRCLE_WORD = (int) 9; + public static final int PSM_SINGLE_CHAR = (int) 10; + public static final int PSM_COUNT = (int) 11; + }; + + /** + * Enum of the elements of the page hierarchy, used in + * ResultIterator to provide functions that operate on each + * level without having to have 5x as many functions. + */ + public static interface TessPageIteratorLevel { + + public static final int RIL_BLOCK = (int) 0; + public static final int RIL_PARA = (int) 1; + public static final int RIL_TEXTLINE = (int) 2; + public static final int RIL_WORD = (int) 3; + public static final int RIL_SYMBOL = (int) 4; + }; + + public static interface TessPolyBlockType { + + public static final int PT_UNKNOWN = (int) 0; + public static final int PT_FLOWING_TEXT = (int) 1; + public static final int PT_HEADING_TEXT = (int) 2; + public static final int PT_PULLOUT_TEXT = (int) 3; + public static final int PT_TABLE = (int) 4; + public static final int PT_VERTICAL_TEXT = (int) 5; + public static final int PT_CAPTION_TEXT = (int) 6; + public static final int PT_FLOWING_IMAGE = (int) 7; + public static final int PT_HEADING_IMAGE = (int) 8; + public static final int PT_PULLOUT_IMAGE = (int) 9; + public static final int PT_HORZ_LINE = (int) 10; + public static final int PT_VERT_LINE = (int) 11; + public static final int PT_NOISE = (int) 12; + public static final int PT_COUNT = (int) 13; + }; + + /** + *
+     *  +------------------+
+     *  | 1 Aaaa Aaaa Aaaa |
+     *  | Aaa aa aaa aa    |
+     *  | aaaaaa A aa aaa. |
+     *  |                2 |
+     *  |   #######  c c C |
+     *  |   #######  c c c |
+     *  | < #######  c c c |
+     *  | < #######  c   c |
+     *  | < #######  .   c |
+     *  | 3 #######      c |
+     *  +------------------+
+     * 
+ * Orientation Example:
+ * ====================
+ * Above is a + * diagram of some (1) English and (2) Chinese text and a (3) photo + * credit.
+ *
+ * Upright Latin characters are represented as A and a. '<' represents + * a latin character rotated anti-clockwise 90 degrees. Upright + * Chinese characters are represented C and c.
+ *
+ * NOTA BENE: enum values here should match goodoc.proto
+ *
+ * If you orient your head so that "up" aligns with Orientation, then + * the characters will appear "right side up" and readable.
+ *
+ * In the example above, both the + * English and Chinese paragraphs are oriented so their "up" is the top of + * the page (page up). The photo credit is read with one's head turned + * leftward ("up" is to page left).
+ *
The values of this enum + * match the convention of Tesseract's osdetect.h + */ + public static interface TessOrientation { + + public static final int ORIENTATION_PAGE_UP = (int) 0; + public static final int ORIENTATION_PAGE_RIGHT = (int) 1; + public static final int ORIENTATION_PAGE_DOWN = (int) 2; + public static final int ORIENTATION_PAGE_LEFT = (int) 3; + }; + + /** + * The grapheme clusters within a line of text are laid out logically in + * this direction, judged when looking at the text line rotated so that its + * Orientation is "page up".

For English text, the writing + * direction is left-to-right. For the Chinese text in the above example, + * the writing direction is top-to-bottom. + */ + public static interface TessWritingDirection { + + public static final int WRITING_DIRECTION_LEFT_TO_RIGHT = (int) 0; + public static final int WRITING_DIRECTION_RIGHT_TO_LEFT = (int) 1; + public static final int WRITING_DIRECTION_TOP_TO_BOTTOM = (int) 2; + }; + + /** + * The text lines are read in the given sequence.

In English, + * the order is top-to-bottom. In Chinese, vertical text lines are read + * right-to-left. Mongolian is written in vertical columns top to bottom + * like Chinese, but the lines order left-to right.

Note that + * only some combinations make sense. For example, + * WRITING_DIRECTION_LEFT_TO_RIGHT implies + * TEXTLINE_ORDER_TOP_TO_BOTTOM. + */ + public static interface TessTextlineOrder { + + public static final int TEXTLINE_ORDER_LEFT_TO_RIGHT = (int) 0; + public static final int TEXTLINE_ORDER_RIGHT_TO_LEFT = (int) 1; + public static final int TEXTLINE_ORDER_TOP_TO_BOTTOM = (int) 2; + }; + public static final int TRUE = (int) 1; + public static final int FALSE = (int) 0; + + /** + * Returns the version identifier. + */ + String TessVersion(); + + void TessDeleteText(Pointer text); + + void TessDeleteTextArray(PointerByReference arr); + + void TessDeleteIntArray(IntBuffer arr); + + /** + * Creates an instance of the base class for all Tesseract APIs. + */ + TessAPI.TessBaseAPI TessBaseAPICreate(); + + /** + * Disposes the TesseractAPI instance. + */ + void TessBaseAPIDelete(TessAPI.TessBaseAPI handle); + + /** + * Set the name of the input file. Needed only for training and reading a + * UNLV zone file. + */ + void TessBaseAPISetInputName(TessAPI.TessBaseAPI handle, String name); + + /** + * Set the name of the bonus output files. Needed only for debugging. + */ + void TessBaseAPISetOutputName(TessAPI.TessBaseAPI handle, String name); + + /** + * Set the value of an internal "parameter." Supply the name of the + * parameter and the value as a string, just as you would in a config file. + * Returns false if the name lookup failed. E.g., + * SetVariable("tessedit_char_blacklist", "xyz"); to ignore x, + * y and z. Or + * SetVariable("classify_bln_numeric_mode", "1"); to set + * numeric-only mode. + * SetVariable may be used before + * Init, but settings will revert to defaults on + * End().

Note: Must be called after + * Init(). Only works for non-init variables (init variables + * should be passed to + * Init()). + */ + int TessBaseAPISetVariable(TessAPI.TessBaseAPI handle, String name, String value); + + /** + * Returns true (1) if the parameter was found among Tesseract parameters. + * Fills in value with the value of the parameter. + */ + int TessBaseAPIGetIntVariable(TessAPI.TessBaseAPI handle, String name, IntBuffer value); + + int TessBaseAPIGetBoolVariable(TessAPI.TessBaseAPI handle, String name, IntBuffer value); + + int TessBaseAPIGetDoubleVariable(TessAPI.TessBaseAPI handle, String name, DoubleBuffer value); + + String TessBaseAPIGetStringVariable(TessAPI.TessBaseAPI handle, String name); + + /** + * Print Tesseract parameters to the given file.

Note: Must not + * be the first method called after instance create. + */ + void TessBaseAPIPrintVariablesToFile(TessAPI.TessBaseAPI handle, String filename); + + /** + * Instances are now mostly thread-safe and totally independent, but some + * global parameters remain. Basically it is safe to use multiple + * TessBaseAPIs in different threads in parallel, UNLESS: you use + * SetVariable on some of the Params in classify and textord. + * If you do, then the effect will be to change it for all your + * instances.

Start tesseract. Returns zero on success and -1 + * on failure. NOTE that the only members that may be called before Init are + * those listed above here in the class definition.

The + * datapath must be the name of the parent directory of + * tessdata and must end in / . Any name after the last / will be stripped. + * The language is (usually) an + * ISO 639-3 string or + * NULL will default to eng. It is entirely safe (and + * eventually will be efficient too) to call Init multiple times on the same + * instance to change language, or just to reset the classifier. The + * language may be a string of the form [~][+[~]]* indicating + * that multiple languages are to be loaded. E.g., hin+eng will load Hindi + * and English. Languages may specify internally that they want to be loaded + * with one or more other languages, so the ~ sign is available to override + * that. E.g., if hin were set to load eng by default, then hin+~eng would + * force loading only hin. The number of loaded languages is limited only by + * memory, with the caveat that loading additional languages will impact + * both speed and accuracy, as there is more work to do to decide on the + * applicable language, and there is more chance of hallucinating incorrect + * words. WARNING: On changing languages, all Tesseract parameters are reset + * back to their default values. (Which may vary between languages.) If you + * have a rare need to set a Variable that controls initialization for a + * second call to + * Init you should explicitly call + * End() and then use + * SetVariable before + * Init. This is only a very rare use case, since there are + * very few uses that require any parameters to be set before + * Init.

If + * set_only_non_debug_params is true, only params that do not + * contain "debug" in the name will be set. + */ + int TessBaseAPIInit1(TessAPI.TessBaseAPI handle, String datapath, String language, int oem, PointerByReference configs, int configs_size); + + int TessBaseAPIInit2(TessAPI.TessBaseAPI handle, String datapath, String language, int oem); + + int TessBaseAPIInit3(TessAPI.TessBaseAPI handle, String datapath, String language); + + /** + * Returns the languages string used in the last valid initialization. If + * the last initialization specified "deu+hin" then that will be returned. + * If hin loaded eng automatically as well, then that will not be included + * in this list. To find the languages actually loaded, use + * GetLoadedLanguagesAsVector. The returned string should NOT + * be deleted. + */ + String TessBaseAPIGetInitLanguagesAsString(TessAPI.TessBaseAPI handle); + + /** + * Returns the loaded languages in the vector of STRINGs. Includes all + * languages loaded by the last + * Init, including those loaded as dependencies of other loaded + * languages. + */ + PointerByReference TessBaseAPIGetLoadedLanguagesAsVector(TessAPI.TessBaseAPI handle); + + /** + * Returns the available languages in the vector of STRINGs. + */ + PointerByReference TessBaseAPIGetAvailableLanguagesAsVector(TessAPI.TessBaseAPI handle); + + /** + * Init only the lang model component of Tesseract. The only functions that + * work after this init are + * SetVariable and + * IsValidWord. WARNING: temporary! This function will be + * removed from here and placed in a separate API at some future time. + */ + int TessBaseAPIInitLangMod(TessAPI.TessBaseAPI handle, String datapath, String language); + + /** + * Init only for page layout analysis. Use only for calls to + * SetImage and + * AnalysePage. Calls that attempt recognition will generate an + * error. + */ + void TessBaseAPIInitForAnalysePage(TessAPI.TessBaseAPI handle); + + /** + * Read a "config" file containing a set of param, value pairs. Searches the + * standard places: + * tessdata/configs, + * tessdata/tessconfigs and also accepts a relative or absolute + * path name. Note: only non-init params will be set (init params are set by + * Init()). + */ + void TessBaseAPIReadConfigFile(TessAPI.TessBaseAPI handle, String filename, int init_only); + + /** + * Set the current page segmentation mode. Defaults to PSM_SINGLE_BLOCK. The + * mode is stored as an IntParam so it can also be modified by + * ReadConfigFile or + * SetVariable("tessedit_pageseg_mode", mode as string). + */ + void TessBaseAPISetPageSegMode(TessAPI.TessBaseAPI handle, int mode); + + /** + * Return the current page segmentation mode. + */ + int TessBaseAPIGetPageSegMode(TessAPI.TessBaseAPI handle); + + /** + * Recognize a rectangle from an image and return the result as a string. + * May be called many times for a single + * Init. Currently has no error checking. Greyscale of 8 and + * color of 24 or 32 bits per pixel may be given. Palette color images will + * not work properly and must be converted to 24 bit. Binary images of 1 bit + * per pixel may also be given but they must be byte packed with the MSB of + * the first byte being the first pixel, and a 1 represents WHITE. For + * binary images set bytes_per_pixel=0. The recognized text is returned as a + * char* which is coded as UTF8 and must be freed with the delete [] + * operator.

Note that + * TesseractRect is the simplified convenience interface. For + * advanced uses, use + * SetImage, (optionally) + * SetRectangle, + * Recognize, and one or more of the + * Get*Text functions below. + */ + Pointer TessBaseAPIRect(TessAPI.TessBaseAPI handle, ByteBuffer imagedata, int bytes_per_pixel, int bytes_per_line, int left, int top, int width, int height); + + /** + * Call between pages or documents etc to free up memory and forget adaptive + * data. + */ + void TessBaseAPIClearAdaptiveClassifier(TessAPI.TessBaseAPI handle); + + /** + * Provide an image for Tesseract to recognize. Format is as TesseractRect + * above. Does not copy the image buffer, or take ownership. The source + * image may be destroyed after Recognize is called, either explicitly or + * implicitly via one of the + * Get*Text functions. + * SetImage clears all recognition results, and sets the + * rectangle to the full image, so it may be followed immediately by a + * GetUTF8Text, and it will automatically perform recognition. + */ + void TessBaseAPISetImage(TessAPI.TessBaseAPI handle, ByteBuffer imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line); + + /** + * Set the resolution of the source image in pixels per inch so font size + * information can be calculated in results. Call this after SetImage(). + */ + void TessBaseAPISetSourceResolution(TessAPI.TessBaseAPI handle, int ppi); + + /** + * Restrict recognition to a sub-rectangle of the image. Call after + * SetImage. Each + * SetRectangle clears the recognition results so multiple + * rectangles can be recognized with the same image. + */ + void TessBaseAPISetRectangle(TessAPI.TessBaseAPI handle, int left, int top, int width, int height); + + /** Scale factor from original image. */ + int TessBaseAPIGetThresholdedImageScaleFactor(TessAPI.TessBaseAPI handle); + + /** Dump the internal binary image to a PGM file. */ + void TessBaseAPIDumpPGM(TessAPI.TessBaseAPI handle, String filename); + + /** + * Runs page layout analysis in the mode set by SetPageSegMode. May + * optionally be called prior to Recognize to get access to just the page + * layout results. Returns an iterator to the results. Returns NULL on + * error. The returned iterator must be deleted after use. WARNING! This + * class points to data held within the TessBaseAPI class, and therefore can + * only be used while the TessBaseAPI class still exists and has not been + * subjected to a call of + * Init, + * SetImage, + * Recognize, + * Clear, + * End, DetectOS, or anything else that changes the internal + * PAGE_RES. + */ + TessAPI.TessPageIterator TessBaseAPIAnalyseLayout(TessAPI.TessBaseAPI handle); + + /** + * Recognize the image from SetAndThresholdImage, generating Tesseract + * internal structures. Returns 0 on success. Optional. The + * Get*Text functions below will call + * Recognize if needed. After Recognize, the output is kept + * internally until the next + * SetImage. + */ + int TessBaseAPIRecognize(TessAPI.TessBaseAPI handle, TessAPI.ETEXT_DESC monitor); + + /** + * Variant on Recognize used for testing chopper. + */ + int TessBaseAPIRecognizeForChopTest(TessAPI.TessBaseAPI handle, TessAPI.ETEXT_DESC monitor); + + /** + * Get a reading-order iterator to the results of LayoutAnalysis and/or + * Recognize. The returned iterator must be deleted after use. WARNING! This + * class points to data held within the TessBaseAPI class, and therefore can + * only be used while the TessBaseAPI class still exists and has not been + * subjected to a call of + * Init, + * SetImage, + * Recognize, + * Clear, + * End, DetectOS, or anything else that changes the internal + * PAGE_RES. + */ + TessAPI.TessResultIterator TessBaseAPIGetIterator(TessAPI.TessBaseAPI handle); + + /** + * Get a mutable iterator to the results of LayoutAnalysis and/or Recognize. + * The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + TessAPI.TessMutableIterator TessBaseAPIGetMutableIterator(TessAPI.TessBaseAPI handle); + + /** + * Recognizes all the pages in the named file, as a multi-page tiff or list + * of filenames, or single image, and gets the appropriate kind of text + * according to parameters: + * tessedit_create_boxfile, + * tessedit_make_boxes_from_boxes, + * tessedit_write_unlv, + * tessedit_create_hocr. Calls ProcessPage on each page in the + * input file, which may be a multi-page tiff, single-page other file + * format, or a plain text list of images to read. If tessedit_page_number + * is non-negative, processing begins at that page of a multi-page tiff + * file, or filelist. The text is returned in text_out. Returns false on + * error. If non-zero timeout_millisec terminates processing after the + * timeout on a single page. If non-NULL and non-empty, and some page fails + * for some reason, the page is reprocessed with the retry_config config + * file. Useful for interactively debugging a bad page. + */ + Pointer TessBaseAPIProcessPages(TessAPI.TessBaseAPI handle, String filename, String retry_config, int timeout_millisec); + + /** + * The recognized text is returned as a char* which is coded as UTF-8 and + * must be freed with the delete [] operator. + */ + Pointer TessBaseAPIGetUTF8Text(TessAPI.TessBaseAPI handle); + + /** + * Make a HTML-formatted string with hOCR markup from the internal data + * structures. page_number is 0-based but will appear in the output as + * 1-based. + */ + Pointer TessBaseAPIGetHOCRText(TessAPI.TessBaseAPI handle, int page_number); + + /** + * The recognized text is returned as a char* which is coded in the same + * format as a box file used in training. Returned string must be freed with + * the delete [] operator. Constructs coordinates in the original image - + * not just the rectangle. page_number is a 0-based page index that will + * appear in the box file. + */ + Pointer TessBaseAPIGetBoxText(TessAPI.TessBaseAPI handle, int page_number); + + /** + * The recognized text is returned as a char* which is coded as UNLV format + * Latin-1 with specific reject and suspect codes and must be freed with the + * delete [] operator. + */ + Pointer TessBaseAPIGetUNLVText(TessAPI.TessBaseAPI handle); + + /** + * Returns the (average) confidence value between 0 and 100. + */ + int TessBaseAPIMeanTextConf(TessAPI.TessBaseAPI handle); + + /** + * Returns all word confidences (between 0 and 100) in an array, terminated + * by -1. The calling function must delete [] after use. The number of + * confidences should correspond to the number of space-delimited words in + * GetUTF8Text. + */ + IntByReference TessBaseAPIAllWordConfidences(TessAPI.TessBaseAPI handle); + + /** + * Applies the given word to the adaptive classifier if possible. The word + * must be SPACE-DELIMITED UTF-8 - l i k e t h i s , so it can tell the + * boundaries of the graphemes. Assumes that SetImage/SetRectangle have been + * used to set the image to the given word. The mode arg should be + * PSM_SINGLE_WORD or PSM_CIRCLE_WORD, as that will be used to control + * layout analysis. The currently set PageSegMode is preserved. Returns + * false if adaption was not possible for some reason. + */ + int TessBaseAPIAdaptToWordStr(TessAPI.TessBaseAPI handle, int mode, String wordstr); + + /** + * Free up recognition results and any stored image data, without actually + * freeing any recognition data that would be time-consuming to reload. + * Afterwards, you must call + * SetImage or + * TesseractRect before doing any + * Recognize or + * Get* operation. + */ + void TessBaseAPIClear(TessAPI.TessBaseAPI handle); + + /** + * Close down tesseract and free up all memory. + * End() is equivalent to destructing and reconstructing your + * TessBaseAPI. Once + * End() has been used, none of the other API functions may be + * used other than + * Init and anything declared above it in the class definition. + */ + void TessBaseAPIEnd(TessAPI.TessBaseAPI handle); + + /** + * Check whether a word is valid according to Tesseract's language model. + * + * @return 0 if the word is invalid, non-zero if valid. @warning temporary! + * This function will be removed from here and placed in a separate API at + * some future time. + */ + int TessBaseAPIIsValidWord(TessAPI.TessBaseAPI handle, String word); + + int TessBaseAPIGetTextDirection(TessAPI.TessBaseAPI handle, IntBuffer out_offset, FloatBuffer out_slope); + + /** + * This method returns the string form of the specified unichar. + */ + String TessBaseAPIGetUnichar(TessAPI.TessBaseAPI handle, int unichar_id); + + /* Page iterator */ + void TessPageIteratorDelete(TessAPI.TessPageIterator handle); + + TessAPI.TessPageIterator TessPageIteratorCopy(TessAPI.TessPageIterator handle); + + void TessPageIteratorBegin(TessAPI.TessPageIterator handle); + + int TessPageIteratorNext(TessAPI.TessPageIterator handle, int level); + + int TessPageIteratorIsAtBeginningOf(TessAPI.TessPageIterator handle, int level); + + int TessPageIteratorIsAtFinalElement(TessAPI.TessPageIterator handle, int level, int element); + + int TessPageIteratorBoundingBox(TessAPI.TessPageIterator handle, int level, IntBuffer left, IntBuffer top, IntBuffer right, IntBuffer bottom); + + int TessPageIteratorBlockType(TessAPI.TessPageIterator handle); + + int TessPageIteratorBaseline(TessAPI.TessPageIterator handle, int level, IntBuffer x1, IntBuffer y1, IntBuffer x2, IntBuffer y2); + + void TessPageIteratorOrientation(TessAPI.TessPageIterator handle, IntBuffer orientation, IntBuffer writing_direction, IntBuffer textline_order, FloatBuffer deskew_angle); + + /* Result iterator */ + void TessResultIteratorDelete(TessAPI.TessResultIterator handle); + + TessAPI.TessResultIterator TessResultIteratorCopy(TessAPI.TessResultIterator handle); + + TessAPI.TessPageIterator TessResultIteratorGetPageIterator(TessAPI.TessResultIterator handle); + + TessAPI.TessPageIterator TessResultIteratorGetPageIteratorConst(TessAPI.TessResultIterator handle); + + Pointer TessResultIteratorGetUTF8Text(TessAPI.TessResultIterator handle, int level); + + float TessResultIteratorConfidence(TessAPI.TessResultIterator handle, int level); + + String TessResultIteratorWordFontAttributes(TessAPI.TessResultIterator handle, IntBuffer is_bold, IntBuffer is_italic, IntBuffer is_underlined, IntBuffer is_monospace, IntBuffer is_serif, IntBuffer is_smallcaps, IntBuffer pointsize, IntBuffer font_id); + + int TessResultIteratorWordIsFromDictionary(TessAPI.TessResultIterator handle); + + int TessResultIteratorWordIsNumeric(TessAPI.TessResultIterator handle); + + int TessResultIteratorSymbolIsSuperscript(TessAPI.TessResultIterator handle); + + int TessResultIteratorSymbolIsSubscript(TessAPI.TessResultIterator handle); + + int TessResultIteratorSymbolIsDropcap(TessAPI.TessResultIterator handle); + + public static class TessBaseAPI extends PointerType { + + public TessBaseAPI(Pointer address) { + super(address); + } + + public TessBaseAPI() { + super(); + } + }; + + public static class ETEXT_DESC extends PointerType { + + public ETEXT_DESC(Pointer address) { + super(address); + } + + public ETEXT_DESC() { + super(); + } + }; + + public static class TessPageIterator extends PointerType { + + public TessPageIterator(Pointer address) { + super(address); + } + + public TessPageIterator() { + super(); + } + }; + + public static class TessMutableIterator extends PointerType { + + public TessMutableIterator(Pointer address) { + super(address); + } + + public TessMutableIterator() { + super(); + } + }; + + public static class TessResultIterator extends PointerType { + + public TessResultIterator(Pointer address) { + super(address); + } + + public TessResultIterator() { + super(); + } + }; +} diff --git a/docs-core/src/main/java/net/sourceforge/tess4j/Tesseract.java b/docs-core/src/main/java/net/sourceforge/tess4j/Tesseract.java new file mode 100644 index 00000000..ab3bee49 --- /dev/null +++ b/docs-core/src/main/java/net/sourceforge/tess4j/Tesseract.java @@ -0,0 +1,258 @@ +/** + * Copyright @ 2012 Quan Nguyen + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package net.sourceforge.tess4j; + +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +import javax.imageio.IIOImage; + +import net.sourceforge.vietocr.ImageIOHelper; + +import com.sun.jna.Pointer; + +/** + * An object layer on top of + * TessAPI, provides character recognition support for common image + * formats, and multi-page TIFF images beyond the uncompressed, binary TIFF + * format supported by Tesseract OCR engine. The extended capabilities are + * provided by the + * Java Advanced Imaging Image I/O Tools.

Support for + * PDF documents is available through + * Ghost4J, a + * JNA wrapper for + * GPL Ghostscript, which should be installed and included in + * system path.

Any program that uses the library will need to + * ensure that the required libraries (the + * .jar files for + * jna, + * jai-imageio, and + * ghost4j) are in its compile and run-time + * classpath. + */ +public class Tesseract { + + private static Tesseract instance; + private final static Rectangle EMPTY_RECTANGLE = new Rectangle(); + private String language = "eng"; + private String datapath = "tessdata"; + private int psm = TessAPI.TessPageSegMode.PSM_AUTO; + private boolean hocr; + private int pageNum; + private int ocrEngineMode = TessAPI.TessOcrEngineMode.OEM_DEFAULT; + private Properties prop = new Properties(); + public final static String htmlBeginTag = + "\n" + + "\n\n\n" + + "\n\n" + + "\n\n"; + public final static String htmlEndTag = "\n\n"; + + /** + * Private constructor. + */ + private Tesseract() { + System.setProperty("jna.encoding", "UTF8"); + } + + /** + * Gets an instance of the class library. + * + * @return instance + */ + public static synchronized Tesseract getInstance() { + if (instance == null) { + instance = new Tesseract(); + } + + return instance; + } + + /** + * Sets tessdata path. + * + * @param datapath the tessdata path to set + */ + public void setDatapath(String datapath) { + this.datapath = datapath; + } + + /** + * Sets language for OCR. + * + * @param language the language code, which follows ISO 639-3 standard. + */ + public void setLanguage(String language) { + this.language = language; + } + + /** + * Sets OCR engine mode. + * + * @param ocrEngineMode the OcrEngineMode to set + */ + public void setOcrEngineMode(int ocrEngineMode) { + this.ocrEngineMode = ocrEngineMode; + } + + /** + * Sets page segmentation mode. + * + * @param mode the page segmentation mode to set + */ + public void setPageSegMode(int mode) { + this.psm = mode; + } + + /** + * Enables hocr output. + * + * @param hocr to enable or disable hocr output + */ + public void setHocr(boolean hocr) { + this.hocr = hocr; + prop.setProperty("tessedit_create_hocr", hocr ? "1" : "0"); + } + + /** + * Set the value of Tesseract's internal parameter. + * + * @param key variable name, e.g., + * tessedit_create_hocr, + * tessedit_char_whitelist, etc. + * @param value value for corresponding variable, e.g., "1", "0", + * "0123456789", etc. + */ + public void setTessVariable(String key, String value) { + prop.setProperty(key, value); + } + + /** + * Performs OCR operation. + * + * @param bi a buffered image + * @return the recognized text + * @throws TesseractException + */ + public String doOCR(BufferedImage bi) throws TesseractException { + return doOCR(bi, null); + } + + /** + * Performs OCR operation. + * + * @param bi a buffered image + * @param rect the bounding rectangle defines the region of the image to be + * recognized. A rectangle of zero dimension or + * null indicates the whole image. + * @return the recognized text + * @throws TesseractException + */ + public String doOCR(BufferedImage bi, Rectangle rect) throws TesseractException { + IIOImage oimage = new IIOImage(bi, null, null); + List imageList = new ArrayList(); + imageList.add(oimage); + return doOCR(imageList, rect); + } + + /** + * Performs OCR operation. + * + * @param imageList a list of + * IIOImage objects + * @param rect the bounding rectangle defines the region of the image to be + * recognized. A rectangle of zero dimension or + * null indicates the whole image. + * @return the recognized text + * @throws TesseractException + */ + public String doOCR(List imageList, Rectangle rect) throws TesseractException { + StringBuilder sb = new StringBuilder(); + pageNum = 0; + + for (IIOImage oimage : imageList) { + pageNum++; + try { + ByteBuffer buf = ImageIOHelper.getImageByteBuffer(oimage); + RenderedImage ri = oimage.getRenderedImage(); + String pageText = doOCR(ri.getWidth(), ri.getHeight(), buf, rect, ri.getColorModel().getPixelSize()); + sb.append(pageText); + } catch (IOException ioe) { + //skip the problematic image + System.err.println(ioe.getMessage()); + } + } + + if (hocr) { + sb.insert(0, htmlBeginTag).append(htmlEndTag); + } + return sb.toString(); + } + + /** + * Performs OCR operation. Use + * SetImage, (optionally) + * SetRectangle, and one or more of the + * Get*Text functions. + * + * @param xsize width of image + * @param ysize height of image + * @param buf pixel data + * @param rect the bounding rectangle defines the region of the image to be + * recognized. A rectangle of zero dimension or + * null indicates the whole image. + * @param bpp bits per pixel, represents the bit depth of the image, with 1 + * for binary bitmap, 8 for gray, and 24 for color RGB. + * @return the recognized text + * @throws TesseractException + */ + public String doOCR(int xsize, int ysize, ByteBuffer buf, Rectangle rect, int bpp) throws TesseractException { + TessAPI api = TessAPI.INSTANCE; + TessAPI.TessBaseAPI handle = api.TessBaseAPICreate(); + api.TessBaseAPIInit2(handle, datapath, language, ocrEngineMode); + api.TessBaseAPISetPageSegMode(handle, psm); + + Enumeration em = prop.propertyNames(); + while (em.hasMoreElements()) { + String key = (String) em.nextElement(); + api.TessBaseAPISetVariable(handle, key, prop.getProperty(key)); + } + + int bytespp = bpp / 8; + int bytespl = (int) Math.ceil(xsize * bpp / 8.0); + api.TessBaseAPISetImage(handle, buf, xsize, ysize, bytespp, bytespl); + + if (rect != null && !rect.equals(EMPTY_RECTANGLE)) { + api.TessBaseAPISetRectangle(handle, rect.x, rect.y, rect.width, rect.height); + } + + Pointer utf8Text = hocr ? api.TessBaseAPIGetHOCRText(handle, pageNum - 1) : api.TessBaseAPIGetUTF8Text(handle); + String str = utf8Text.getString(0); + api.TessDeleteText(utf8Text); + api.TessBaseAPIDelete(handle); + + return str; + } +} diff --git a/docs-core/src/main/java/net/sourceforge/tess4j/TesseractException.java b/docs-core/src/main/java/net/sourceforge/tess4j/TesseractException.java new file mode 100644 index 00000000..1bda189f --- /dev/null +++ b/docs-core/src/main/java/net/sourceforge/tess4j/TesseractException.java @@ -0,0 +1,38 @@ +/** + * Copyright @ 2010 Quan Nguyen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.tess4j; + +public class TesseractException extends Exception { + + private static final long serialVersionUID = 1L; + + public TesseractException() { + super(); + } + + public TesseractException(String message) { + super(message); + } + + public TesseractException(Throwable cause) { + super(cause); + } + + public TesseractException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/docs-core/src/main/java/net/sourceforge/vietocr/ImageHelper.java b/docs-core/src/main/java/net/sourceforge/vietocr/ImageHelper.java new file mode 100644 index 00000000..f8b7a126 --- /dev/null +++ b/docs-core/src/main/java/net/sourceforge/vietocr/ImageHelper.java @@ -0,0 +1,173 @@ +/** + * Copyright @ 2008 Quan Nguyen + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package net.sourceforge.vietocr; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.Transparency; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.image.*; + +public class ImageHelper { + + /** + * Convenience method that returns a scaled instance of the provided + * {@code BufferedImage}. + * + * @param image the original image to be scaled + * @param targetWidth the desired width of the scaled instance, in pixels + * @param targetHeight the desired height of the scaled instance, in pixels + * @return a scaled version of the original {@code BufferedImage} + */ + public static BufferedImage getScaledInstance(BufferedImage image, int targetWidth, int targetHeight) { + int type = (image.getTransparency() == Transparency.OPAQUE) + ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage tmp = new BufferedImage(targetWidth, targetHeight, type); + Graphics2D g2 = tmp.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2.drawImage(image, 0, 0, targetWidth, targetHeight, null); + g2.dispose(); + return tmp; + } + + /** + * A replacement for the standard + * BufferedImage.getSubimage method. + * + * @param image + * @param x the X coordinate of the upper-left corner of the specified + * rectangular region + * @param y the Y coordinate of the upper-left corner of the specified + * rectangular region + * @param width the width of the specified rectangular region + * @param height the height of the specified rectangular region + * @return a BufferedImage that is the subimage of image. + */ + public static BufferedImage getSubImage(BufferedImage image, int x, int y, int width, int height) { + int type = (image.getTransparency() == Transparency.OPAQUE) + ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage tmp = new BufferedImage(width, height, type); + Graphics2D g2 = tmp.createGraphics(); + g2.drawImage(image.getSubimage(x, y, width, height), 0, 0, null); + g2.dispose(); + return tmp; + } + + /** + * A simple method to convert an image to binary or B/W image. + * + * @param image input image + * @return a monochrome image + */ + public static BufferedImage convertImageToBinary(BufferedImage image) { + BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_BINARY); + Graphics2D g2 = tmp.createGraphics(); + g2.drawImage(image, 0, 0, null); + g2.dispose(); + return tmp; + } + + /** + * A simple method to convert an image to binary or B/W image. + * + * @param image input image + * @return a monochrome image + * @deprecated As of release 1.1, renamed to {@link #convertImageToBinary(BufferedImage image)} + */ + @Deprecated + public static BufferedImage convertImage2Binary(BufferedImage image) { + return convertImageToBinary(image); + } + + /** + * A simple method to convert an image to gray scale. + * + * @param image input image + * @return a monochrome image + */ + public static BufferedImage convertImageToGrayscale(BufferedImage image) { + BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2 = tmp.createGraphics(); + g2.drawImage(image, 0, 0, null); + g2.dispose(); + return tmp; + } + + private static final short[] invertTable; + + static { + invertTable = new short[256]; + for (int i = 0; i < 256; i++) { + invertTable[i] = (short) (255 - i); + } + } + + /** + * Inverts image color. + * + * @param image input image + * @return an inverted-color image + */ + public static BufferedImage invertImageColor(BufferedImage image) { + BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); + BufferedImageOp invertOp = new LookupOp(new ShortLookupTable(0, invertTable), null); + return invertOp.filter(image, tmp); + } + + /** + * Rotates an image. + * + * @param image the original image + * @param angle the degree of rotation + * @return a rotated image + */ + public static BufferedImage rotateImage(BufferedImage image, double angle) { + double theta = Math.toRadians(angle); + double sin = Math.abs(Math.sin(theta)); + double cos = Math.abs(Math.cos(theta)); + int w = image.getWidth(); + int h = image.getHeight(); + int newW = (int) Math.floor(w * cos + h * sin); + int newH = (int) Math.floor(h * cos + w * sin); + + BufferedImage tmp = new BufferedImage(newW, newH, image.getType()); + Graphics2D g2d = tmp.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2d.translate((newW - w) / 2, (newH - h) / 2); + g2d.rotate(theta, w / 2, h / 2); + g2d.drawImage(image, 0, 0, null); + g2d.dispose(); + return tmp; + } + + /** + * Gets an image from Clipboard. + * + * @return image + */ + public static Image getClipboardImage() { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + try { + return (Image) clipboard.getData(DataFlavor.imageFlavor); + } catch (Exception e) { + return null; + } + } +} diff --git a/docs-core/src/main/java/net/sourceforge/vietocr/ImageIOHelper.java b/docs-core/src/main/java/net/sourceforge/vietocr/ImageIOHelper.java new file mode 100644 index 00000000..154ad5c0 --- /dev/null +++ b/docs-core/src/main/java/net/sourceforge/vietocr/ImageIOHelper.java @@ -0,0 +1,128 @@ +/** + * Copyright @ 2008 Quan Nguyen + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package net.sourceforge.vietocr; + +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageOutputStream; + +import org.w3c.dom.NodeList; + +import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam; +import com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriterSpi; + +public class ImageIOHelper { + + final static String TIFF_FORMAT = "tiff"; + + + /** + * Gets pixel data of an + * IIOImage object. + * + * @param image an + * IIOImage object + * @return a byte buffer of pixel data + * @throws Exception + */ + public static ByteBuffer getImageByteBuffer(IIOImage image) throws IOException { + //Set up the writeParam + TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US); + tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED); + + //Get tif writer and set output to file + ImageWriter writer = new TIFFImageWriterSpi().createWriterInstance(); + + //Get the stream metadata + IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream); + writer.setOutput(ios); + writer.write(streamMetadata, new IIOImage(image.getRenderedImage(), null, null), tiffWriteParam); + writer.dispose(); + ios.seek(0); + BufferedImage bi = ImageIO.read(ios); + return convertImageData(bi); + } + + /** + * Converts BufferedImage to ByteBuffer. + * + * @param bi Input image + * @return pixel data + */ + public static ByteBuffer convertImageData(BufferedImage bi) { + byte[] pixelData = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData(); + // return ByteBuffer.wrap(pixelData); + ByteBuffer buf = ByteBuffer.allocateDirect(pixelData.length); + buf.order(ByteOrder.nativeOrder()); + buf.put(pixelData); + buf.flip(); + return buf; + } + + /** + * Reads image meta data. + * + * @param oimage + * @return a map of meta data + */ + public static Map readImageData(IIOImage oimage) { + Map dict = new HashMap(); + + IIOMetadata imageMetadata = oimage.getMetadata(); + if (imageMetadata != null) { + IIOMetadataNode dimNode = (IIOMetadataNode) imageMetadata.getAsTree("javax_imageio_1.0"); + NodeList nodes = dimNode.getElementsByTagName("HorizontalPixelSize"); + int dpiX; + if (nodes.getLength() > 0) { + float dpcWidth = Float.parseFloat(nodes.item(0).getAttributes().item(0).getNodeValue()); + dpiX = (int) Math.round(25.4f / dpcWidth); + } else { + dpiX = Toolkit.getDefaultToolkit().getScreenResolution(); + } + dict.put("dpiX", String.valueOf(dpiX)); + + nodes = dimNode.getElementsByTagName("VerticalPixelSize"); + int dpiY; + if (nodes.getLength() > 0) { + float dpcHeight = Float.parseFloat(nodes.item(0).getAttributes().item(0).getNodeValue()); + dpiY = (int) Math.round(25.4f / dpcHeight); + } else { + dpiY = Toolkit.getDefaultToolkit().getScreenResolution(); + } + dict.put("dpiY", String.valueOf(dpiY)); + } + + return dict; + } +} diff --git a/docs-parent/TODO b/docs-parent/TODO index 93acc0cd..80fc035b 100644 --- a/docs-parent/TODO +++ b/docs-parent/TODO @@ -1,3 +1,3 @@ - Disable the whole document edit form while uploading files (client) - Change browser title while uploading (client) -- Automatic backup system using Quartz (server) \ No newline at end of file +- Automatic backup system using Quartz (server) diff --git a/docs-parent/lib/tess4j.jar b/docs-parent/lib/tess4j.jar deleted file mode 100644 index 2ab9b13b1e7c888783a198ec6cb9f92767f8a3d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66123 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 k2gshYU1uVd^8-K4E@z|f@>!mRKf0&_9~P5=M^ diff --git a/docs-parent/pom.xml b/docs-parent/pom.xml index 0c651bd9..c1e82c23 100644 --- a/docs-parent/pom.xml +++ b/docs-parent/pom.xml @@ -463,11 +463,6 @@ 1.0
- - tess4j - tess4j - 1.0 - @@ -531,23 +526,6 @@ - - install-tess4j - validate - - ${project.basedir}/lib/tess4j.jar - default - tess4j - tess4j - 1.0 - jar - true - - - install-file - - -