最近有个项目需要用到 OCR 功能,研究了一下,感觉 Tesseract 还不错,准备在项目中试试。 但是发现 Tesseract 底层图像数据用到了 Leptonica。而我的程序准备用 Qt C++ 来写。这里就涉及一个问题,如何把 QImage 图像数据传递给 Tesseract。

花了两天时间,写了两个 Leptonica PIX 与 Qt QImage 的相互转换函数。

为了写这个代码,首先需要搞明白 PIX 都有哪些部分,如何构造。Leptonica 的文档不多,搞懂这些问题主要还是读源码。按照 Linus 的说法就是: reading the fucking code 。

PIX 的结构如下:

/*! Basic Pix */
struct Pix
{l_uint32             w;         /*!< width in pixels                   */l_uint32             h;         /*!< height in pixels                  */l_uint32             d;         /*!< depth in bits (bpp)               */l_uint32             spp;       /*!< number of samples per pixel       */l_uint32             wpl;       /*!< 32-bit words/line                 */l_uint32             refcount;  /*!< reference count (1 if no clones)  */l_int32              xres;      /*!< image res (ppi) in x direction    *//*!< (use 0 if unknown)                */l_int32              yres;      /*!< image res (ppi) in y direction    *//*!< (use 0 if unknown)                */l_int32              informat;  /*!< input file format, IFF_*          */l_int32              special;   /*!< special instructions for I/O, etc */char                *text;      /*!< text string associated with pix   */struct PixColormap  *colormap;  /*!< colormap (may be null)            */l_uint32            *data;      /*!< the image data                    */
};
typedef struct Pix PIX;

其中有几个字段需要简单介绍一下:

  • wpl 这个记录图像每行数据占用多少个 32bit word. QImage 里类似的对应物是 QImage::bytesPerLine()。当然准确的说是 bytesPerLine / 4。
  • 从这里我们还可以知道 PIX 中每行数据占用的空间都是 4字节的整数倍。也就是每行的首地址是四字节对齐的。
  • PIX 支持 colormap,在 QImage 中叫做 QImage::colorTable()。
  • data 指向真正的数据,虽然是 uint32 * 类型,但是实际数据有可能是 单字节、双字节、三字节(RGB24)或者 四字节(RGB32 或 RGBA32)。

由于PIX 强制要求每行数据4字节对齐,而 QImage 没这个要求。所以PIX 很难和 QImage共享数据。因此我下面的代码中都是新分配了数据空间之后复制数据,没有去考虑数据共享问题。这样会有些效率问题,但是一般情况下是够用的。如果真到了需要考虑这个转化过程耗时问题时,可以考虑再单独写一个函数来实现。不过 PIX 和 QImage 的数据中 R\G\B 三个分量的顺序不同,所以共享数据时肯定会遇到各种麻烦。
先来看看 PIX 到 QImage 的转换:

#include <leptonica/allheaders.h>
#include <QImage>
#include <QBuffer>
#include <QtDebug>
/*** @brief PIX2QImage Leptonica PIX 转换为 QImage。返回的 QImage 与 pixImage 不共享数据。* @param pixImage* @return*/
QImage PIX2QImage(PIX *pixImage)
{static QImage none(0, 0, QImage::Format_Invalid);if(pixImage == nullptr){qDebug() << "***Invalid format!!!";return none;}l_int32 width = pixGetWidth(pixImage);l_int32 height = pixGetHeight(pixImage);l_int32 depth = pixGetDepth(pixImage);l_int32 bytesPerLine = pixGetWpl(pixImage) * 4;l_int32 wpld = pixGetWpl(pixImage);l_uint32 * start = pixGetData(pixImage);//l_uint32 * s_data = pixGetData(pixEndianByteSwapNew(pixImage));QImage::Format format;switch (depth){case 1:format = QImage::Format_Mono;break;case 8:format = QImage::Format_Indexed8;break;case 24:format = QImage::Format_RGB888;break;default:format = QImage::Format_RGB32;break;}QImage result(width, height, format);if (result.format() == QImage::Format_RGB32){qDebug() << "QImage::Format_RGB32";for(int i = 0; i < height; i++){QRgb * lined = (QRgb *)result.scanLine(i);l_uint32 * lines = start + wpld * i ;for(int j = 0; j < width; j ++){l_int32 rval, gval, bval;extractRGBValues(lines[j], &rval, &gval, &bval);lined[j] = qRgb(rval, gval, bval);}}}else{for(int i = 0; i < height; i++){uchar * lined = result.scanLine(i);uchar * lines = (uchar *)(start + wpld * i) ;memcpy(lined , lines, static_cast<size_t>(bytesPerLine));}}
//PIXCMAP * pixcmap = pixGetColormap(pixImage);if(pixcmap != nullptr){qDebug() << "generate colorTable";QVector<QRgb> colorTable;RGBA_QUAD * map = (RGBA_QUAD *) pixcmap->array;for(int i = 0; i < pixcmap->n; i++){colorTable.append(qRgb(map[i].red, map[i].green, map[i].blue ));}result.setColorTable(colorTable);}return result;
}

