Leptonica PIX 与 Qt QImage 的相互转换
最近有个项目需要用到 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 的相互转换相关推荐
- Qt QImage 显示TIFF格式图片
一,需求 利用Qt 控件 显示 tiff 图片,由于tiff图像深度位96位,3通道,所以无法直接用QImage 显示,QImage 支持24位,因此需要利用Opencv 进行转换. 二,关键点 (1 ...
- Qt QImage与OpenCV Mat转换
本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51029382 应一个朋友的要求,整理总 ...
- Qt QImage scaled方法缩放中的问题
最近在某些测试中发现,QImage 先按照一定的比例进行缩放,在对QImage对象进行绘制等操作后,使用以下的方式将其恢复到其原来的尺寸. 为了后面在可能在绘制的过程中让成员变量尽量的少,我定义了一个 ...
- Qt QImage类详解(QImage类型转换、QImage类函数及QImage像素操作)
打开Qt帮助文档,会看到有关于QImage的描述如下:The QImage class provides a hardware-independent image representation tha ...
- Qt QImage 加载 BMP 图像的一个BUG
这个问题源于水木社区的一个帖子:https://www.mysmth.net/nForum/#!article/KDE_Qt/27466 经过测试 QImage 加载像素数大于 16384*16384 ...
- QT QImage使用方法(Qt学习1)
参考链接: 1.http://blog.csdn.net/feiyangyangfei/article/details/8672748 Qt信号与槽 以及图像在label缩放显示 //mysignal ...
- QT QImage 无法加载图片 png jpg
需要在Debug(或者Release)目录下面放置imageformats : Debug\imageformats imageformats 在 Qt安装目录的plugins下面,直接拷贝过来就可 ...
- Qt Qimage图片翻转(水平、垂直、顺时针、逆时针)
1.水平翻转 void ImageViewer::horFilp(){image = image.mirrored(true, false);imageLabel->setPixmap(QPix ...
- QT QImage显示图片后缀问题,不能显示图片(Image is a null image)
QImage在加载图片的时候是按图片后缀识别图片类型的,加入我们有一张png的图片,但是图片后缀却是jpg,这时候按jpg加载就会出错,所以我们加载图片的时候可以给QImage指定后缀列表 QImag ...
最新文章
- 【摄像头】宽动态范围
- 霸气!曝阿里于AI方面取得卓越成绩
- 调制优缺点_复合铝基润滑脂和普通润滑脂的区别,复合铝基脂有什么优缺点
- 【pip install psycopg2安装报错】Error: pg_config executable not found.
- we are the world 群星,
- URAL 1146 Maximum Sum(最大子矩阵的和 DP)
- python新手入门总结_初学python的操作难点总结(新手必看篇)
- Silverlight学习笔记三(鼠标点击动态画直线|动态设置Ellipse的Canvas.Top与Canvas.Left|动态设置Stroke属性的方法。)...
- 百度图神经网络学习——day02:图游走类模型
- 数据结构(严蔚敏版)与算法的实现(含全部代码)
- 捷联惯导matlab算法,捷联惯导算法与组合导航原理讲义(20170220).pdf
- 手把手教你:人脸识别的视频打码(基于opencv的人脸打马赛克)
- Qt之Cannot retrieve debugging output.
- 项目管理论坛_活动预告|2019年“VUCA时代项目管理与项目治理”论坛通知
- 详细理解安卓云信的接入及使用
- js闭包深入理解(Closure)
- SAS初学者笔记---003---利用数据步读取数据--逻辑库
- 28岁自学Python转行靠谱吗?入行晚吗?
- idea为web项目添加tomcat并配置Artifacts
- 均值定理最大值最小值公式_求函数最值问题复杂难算,只要用对方法,考试得分不用愁...
热门文章
- Anaconda(Python) Eric Pyqt Spyder 下载攻略等知识 汇总
- 软考高项——第七章质量管理
- opencv 图像识别
- namp安装及官方使用手册翻译及注释3
- DataLoader问题解决:RuntimeError: stack expects each tensor to be equal size, but got [3, 200, 200]entry1
- dwg文件的编辑方法有哪些
- 微信ipad协议,微信开发API接口
- 非暴力沟通:简介及内容导航
- 《非暴力沟通》就是分享式沟通
- 算法与数据结构 --- 串,数组和广义表 --- 串