You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

439 lines
17 KiB
C

/*! \file QtCVUtils.h
\brief A brief file description.
A more elaborated file description.
Created: 2014/12/30, author: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
*/
#ifndef __qtcvutils_h_
#define __qtcvutils_h_
#include <opencv2/opencv.hpp>
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtGui/QVector3D>
#else
#include "QImage"
#include "QPixmap"
#include "QVector3D"
#endif
#include <qlogging.h>
#include <QDebug>
#include <vector>
using namespace cv;
// std::vector to QList
template<typename Tqt, typename Tcv> static QList<Tqt> _cvqt_cast_list(const std::vector<Tcv>& v) {
QList<Tqt> ret; ret.reserve(v.size());
for (const Tcv& p : v) ret.push_back(cvqt_cast<Tqt>(p));
return ret;
}
// std::list to QList
template<typename Tqt, typename Tcv> static QList<Tqt> _cvqt_cast_list(const std::list<Tcv>& v) {
QList<Tqt> ret; ret.reserve(v.size());
for (const Tcv& p : v) ret.push_back(cvqt_cast<Tqt>(p));
return ret;
}
// QList to std::list
template<typename Tqt, typename Tcv> static std::list<Tcv> _cvqt_cast_list(const QList<Tqt>& v) {
std::list<Tcv> ret;
foreach(const Tqt& p, v) ret.push_back(cvqt_cast<Tcv>(p));
return ret;
}
// QVector to std::list
template<typename Tqt, typename Tcv> static std::list<Tcv> _cvqt_cast_list(const QVector<Tqt>& v) {
std::list<Tcv> ret;
foreach(const Tqt& p, v) ret.push_back(cvqt_cast<Tcv>(p));
return ret;
}
// std::vector to QVector
template<typename Tqt, typename Tcv> static QVector<Tqt> _cvqt_cast_vec(const std::vector<Tcv>& v) {
QVector<Tqt> ret; ret.reserve(v.size());
for (const Tcv& p : v) ret.push_back(cvqt_cast<Tqt>(p));
return ret;
}
// std::list to QVector
template<typename Tqt, typename Tcv> static QVector<Tqt> _cvqt_cast_vec(const std::list<Tcv>& v) {
QVector<Tqt> ret; ret.reserve(v.size());
for (const Tcv& p : v) ret.push_back(cvqt_cast<Tqt>(p));
return ret;
}
// QList to std::vector
template<typename Tqt, typename Tcv> static std::vector<Tcv> _cvqt_cast_vec(const QList<Tqt>& v) {
std::vector<Tcv> ret; ret.reserve(v.size());
foreach(const Tqt& p, v) ret.push_back(cvqt_cast<Tcv>(p));
return ret;
}
// QVector to std::vector
template<typename Tqt, typename Tcv> static std::vector<Tcv> _cvqt_cast_vec(const QVector<Tqt>& v) {
std::vector<Tcv> ret; ret.reserve(v.size());
foreach(const Tqt& p, v) ret.push_back(cvqt_cast<Tcv>(p));
return ret;
}
#define INST_CVQT_CAST_CONTAINER(TQT, TCV)\
template<> inline QList<TQT> cvqt_cast<QList<TQT> >(const std::list<TCV>& v) { return _cvqt_cast_list<TQT, TCV>(v); }\
template<> inline QVector<TQT> cvqt_cast<QVector<TQT> >(const std::list<TCV>& v) { return _cvqt_cast_vec<TQT, TCV>(v); }\
template<> inline QList<TQT> cvqt_cast<QList<TQT> >(const std::vector<TCV>& v) { return _cvqt_cast_list<TQT, TCV>(v); }\
template<> inline QVector<TQT> cvqt_cast<QVector<TQT> >(const std::vector<TCV>& v) { return _cvqt_cast_vec<TQT, TCV>(v); }\
template<> inline std::list<TCV> cvqt_cast<std::list<TCV> >(const QList<TQT>& v) { return _cvqt_cast_list<TQT, TCV>(v);}\
template<> inline std::list<TCV> cvqt_cast<std::list<TCV> >(const QVector<TQT>& v) { return _cvqt_cast_list<TQT, TCV>(v);}\
template<> inline std::vector<TCV> cvqt_cast<std::vector<TCV> >(const QList<TQT>& v) { return _cvqt_cast_vec<TQT, TCV>(v);}\
template<> inline std::vector<TCV> cvqt_cast<std::vector<TCV> >(const QVector<TQT>& v) { return _cvqt_cast_vec<TQT, TCV>(v);}
#define DECL_CVQT_CAST_QT(TQT)\
template<typename Tcv> static inline Tcv cvqt_cast(const TQT& v) { return Tcv(); }\
template<typename Tcv> static inline Tcv cvqt_cast(const QList<TQT>& v) { return Tcv(); }\
template<typename Tcv> static inline Tcv cvqt_cast(const QVector<TQT>& v) { return Tcv(); }
#define INSTANCE_CVQT_CAST_QT(TQT)\
template<> static inline std::vector<Point2f> cvqt_cast<std::vector<Point2f>> (const TQT& v) {\
std::vector<Point2f> ret;\
for (int i = 0; i < v.size(); ++i)\
ret.push_back(Point2f(v[i].x(), v[i].y()));\
return ret;\
}
// Points ---begin
// declarations
template<typename Tcv> static inline Tcv cvqt_cast(const QPoint& v) { return Tcv(v.x(), v.y()); }
template<typename Tcv> static inline Tcv cvqt_cast(const QPointF& v) { return Tcv(v.x(), v.y()); }
template<typename Tcv> static inline Tcv cvqt_cast(const QList<QPoint>& v) { return Tcv(); }
template<typename Tcv> static inline Tcv cvqt_cast(const QVector<QPoint>& v) { return Tcv(); }
template<typename Tcv> static inline Tcv cvqt_cast(const QList<QPointF>& v) { return Tcv(); }
template<typename Tcv> static inline Tcv cvqt_cast(const QVector<QPointF>& v) { return Tcv(); }
#define DECL_CVQT_CAST_POINT(TCV)\
template<typename Tqt> static inline Tqt cvqt_cast(const Point_<TCV>& v) { return Tqt(v.x, v.y); }\
template<typename Tqt> static inline Tqt cvqt_cast(const std::list<Point_<TCV> >& v) { return Tqt(); }\
template<typename Tqt> static inline Tqt cvqt_cast(const std::vector<Point_<TCV> >& v) { return Tqt(); }
DECL_CVQT_CAST_POINT(int) // Point2i
DECL_CVQT_CAST_POINT(float) // Point2f
DECL_CVQT_CAST_POINT(double) // Point2d
// instantiations
template<> inline QPoint cvqt_cast<QPoint>(const Point2f& v) { return QPoint(saturate_cast<int>(v.x), saturate_cast<int>(v.y)); }
template<> inline QPoint cvqt_cast<QPoint>(const Point2d& v) { return QPoint(saturate_cast<int>(v.x), saturate_cast<int>(v.y)); }
template<> inline Point2i cvqt_cast<Point2i>(const QPointF& v) { return Point2i(saturate_cast<int>(v.x()), saturate_cast<int>(v.y())); }
#define INST_CVQT_CAST_POINT(TCV)\
template<> inline QPolygon cvqt_cast<QPolygon>(const std::list<TCV>& v) { QPolygon ret; ret.reserve(v.size());\
for (const TCV& p : v) { ret << cvqt_cast<QPoint>(p); } return ret; }\
template<> inline QPolygon cvqt_cast<QPolygon>(const std::vector<TCV>& v) { QPolygon ret; ret.reserve(v.size());\
for (const TCV& p : v) { ret << cvqt_cast<QPoint>(p); } return ret; }\
template<> inline QPolygonF cvqt_cast<QPolygonF>(const std::list<TCV>& v) { QPolygonF ret; ret.reserve(v.size());\
for (const TCV& p : v) { ret << cvqt_cast<QPointF>(p); } return ret; }\
template<> inline QPolygonF cvqt_cast<QPolygonF>(const std::vector<TCV>& v) { QPolygonF ret; ret.reserve(v.size());\
for (const TCV& p : v) { ret << cvqt_cast<QPointF>(p); } return ret; }
INST_CVQT_CAST_POINT(Point2i)
INST_CVQT_CAST_POINT(Point2f)
INST_CVQT_CAST_POINT(Point2d)
// container instantiations
INST_CVQT_CAST_CONTAINER(QPoint, Point2i)
INST_CVQT_CAST_CONTAINER(QPoint, Point2f)
INST_CVQT_CAST_CONTAINER(QPoint, Point2d)
INST_CVQT_CAST_CONTAINER(QPointF, Point2i)
INST_CVQT_CAST_CONTAINER(QPointF, Point2f)
INST_CVQT_CAST_CONTAINER(QPointF, Point2d)
// Points ---end
// 3D Points ---begin
// declarations
DECL_CVQT_CAST_QT(QVector3D)
#define DECL_CVQT_CAST_3DPOINT(TCV)\
template<typename Tqt> static inline Tqt cvqt_cast(const Point3_<TCV>& v) { return Tqt(v.x, v.y, v.z); }\
template<typename Tqt> static inline Tqt cvqt_cast(const std::list<Point3_<TCV> >& v) { return Tqt(); }\
template<typename Tqt> static inline Tqt cvqt_cast(const std::vector<Point3_<TCV> >& v) { return Tqt(); }
DECL_CVQT_CAST_3DPOINT(int) // Point3i
DECL_CVQT_CAST_3DPOINT(float) // Point3f
DECL_CVQT_CAST_3DPOINT(double) // Point3d
// instantiations
#define INST_CVQT_CAST_3DPOINT(TCV)\
template<> inline QVector3D cvqt_cast<QVector3D>(const TCV& v) { return QVector3D(v.x, v.y, v.z); }\
template<> inline TCV cvqt_cast<TCV>(const QVector3D& v) { return TCV(v.x(), v.y(), v.z()); }
INST_CVQT_CAST_3DPOINT(Point3i)
INST_CVQT_CAST_3DPOINT(Point3f)
INST_CVQT_CAST_3DPOINT(Point3d)
INST_CVQT_CAST_CONTAINER(QVector3D, Point3i)
INST_CVQT_CAST_CONTAINER(QVector3D, Point3f)
INST_CVQT_CAST_CONTAINER(QVector3D, Point3d)
// 3D Points ---end
// Circle ---begin
// declarations
DECL_CVQT_CAST_QT(QPolygon)
DECL_CVQT_CAST_QT(QPolygonF)
INSTANCE_CVQT_CAST_QT(QPolygonF)
#define DECL_CVQT_CAST_VEC(TCV, CN)\
template<typename Tqt> static inline Tqt cvqt_cast(const Vec<TCV, CN>& v) { Tqt ret; for (int i = 0; i < CN; ++i) ret << v[i]; return ret; }\
template<typename Tqt> static inline Tqt cvqt_cast(const std::list<Vec<TCV, CN> >& v) { return Tqt(); }\
template<typename Tqt> static inline Tqt cvqt_cast(const std::vector<Vec<TCV, CN> >& v) { return Tqt(); }
DECL_CVQT_CAST_VEC(int, 3) // Vec3i
DECL_CVQT_CAST_VEC(float, 3) // Vec3f
DECL_CVQT_CAST_VEC(double, 3) // Vec3d
// instantiations
#define INST_CVQT_CAST_CIRCEL(TCV)\
template<> inline QPolygon cvqt_cast<QPolygon>(const TCV& v) {\
return QPolygon() << QPoint(saturate_cast<int>(v[0]), saturate_cast<int>(v[1])) << QPoint(saturate_cast<int>(v[0]), saturate_cast<int>(v[1] + v[2])); }\
template<> inline QPolygonF cvqt_cast<QPolygonF>(const TCV& v) { return QPolygonF() << QPointF(v[0], v[1]) << QPointF(v[0], v[1] + v[2]); }\
template<> inline TCV cvqt_cast<TCV>(const QPolygon& v) { return TCV(v[0].x(), v[0].y(), std::hypot(v[0].x() - v[1].x(), v[0].y() - v[1].y())); }\
template<> inline TCV cvqt_cast<TCV>(const QPolygonF& v) { return TCV(v[0].x(), v[0].y(), std::hypot(v[0].x() - v[1].x(), v[0].y() - v[1].y())); }
INST_CVQT_CAST_CIRCEL(Vec3i)
INST_CVQT_CAST_CIRCEL(Vec3f)
INST_CVQT_CAST_CIRCEL(Vec3d)
// container instantiations
INST_CVQT_CAST_CONTAINER(QPolygon, Vec3i)
INST_CVQT_CAST_CONTAINER(QPolygon, Vec3f)
INST_CVQT_CAST_CONTAINER(QPolygon, Vec3d)
INST_CVQT_CAST_CONTAINER(QPolygonF, Vec3i)
INST_CVQT_CAST_CONTAINER(QPolygonF, Vec3f)
INST_CVQT_CAST_CONTAINER(QPolygonF, Vec3d)
// Circle ---end
// Line ---begin
// declarations
DECL_CVQT_CAST_QT(QLine)
DECL_CVQT_CAST_QT(QLineF)
DECL_CVQT_CAST_VEC(int, 4) // Vec4i
DECL_CVQT_CAST_VEC(float, 4) // Vec4f
DECL_CVQT_CAST_VEC(double, 4) // Vec4d
// instantiations
#define INST_CVQT_CAST_LINE(TCV)\
template<> inline QLine cvqt_cast<QLine>(const TCV& v) { return QLine(saturate_cast<int>(v[0]), saturate_cast<int>(v[1]),\
saturate_cast<int>(v[2]), saturate_cast<int>(v[3])); }\
template<> inline QLineF cvqt_cast<QLineF>(const TCV& v) { return QLineF(v[0], v[1], v[2], v[3]); }\
template<> inline TCV cvqt_cast<TCV>(const QLine& v) { return TCV(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y()); }\
template<> inline TCV cvqt_cast<TCV>(const QLineF& v) { return TCV(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y()); }
INST_CVQT_CAST_LINE(Vec4i)
INST_CVQT_CAST_LINE(Vec4f)
INST_CVQT_CAST_LINE(Vec4d)
// container instantiations
INST_CVQT_CAST_CONTAINER(QLine, Vec4i)
INST_CVQT_CAST_CONTAINER(QLine, Vec4f)
INST_CVQT_CAST_CONTAINER(QLine, Vec4d)
INST_CVQT_CAST_CONTAINER(QLineF, Vec4i)
INST_CVQT_CAST_CONTAINER(QLineF, Vec4f)
INST_CVQT_CAST_CONTAINER(QLineF, Vec4d)
// Line ---end
// Size ---begin
// declarations
template<typename Tcv> static inline Tcv cvqt_cast(const QSize& v) { return Tcv(v.width(), v.height()); }
template<typename Tcv> static inline Tcv cvqt_cast(const QSizeF& v) { return Tcv(v.width(), v.height()); }
template<typename Tqt> static inline Tqt cvqt_cast(const Size& v) { return Tqt(v.width, v.height); }
template<typename Tqt> static inline Tqt cvqt_cast(const Sizef& v) { return Tqt(v.width, v.height); }
// instantiations
template<> static inline Size cvqt_cast<Size>(const QSizeF& v) { return Size(saturate_cast<int>(v.width()), saturate_cast<int>(v.height())); }
template<> static inline QSize cvqt_cast<QSize>(const Sizef& v) { return QSize(saturate_cast<int>(v.width), saturate_cast<int>(v.height)); }
// Size ---end
namespace lp
{
class QtCVUtils
{
public:
// If inImage exists for the lifetime of the resulting cv::Mat,
// pass false to inCloneImageData to share inImage's data with
// the cv::Mat directly
// NOTE: Format_RGB888 is an exception since we need to use a
// local QImage and thus must clone the data regardless
static inline cv::Mat QImageToCvMat(const QImage &inImage,
bool inCloneImageData = true)
{
switch (inImage.format())
{
// 8-bit, 4 channel
case QImage::Format_RGBA8888:
{
QImage swapped = inImage.rgbSwapped();
cv::Mat mat(swapped.height(), swapped.width(),
CV_8UC4, const_cast<uchar*>(swapped.bits()),
swapped.bytesPerLine());
return (inCloneImageData ? mat.clone() : mat);
}
case QImage::Format_ARGB32:
{
cv::Mat mat(inImage.height(), inImage.width(),
CV_8UC4, const_cast<uchar*>(inImage.bits()),
inImage.bytesPerLine());
return (inCloneImageData ? mat.clone() : mat);
}
case QImage::Format_RGB32:
{
cv::Mat mat(inImage.height(), inImage.width(),
CV_8UC4, const_cast<uchar*>(inImage.bits()),
inImage.bytesPerLine());
return (inCloneImageData ? mat.clone() : mat);
}
// 8-bit, 3 channel
case QImage::Format_RGB888:
{
if (!inCloneImageData)
qWarning() << "ASM::QImageToCvMat() - \
Conversion requires cloning since we use \
a temporary QImage";
QImage swapped = inImage.rgbSwapped();
return cv::Mat(swapped.height(), swapped.width(),
CV_8UC3, const_cast<uchar*>(swapped.bits()),
swapped.bytesPerLine()).clone();
}
// 8-bit, 1 channel
case QImage::Format_Indexed8:
{
cv::Mat mat(inImage.height(), inImage.width(),
CV_8UC1, const_cast<uchar*>(inImage.bits()),
inImage.bytesPerLine());
return (inCloneImageData ? mat.clone() : mat);
}
default:
qWarning() << "ASM::QImageToCvMat() - QImage format \
not handled in switch:" << inImage.format();
break;
}
return cv::Mat();
}
// If inPixmap exists for the lifetime of the resulting cv::Mat,
// pass false to inCloneImageData to share inPixmap's data with
// the cv::Mat directly
// NOTE: Format_RGB888 is an exception since we need to use a
// local QImage and thus must clone the data regardless
static inline cv::Mat QPixmapToCvMat(const QPixmap &inPixmap,
bool inCloneImageData = true)
{
return QImageToCvMat(inPixmap.toImage(), inCloneImageData);
}
// warning: isSwapRB should always be true, unless you do know what
// you are doing.
static inline QImage cvMatToQImage(const cv::Mat &inMat,
bool isCloneImageData = false, bool isSwapRB = true)
{
uchar* pData = NULL;
uchar* pBuffer = NULL;
QImageCleanupFunction cleanupFunc = NULL;
if (isCloneImageData)
{
pBuffer = new uchar[inMat.step*inMat.rows];
memcpy(pBuffer, inMat.data, inMat.step*inMat.rows);
cleanupFunc = [](void* p) { delete[]p; };
pData = pBuffer;
}
else
{
pData = inMat.data;
}
switch (inMat.type())
{
// 8-bit, 4 channel
case CV_8UC4:
{
QImage image(pData, inMat.cols, inMat.rows, inMat.step,
QImage::Format_RGBA8888, cleanupFunc, pBuffer);
if (isSwapRB)
{
return image.rgbSwapped();
}
else
{
return image;
}
}
// 8-bit, 3 channel
case CV_8UC3:
{
QImage image(pData, inMat.cols, inMat.rows, inMat.step,
QImage::Format_RGB888, cleanupFunc, pBuffer);
if (isSwapRB)
{
return image.rgbSwapped();
}
else
{
return image;
}
}
// 8-bit, 1 channel
case CV_8UC1:
{
static QVector<QRgb> sColorTable;
// only create our color table once
if (sColorTable.isEmpty())
{
for (int i = 0; i < 256; ++i)
sColorTable.push_back(qRgb(i, i, i));
}
QImage image(pData, inMat.cols, inMat.rows, inMat.step,
QImage::Format_Indexed8, cleanupFunc, pBuffer);
image.setColorTable(sColorTable);
return image;
}
case CV_32FC1:
{
double minVal, maxVal;
minMaxIdx(inMat, &minVal, &maxVal);
cv::Mat normedMat = (inMat - minVal) / (maxVal - minVal) * 255;
cv::Mat ret;
normedMat.convertTo(ret, CV_8UC1);
return lp::QtCVUtils::cvMatToQImage(ret, isCloneImageData);
}
default:
qWarning() << "ASM::cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
break;
}
return QImage();
}
static inline QPixmap cvMatToQPixmap(const cv::Mat &inMat, bool isCloneData = false)
{
return QPixmap::fromImage(cvMatToQImage(inMat, isCloneData));
}
static inline void qtRectFCenterScale(QRectF& rect, float wScale, float hScale)
{
QPointF oldCenter = rect.center();
rect.setWidth(rect.width() * wScale);
rect.setHeight(rect.height() * hScale);
rect.moveCenter(oldCenter);
}
};
}
#endif // __qtcvutils_h_