为了解决 R、G、B 顺序问题,用到了 下面两个函数。这样效率可能会稍微低点,但是代码已读性会好很多,而且也不怕 PIX 或 QImage 改变底层数据表示。

extractRGBValues(lines[j], &rval, &gval, &bval);
lined[j] = qRgb(rval, gval, bval);

然后是 QImage 到 PIX 的代码:

/*** @brief QImage2Pix QImage 转换为 PIX,不共享数据* @param image* @return*/
PIX* QImage2Pix(const QImage &image)
{PIX * pix;int width = image.width();int height = image.height();int depth = image.depth();pix = pixCreate(width, height, depth);if(image.isNull() ){qDebug() << "image is null";return nullptr;}if( image.colorCount() ){QVector<QRgb> table = image.colorTable();PIXCMAP * map = pixcmapCreate(8);int n = table.size();for(int i = 0; i < n; i++){pixcmapAddColor(map, qRed(table[i]), qGreen(table[i]), qBlue(table[i]));}pixSetColormap(pix, map);}int bytePerLine = image.bytesPerLine();l_uint32* start = pixGetData(pix);l_int32 wpld = pixGetWpl(pix);if(image.format() == QImage::Format_Mono || image.format() == QImage::Format_Indexed8 || image.format() == QImage::Format_RGB888){for(int i = 0; i < height; i++){const uchar * lines = image.scanLine(i);uchar * lined = (uchar *)(start + wpld * i) ;memcpy(lined , lines, static_cast<size_t>(bytePerLine));}}else if (image.format() == QImage::Format_RGB32 || image.format() == QImage::Format_ARGB32){qDebug() << "QImage::Format_RGB32";for(int i = 0; i < image.height(); i++){const QRgb * lines = (const QRgb *)image.scanLine(i);l_uint32 * lined = start + wpld * i ;for(int j = 0; j < width; j ++){uchar rval = qRed(lines[j]);uchar gval = qGreen(lines[j]);uchar bval = qBlue(lines[j]);l_uint32 pixel;composeRGBPixel(rval, gval, bval, &pixel);lined[j] = pixel;}}}return pix;
}

这两个函数都没有考虑全 所有的图像格式,但是基本我们常见的8 bit \24 bit\32 bit 图像格式都支持了。8 bit 图像没有测试,24bit 和 32 bit 图像都测试通过。

如果不考虑效率,这代码还可以写的更简单些,比如 QImage 转换到 PIX 可以写为:

PIX* makePIXFromQImage(const QImage &image)
{QByteArray ba;QBuffer buf(&ba);buf.open(QIODevice::WriteOnly);image.save(&buf, "BMP");return pixReadMemBmp((const l_uint8*) ba.constData(), ba.size());
}

PIX 转换为 QImage 也可以类似的转换,这里就不介绍了。
希望上面的代码对大家有用。

