zoukankan      html  css  js  c++  java
  • [转]Tesseract-OCR学习系列(四)API

     

    原文地址:http://www.jianshu.com/p/3df039e42986
    2016.09.20 

    Other API Examples

    参考文档:https://github.com/tesseract-ocr/tesseract/wiki/APIExample

    在上一篇中, 我们学习了参考文档中的第一个示例。用CMake构建了工程,并且看了一下例子中调用到的API。在这一篇中,我们继续看一看其它的例子。但如何用CMake构建工程的方法就不赘述了。这里给出我写的例程,若有疑问之处,请阅读Tesseract-OCR学习系列(三)简例以及CMake简要教程这两篇文章。

    GetComponentImages example

    #include <tesseract/baseapi.h>
    #include <leptonica/allheaders.h>
    
    int main()
    {
        Pix *image = pixRead("D:\open_source\tesseract-3.04.01\tesseract\testing\phototest.tif");
        tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
        api->Init(NULL, "eng");
        api->SetImage(image);
        Boxa* boxes = api->GetComponentImages(tesseract::RIL_TEXTLINE, true, NULL, NULL);
        printf("Found %d textline image components.
    ", boxes->n);
        for (int i = 0; i < boxes->n; i++){
            BOX* box = boxaGetBox(boxes, i, L_CLONE);
            api->SetRectangle(box->x, box->y, box->w, box->h);
            char* ocrResult = api->GetUTF8Text();
            int conf = api->MeanTextConf();
            fprintf(stdout, "Box[%d]: x=%d, y=%d, w=%d, h=%d, confidence: %d, text: %s",
                i, box->x, box->y, box->w, box->h, conf, ocrResult);
        }
    }

    我们知道,如果要进行字符识别,首先要搜索到文字图块。或者说,找到包含字符的文字图块。这个例子帮助我们将每一个文字图块找到,并对文字图块进行识别。下面来看代码(之前说过的就不说了):

        Pix *image = pixRead("D:\open_source\tesseract-3.04.01\tesseract\testing\phototest.tif");
        tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
        api->Init(NULL, "eng");
        api->SetImage(image);

    已经熟悉了,Pass……

        Boxa* boxes = api->GetComponentImages(tesseract::RIL_TEXTLINE, true, NULL, NULL);

    这是最关键的一行代码。GetComponentImages用于查找图像内的图像块,并将分割到的图像块返回给Boxa这个结构中。

      Boxa* GetComponentImages(const PageIteratorLevel level,
                               const bool text_only,
                               Pixa** pixa, int** blockids)

    那么,分割到什么程度呢?这是函数的第一个参数来控制的。

    enum PageIteratorLevel {
      RIL_BLOCK,     // Block of text/image/separator line.
      RIL_PARA,      // Paragraph within a block.
      RIL_TEXTLINE,  // Line within a paragraph.
      RIL_WORD,      // Word within a textline.
      RIL_SYMBOL     // Symbol/character within a word.
    };

    也就是说,我们可以分割到一块、一段、一行、一个单词或者一个单字。这特别适合用于做文档的OCR。一份文档,有可能包含图像和大大小小的各种文字。用这个函数,就可以将图像、文字等单独拎出来,然后再分别进行处理。第二个参数,text_only如果是true的话,就表示只返回文字区域坐标,不返回图像区域坐标。pixa用于返回分割出来的图像。这里设为NULL,即表示不需要返回图像。blockids返回的是序列号。这里也不需要,所以设置成NULL。最后,返回值是分割到的矩形数组。

    struct Box
    {
        l_int32            x;
        l_int32            y;
        l_int32            w;
        l_int32            h;
        l_uint32           refcount;      /* reference count (1 if no clones)  */
    
    };
    typedef struct Box    BOX;
    
    struct Boxa
    {
        l_int32            n;             /* number of box in ptr array        */
        l_int32            nalloc;        /* number of box ptrs allocated      */
        l_uint32           refcount;      /* reference count (1 if no clones)  */
        struct Box       **box;           /* box ptr array                     */
    };
    typedef struct Boxa  BOXA;

    接着,进入循环。

    for (int i = 0; i < boxes->n; i++){
            BOX* box = boxaGetBox(boxes, i, L_CLONE);
            api->SetRectangle(box->x, box->y, box->w, box->h);
            char* ocrResult = api->GetUTF8Text();
            int conf = api->MeanTextConf();
            fprintf(stdout, "Box[%d]: x=%d, y=%d, w=%d, h=%d, confidence: %d, text: %s",
                i, box->x, box->y, box->w, box->h, conf, ocrResult);
        }

    只有两个函数没有见过,一个是boxaGetBox,一个是MeanTextConf

    • boxaGetBox:用于提取矩形数组中的某个矩形。其它参数一眼就看出来了。第三个参数可以选择L_CLONE或者L_COPYL_CLONE是软拷贝,只增加引用数目。L_COPY是硬拷贝,把数据都复制一遍。
    • MeanTextConf:用于返回OCR的平均信心。信心的值最低为0,最高为100。

    最后,看一下运行结果:

    Result iterator example

    这个例子是说,对于OCR的结果,我们可以一个词一个词地遍历了来看。可以看到每一个词的OCR结果、置信度以及在原图中的位置。

    #include <tesseract/baseapi.h>
    #include <leptonica/allheaders.h>
    
    int main()
    {
        Pix *image = pixRead("D:\open_source\tesseract-3.04.01\tesseract\testing\phototest.tif");
        tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
        api->Init(NULL, "eng");
        api->SetImage(image);
        api->Recognize(0);
        tesseract::ResultIterator* ri = api->GetIterator();
        tesseract::PageIteratorLevel level = tesseract::RIL_WORD;
        if (ri != 0)
        {
            do
            {
                const char* word = ri->GetUTF8Text(level);
                float conf = ri->Confidence(level);
                int x1, y1, x2, y2;
                ri->BoundingBox(level, &x1, &y1, &x2, &y2);
                printf("word: '%s';  	conf: %.2f; BoundingBox: %d,%d,%d,%d;
    ",
                    word, conf, x1, y1, x2, y2);
                delete[] word;
            } while (ri->Next(level));
        }
    }

    最为关键的就是下面这两行

        tesseract::ResultIterator* ri = api->GetIterator();
        tesseract::PageIteratorLevel level = tesseract::RIL_WORD;

    第一句按照阅读顺序来获取一个OCR结果的迭代器。第二句设置迭代的单位。可用的迭代单位有:

    enum PageIteratorLevel {
      RIL_BLOCK,     // Block of text/image/separator line.
      RIL_PARA,      // Paragraph within a block.
      RIL_TEXTLINE,  // Line within a paragraph.
      RIL_WORD,      // Word within a textline.
      RIL_SYMBOL     // Symbol/character within a word.
    };

    运行下来的部分结果如下:

    Orientation and script detection (OSD) example

    这个例子讲了如何进行页面的方向检测和文字的方向检测。不知道大家是否与我有同样的疑问,就是页面的方向如果检测出来了,那文字的方向还用检测吗?文字不就是正着的了吗?可是人家说的文字方向检测根本不是说的这个,而是说阅读的方向性。比如,我们知道英文的一行肯定是横着排的,阅读方向是从左到右的。读完上面一行再读下面一行。然而对于古体中文来说,文字是竖着写的,阅读方向是从上到下的,行与行之间呢,是从右往左读的。这里文字的方向检测检测的是这个。

    先看代码:

    #include <tesseract/baseapi.h>
    #include <leptonica/allheaders.h>
    
    int main()
    {
        const char* inputfile = "D:\open_source\tesseract-3.04.01\tesseract\testing\phototest.tif";
        tesseract::Orientation orientation;
        tesseract::WritingDirection direction;
        tesseract::TextlineOrder order;
        float deskew_angle;
    
        PIX *image = pixRead(inputfile);
        tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
        api->Init(NULL, "eng");
        api->SetPageSegMode(tesseract::PSM_AUTO_OSD);
        api->SetImage(image);
        api->Recognize(0);
    
        tesseract::PageIterator* it = api->AnalyseLayout();
        it->Orientation(&orientation, &direction, &order, &deskew_angle);
        printf("Orientation: %d;
    WritingDirection: %d
    TextlineOrder: %d
    " 
            "Deskew angle: %.4f
    ",
            orientation, direction, order, deskew_angle);
    
    }

    只看之前没有看到过的。

        api->SetPageSegMode(tesseract::PSM_AUTO_OSD);

    这句是重点。它设置了页面分割模式。页面分割有如下的模式可供选择:

    enum PageSegMode {
      PSM_OSD_ONLY,       ///< Orientation and script detection only.
      PSM_AUTO_OSD,       ///< Automatic page segmentation with orientation and
                          ///< script detection. (OSD)
      PSM_AUTO_ONLY,      ///< Automatic page segmentation, but no OSD, or OCR.
      PSM_AUTO,           ///< Fully automatic page segmentation, but no OSD.
      PSM_SINGLE_COLUMN,  ///< Assume a single column of text of variable sizes.
      PSM_SINGLE_BLOCK_VERT_TEXT,  ///< Assume a single uniform block of vertically
                                   ///< aligned text.
      PSM_SINGLE_BLOCK,   ///< Assume a single uniform block of text. (Default.)
      PSM_SINGLE_LINE,    ///< Treat the image as a single text line.
      PSM_SINGLE_WORD,    ///< Treat the image as a single word.
      PSM_CIRCLE_WORD,    ///< Treat the image as a single word in a circle.
      PSM_SINGLE_CHAR,    ///< Treat the image as a single character.
      PSM_SPARSE_TEXT,    ///< Find as much text as possible in no particular order.
      PSM_SPARSE_TEXT_OSD,  ///< Sparse text with orientation and script det.
      PSM_RAW_LINE,       ///< Treat the image as a single text line, bypassing
                          ///< hacks that are Tesseract-specific.
    
      PSM_COUNT           ///< Number of enum entries.
    };

    多提一句,如需使用OSD功能,则需要下载osd.traineddata

        api->Recognize(0);

    这一句当然是用来根据之前的设定来进行识别的。

        tesseract::PageIterator* it = api->AnalyseLayout();

    这一句根据之前SetPageSegMode的设定来运行页面的布局分析。这句话其实也可以在Recognize前面进行。

        it->Orientation(&orientation, &direction, &order, &deskew_angle);

    这个函数用来获取页面和文字的方向。其签名如下:

    void Orientation(tesseract::Orientation *orientation,
                       tesseract::WritingDirection *writing_direction,
                       tesseract::TextlineOrder *textline_order,
                       float *deskew_angle) const;

    其中,

    • tesseract::Orientation指的是页面的方向。
    • tesseract::WritingDirection指的是书写方向。(比如刚刚说的英文是从左到右,中文是从上到下)
    • tesseract::TextlineOrder指的是一行一行的方向。(比如刚刚说的英文是从上往下阅读,中文是从右往左阅读)
    • deskew_angle是指倾斜角度。因为排出来的图片也不可能完全是正着的。这里可以计算出偏转的角度。

    运行的结果如下:

    Example of iterator over the classifier choices for a single symbol

    这个例子可以帮助我们学习如何找到一个识别对象的其它候选结果。

    #include <tesseract/baseapi.h>
    #include <leptonica/allheaders.h>
    
    int main()
    {
        Pix *image = pixRead("D:\open_source\tesseract-3.04.01\tesseract\testing\phototest.tif");
        tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
        api->Init(NULL, "eng");
        api->SetImage(image);
        api->SetVariable("save_blob_choices", "T");
        api->SetRectangle(37, 228, 548, 31);
        api->Recognize(NULL);
    
        tesseract::ResultIterator* ri = api->GetIterator();
        tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL;
        if (ri != 0)
        {
            do
            {
                const char* symbol = ri->GetUTF8Text(level);
                float conf = ri->Confidence(level);
                if (symbol != 0)
                {
                    printf("symbol %s, conf: %f", symbol, conf);
                    bool indent = false;
                    tesseract::ChoiceIterator ci(*ri);
                    do
                    {
                        if (indent) printf("		 ");
                        printf("	- ");
                        const char* choice = ci.GetUTF8Text();
                        printf("%s conf: %f
    ", choice, ci.Confidence());
                        indent = true;
                    } while (ci.Next());
                }
                printf("------------------------------------------
    ");
                delete[] symbol;
            } while (ri->Next(level));
        }
    }

    在编译的时候出现了写问题。问题在于:

                    tesseract::ChoiceIterator ci(*ri);

    这个类在dll中没有。没有的原因是,这个类根本就没有被导出来!如果需要导出这个类,那么就需要在tesseract的源代码中修改一下,然后再重新编译。

    修改方法为,在ltrresultiterator.h头文件中,将:

    class ChoiceIterator {

    修改为:

    class TESS_API ChoiceIterator {

    然后要记得重新编译哦!并且将生成的动态库覆盖原来的动态库。目前我们还不太熟悉的API如下:

        api->SetVariable("save_blob_choices", "T");

    这个函数的作用是设置内部的参数。(不过话说我怎么知道内部有哪些参数,这些参数又有什么意义啊!)设置"save_blob_choices"的目的是将候选项全部保存下来。

                    tesseract::ChoiceIterator ci(*ri);

    这是一个迭代器,通过这个迭代器,可以将每一个候选的结果都打印出来。

    部分结果如下:

    好了,就写到这儿吧。可以看出,Tesseract的应用是非常灵活的。下面一段时间,我希望自己可以慢慢了解Tesseract-OCR的算法原理。这不是一件容易的事。这个系列可能要暂停一段时间了。

  • 相关阅读:
    apache的源代码编译安装
    python学习笔记(五) 200行实现2048小游戏
    python学习笔记(四) 思考和准备
    python学习笔记(三)高级特性
    python自学笔记(二)
    python自学笔记(一)
    redis 配置和使用(C++)
    汇编基础最后一篇--机器语言指令
    汇编语言学习笔记(六)
    网络编程学习方法和图书推荐
  • 原文地址:https://www.cnblogs.com/Crysaty/p/6437595.html
Copyright © 2011-2022 走看看