如何使用tesseract的C++接口做字符识别
要从图片中识别字符,可以使用tesseract库。
如果使用Python,可以直接使用
pip install tesseract
安装之后,就可以在Python中
import tesseract
来使用了。
如果我们在C++的开发中,要使用tesseract,其实也非常简单。
tesseract在Linux各发行版,都有现成的二进制安装包,而且还同时提供了C语言、C++的支持。本文中的示例,采用C++的接口。
开发环境
如果是Fedora系统,可以直接通过
dnf install tesseract-devel
来安装tesseract的开发库。
之后,可以直接在程序中链接-ltesseract,包含的头文件目录除了/usr/include/tesseract之外,还需要/usr/include/leptonica、/usr/include/libpng16等。
为了简化这一操作,可以使用pkg-config文件。
如:
~/$ pkg-config --cflags --libs tesseract
-I/usr/include/leptonica -I/usr/include/libpng16 -DWITH_GZFILEOP -I/usr/include/webp -ltesseract
头文件以及API
安装好tesseract,在构建系统中设置包含目录之后,就可以在程序中包含相应的头文件了。
如果是在C语言中,需要包含
#include <tesseract/capi.h>
,而如果是在C++中,可以使用C语言的API,也可以使用C++的Class API,需要包含:
#include <tesseract/baseapi.h>
使用这个头文件的话,使用的是tesseract的命名空间之中的TessBaseAPI。
如果我们打开tesseract/baseapi.h,可以看到TessBaseAPI的方法很多。但是,其实我们做OCR核心要使用的却并不多,基本上就Init、SetImage与GetUTF8Text这样三个就可以了。
- 构造函数
76 class TESS_API TessBaseAPI {
77 public:
78 TessBaseAPI();
79 virtual ~TessBaseAPI();
80 // Copy constructor and assignment operator are currently unsupported.
81 TessBaseAPI(TessBaseAPI const &) = delete;
82 TessBaseAPI &operator=(TessBaseAPI const &) = delete;
构造函数没有参数,不支持复制,非常简单。
- 版本号
84 /**
85 * Returns the version identifier as a static string. Do not delete.
86 */
87 static const char *Version();
一个静态方法,返回API的版本,可以根据不同的版本做条件开发。
- 初始化设置
97 int Init(const char *datapath, const char *language, OcrEngineMode mode,
198 char **configs, int configs_size,
199 const std::vector<std::string> *vars_vec,
200 const std::vector<std::string> *vars_values,
201 bool set_only_non_debug_params);
202 int Init(const char *datapath, const char *language, OcrEngineMode oem) {
203 return Init(datapath, language, oem, nullptr, 0, nullptr, nullptr, false);
204 }
205 int Init(const char *datapath, const char *language) {
206 return Init(datapath, language, OEM_DEFAULT, nullptr, 0, nullptr, nullptr,
207 false);
208 }
209 // In-memory version reads the traineddata file directly from the given
210 // data[data_size] array, and/or reads data via a FileReader.
211 int Init(const char *data, int data_size, const char *language,
212 OcrEngineMode mode, char **configs, int configs_size,
213 const std::vector<std::string> *vars_vec,
214 const std::vector<std::string> *vars_values,
215 bool set_only_non_debug_params, FileReader reader);
216
Init方法的目的是初始化语言支持,比如是识别英语,还是汉语,重载版本比较多。
但是,如果开发简单的应用,使用最简单的
int Init(const char *datapath, const char *language)
重载版本就可以了。
而且,datapath都可以简单地设置为nullptr,指的是使用默认的路径,在我的Fedora 41系统上,这个目录是/usr/share/tesseract/testdata。
这个目录里面有:
ls /usr/share/tesseract/tessdata/
chi_sim.traineddata chi_sim_vert.traineddata configs eng.traineddata tessconfigs
我们要识别什么语言,就要加载相应的.traineddata文件。所以,后面的language需要设置.traineddata前面的部分。
比如,我们要识别英语,就使用:
TessBaseAPI baseapi;
baseapi.Init (nullptr, "eng");
如果要识别多种语言,就使用+连接起来。如我们要识别英语与简体中文,就使用:
TessBaseAPI baseapi;
baseapi.Init (nullptr, "eng+chi_sim");
根据头文件里注释,Init()这个方法可以调用多次,在运行中动态更改识别的语言。
- 设置图片数据
307 void SetImage(const unsigned char *imagedata, int width, int height,
308 int bytes_per_pixel, int bytes_per_line);
309
设置图片的数据指针,之后是图片的宽、长、以及每个像素的字节数,最后是每一行的字节数。一般来说,最后一个参数可以通过宽度以及每个像素的字节数相乘计算出来,但是因为图片数据往往会做填充,所以每行字节数就需要单独指定。
- 设置识别范围
void SetRectangle(int left, int top, int width, int height);
设置图片以后,使用这个方法可以设置一下识别的范围。一般需要识别图片中的一个矩形框的时候使用,如果识别整个图片可以不设。
- 识别字符
char *GetUTF8Text();
做字符识别最核心的方法,返回的是一个UTF-8编码的字节数组。
注意:这个返回的字节数组,需要调用者手动释放。
- 清除临时数据
void Clear();
这个方法是用来清理图片数据的。调用这个函数之后,就不能再做任何与前面设置过的图片相关的操作了。
- 结束
void End();
跟Init()配对使用,用于把临时数据都清理干净。
示例
下面这块代码,可以对一张输入图片,识别字符,之后保存到剪贴板里。
#include <QApplication>
#include <QClipboard>
#include <QImage>
#include <QPixmap>
#include <memory>
#include <tesseract/baseapi.h>
using namespace std;
using namespace tesseract;
std::unique_ptr<char>
getTextFromPixmap(const QPixmap &pixmap) {
auto api = TessBaseAPI{};
api.Init(nullptr, "eng+chi_sim");
auto image = pixmap.toImage();
image = image.convertToFormat(QImage::Format_RGB888);
api.SetImage(image.bits(), image.width(), image.height(), 3,
static_cast<int>(image.bytesPerLine()));
auto text = api.GetUTF8Text();
api.Clear();
api.End();
return unique_ptr<char>(text);
}
int
main(int argc, char *argv[]) {
Q_ASSERT(argc > 1);
QApplication app{argc, argv};
auto pixmap = QPixmap(argv[1]);
auto text = getTextFromPixmap(pixmap);
if (text) {
qInfo() << "get :" << text.get() << " from " << argv[1];
auto clipboard = QApplication::clipboard();
clipboard->setText(text.get());
} else {
qCritical() << "get nothing from " << argv[1];
}
}