Leptonica PIX 与 Qt QImage 的相互转换相关推荐

  1. Qt QImage 显示TIFF格式图片

    一,需求 利用Qt 控件 显示 tiff 图片,由于tiff图像深度位96位,3通道,所以无法直接用QImage 显示,QImage 支持24位,因此需要利用Opencv 进行转换. 二,关键点 (1 ...

  2. Qt QImage与OpenCV Mat转换

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51029382 应一个朋友的要求,整理总 ...

  3. Qt QImage scaled方法缩放中的问题

    最近在某些测试中发现,QImage 先按照一定的比例进行缩放,在对QImage对象进行绘制等操作后,使用以下的方式将其恢复到其原来的尺寸. 为了后面在可能在绘制的过程中让成员变量尽量的少,我定义了一个 ...

  4. Qt QImage类详解(QImage类型转换、QImage类函数及QImage像素操作)

    打开Qt帮助文档,会看到有关于QImage的描述如下:The QImage class provides a hardware-independent image representation tha ...

  5. Qt QImage 加载 BMP 图像的一个BUG

    这个问题源于水木社区的一个帖子:https://www.mysmth.net/nForum/#!article/KDE_Qt/27466 经过测试 QImage 加载像素数大于 16384*16384 ...

  6. QT QImage使用方法(Qt学习1)

    参考链接: 1.http://blog.csdn.net/feiyangyangfei/article/details/8672748 Qt信号与槽 以及图像在label缩放显示 //mysignal ...

  7. QT QImage 无法加载图片 png jpg

    需要在Debug(或者Release)目录下面放置imageformats  : Debug\imageformats imageformats 在 Qt安装目录的plugins下面,直接拷贝过来就可 ...

  8. Qt Qimage图片翻转(水平、垂直、顺时针、逆时针)

    1.水平翻转 void ImageViewer::horFilp(){image = image.mirrored(true, false);imageLabel->setPixmap(QPix ...

  9. QT QImage显示图片后缀问题,不能显示图片(Image is a null image)

    QImage在加载图片的时候是按图片后缀识别图片类型的,加入我们有一张png的图片,但是图片后缀却是jpg,这时候按jpg加载就会出错,所以我们加载图片的时候可以给QImage指定后缀列表 QImag ...

最新文章

  1. 【摄像头】宽动态范围
  2. 霸气!曝阿里于AI方面取得卓越成绩
  3. 调制优缺点_复合铝基润滑脂和普通润滑脂的区别,复合铝基脂有什么优缺点
  4. 【pip install psycopg2安装报错】Error: pg_config executable not found.
  5. we are the world 群星,
  6. URAL 1146 Maximum Sum(最大子矩阵的和 DP)
  7. python新手入门总结_初学python的操作难点总结(新手必看篇)
  8. Silverlight学习笔记三(鼠标点击动态画直线|动态设置Ellipse的Canvas.Top与Canvas.Left|动态设置Stroke属性的方法。)...
  9. 百度图神经网络学习——day02:图游走类模型
  10. 数据结构(严蔚敏版)与算法的实现(含全部代码)
  11. 捷联惯导matlab算法,捷联惯导算法与组合导航原理讲义(20170220).pdf
  12. 手把手教你:人脸识别的视频打码(基于opencv的人脸打马赛克)
  13. Qt之Cannot retrieve debugging output.
  14. 项目管理论坛_活动预告|2019年“VUCA时代项目管理与项目治理”论坛通知
  15. 详细理解安卓云信的接入及使用
  16. js闭包深入理解(Closure)
  17. SAS初学者笔记---003---利用数据步读取数据--逻辑库
  18. 28岁自学Python转行靠谱吗?入行晚吗?
  19. idea为web项目添加tomcat并配置Artifacts
  20. 均值定理最大值最小值公式_求函数最值问题复杂难算,只要用对方法,考试得分不用愁...

热门文章

  1. Anaconda(Python) Eric Pyqt Spyder 下载攻略等知识 汇总
  2. 软考高项——第七章质量管理
  3. opencv 图像识别
  4. namp安装及官方使用手册翻译及注释3
  5. DataLoader问题解决:RuntimeError: stack expects each tensor to be equal size, but got [3, 200, 200]entry1
  6. dwg文件的编辑方法有哪些
  7. 微信ipad协议,微信开发API接口
  8. 非暴力沟通:简介及内容导航
  9. 《非暴力沟通》就是分享式沟通
  10. 算法与数据结构 --- 串,数组和广义表 --- 串