/*! \file QtCVUtils.h \brief A brief file description. A more elaborated file description. Created: 2014/12/30, author: ½ð±üÎÄ. */ #ifndef __qtcvutils_h_ #define __qtcvutils_h_ #include #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) #include #include #include #else #include "QImage" #include "QPixmap" #include "QVector3D" #endif #include #include #include using namespace cv; // std::vector to QList template static QList _cvqt_cast_list(const std::vector& v) { QList ret; ret.reserve(v.size()); for (const Tcv& p : v) ret.push_back(cvqt_cast(p)); return ret; } // std::list to QList template static QList _cvqt_cast_list(const std::list& v) { QList ret; ret.reserve(v.size()); for (const Tcv& p : v) ret.push_back(cvqt_cast(p)); return ret; } // QList to std::list template static std::list _cvqt_cast_list(const QList& v) { std::list ret; foreach(const Tqt& p, v) ret.push_back(cvqt_cast(p)); return ret; } // QVector to std::list template static std::list _cvqt_cast_list(const QVector& v) { std::list ret; foreach(const Tqt& p, v) ret.push_back(cvqt_cast(p)); return ret; } // std::vector to QVector template static QVector _cvqt_cast_vec(const std::vector& v) { QVector ret; ret.reserve(v.size()); for (const Tcv& p : v) ret.push_back(cvqt_cast(p)); return ret; } // std::list to QVector template static QVector _cvqt_cast_vec(const std::list& v) { QVector ret; ret.reserve(v.size()); for (const Tcv& p : v) ret.push_back(cvqt_cast(p)); return ret; } // QList to std::vector template static std::vector _cvqt_cast_vec(const QList& v) { std::vector ret; ret.reserve(v.size()); foreach(const Tqt& p, v) ret.push_back(cvqt_cast(p)); return ret; } // QVector to std::vector template static std::vector _cvqt_cast_vec(const QVector& v) { std::vector ret; ret.reserve(v.size()); foreach(const Tqt& p, v) ret.push_back(cvqt_cast(p)); return ret; } #define INST_CVQT_CAST_CONTAINER(TQT, TCV)\ template<> inline QList cvqt_cast >(const std::list& v) { return _cvqt_cast_list(v); }\ template<> inline QVector cvqt_cast >(const std::list& v) { return _cvqt_cast_vec(v); }\ template<> inline QList cvqt_cast >(const std::vector& v) { return _cvqt_cast_list(v); }\ template<> inline QVector cvqt_cast >(const std::vector& v) { return _cvqt_cast_vec(v); }\ template<> inline std::list cvqt_cast >(const QList& v) { return _cvqt_cast_list(v);}\ template<> inline std::list cvqt_cast >(const QVector& v) { return _cvqt_cast_list(v);}\ template<> inline std::vector cvqt_cast >(const QList& v) { return _cvqt_cast_vec(v);}\ template<> inline std::vector cvqt_cast >(const QVector& v) { return _cvqt_cast_vec(v);} #define DECL_CVQT_CAST_QT(TQT)\ template static inline Tcv cvqt_cast(const TQT& v) { return Tcv(); }\ template static inline Tcv cvqt_cast(const QList& v) { return Tcv(); }\ template static inline Tcv cvqt_cast(const QVector& v) { return Tcv(); } #define INSTANCE_CVQT_CAST_QT(TQT)\ template<> static inline std::vector cvqt_cast> (const TQT& v) {\ std::vector 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 static inline Tcv cvqt_cast(const QPoint& v) { return Tcv(v.x(), v.y()); } template static inline Tcv cvqt_cast(const QPointF& v) { return Tcv(v.x(), v.y()); } template static inline Tcv cvqt_cast(const QList& v) { return Tcv(); } template static inline Tcv cvqt_cast(const QVector& v) { return Tcv(); } template static inline Tcv cvqt_cast(const QList& v) { return Tcv(); } template static inline Tcv cvqt_cast(const QVector& v) { return Tcv(); } #define DECL_CVQT_CAST_POINT(TCV)\ template static inline Tqt cvqt_cast(const Point_& v) { return Tqt(v.x, v.y); }\ template static inline Tqt cvqt_cast(const std::list >& v) { return Tqt(); }\ template static inline Tqt cvqt_cast(const std::vector >& 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(const Point2f& v) { return QPoint(saturate_cast(v.x), saturate_cast(v.y)); } template<> inline QPoint cvqt_cast(const Point2d& v) { return QPoint(saturate_cast(v.x), saturate_cast(v.y)); } template<> inline Point2i cvqt_cast(const QPointF& v) { return Point2i(saturate_cast(v.x()), saturate_cast(v.y())); } #define INST_CVQT_CAST_POINT(TCV)\ template<> inline QPolygon cvqt_cast(const std::list& v) { QPolygon ret; ret.reserve(v.size());\ for (const TCV& p : v) { ret << cvqt_cast(p); } return ret; }\ template<> inline QPolygon cvqt_cast(const std::vector& v) { QPolygon ret; ret.reserve(v.size());\ for (const TCV& p : v) { ret << cvqt_cast(p); } return ret; }\ template<> inline QPolygonF cvqt_cast(const std::list& v) { QPolygonF ret; ret.reserve(v.size());\ for (const TCV& p : v) { ret << cvqt_cast(p); } return ret; }\ template<> inline QPolygonF cvqt_cast(const std::vector& v) { QPolygonF ret; ret.reserve(v.size());\ for (const TCV& p : v) { ret << cvqt_cast(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 static inline Tqt cvqt_cast(const Point3_& v) { return Tqt(v.x, v.y, v.z); }\ template static inline Tqt cvqt_cast(const std::list >& v) { return Tqt(); }\ template static inline Tqt cvqt_cast(const std::vector >& 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(const TCV& v) { return QVector3D(v.x, v.y, v.z); }\ template<> inline TCV cvqt_cast(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 static inline Tqt cvqt_cast(const Vec& v) { Tqt ret; for (int i = 0; i < CN; ++i) ret << v[i]; return ret; }\ template static inline Tqt cvqt_cast(const std::list >& v) { return Tqt(); }\ template static inline Tqt cvqt_cast(const std::vector >& 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(const TCV& v) {\ return QPolygon() << QPoint(saturate_cast(v[0]), saturate_cast(v[1])) << QPoint(saturate_cast(v[0]), saturate_cast(v[1] + v[2])); }\ template<> inline QPolygonF cvqt_cast(const TCV& v) { return QPolygonF() << QPointF(v[0], v[1]) << QPointF(v[0], v[1] + v[2]); }\ template<> inline TCV cvqt_cast(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(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(const TCV& v) { return QLine(saturate_cast(v[0]), saturate_cast(v[1]),\ saturate_cast(v[2]), saturate_cast(v[3])); }\ template<> inline QLineF cvqt_cast(const TCV& v) { return QLineF(v[0], v[1], v[2], v[3]); }\ template<> inline TCV cvqt_cast(const QLine& v) { return TCV(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y()); }\ template<> inline TCV cvqt_cast(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 static inline Tcv cvqt_cast(const QSize& v) { return Tcv(v.width(), v.height()); } template static inline Tcv cvqt_cast(const QSizeF& v) { return Tcv(v.width(), v.height()); } template static inline Tqt cvqt_cast(const Size& v) { return Tqt(v.width, v.height); } template static inline Tqt cvqt_cast(const Sizef& v) { return Tqt(v.width, v.height); } // instantiations template<> static inline Size cvqt_cast(const QSizeF& v) { return Size(saturate_cast(v.width()), saturate_cast(v.height())); } template<> static inline QSize cvqt_cast(const Sizef& v) { return QSize(saturate_cast(v.width), saturate_cast(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(swapped.bits()), swapped.bytesPerLine()); return (inCloneImageData ? mat.clone() : mat); } case QImage::Format_ARGB32: { cv::Mat mat(inImage.height(), inImage.width(), CV_8UC4, const_cast(inImage.bits()), inImage.bytesPerLine()); return (inCloneImageData ? mat.clone() : mat); } case QImage::Format_RGB32: { cv::Mat mat(inImage.height(), inImage.width(), CV_8UC4, const_cast(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(swapped.bits()), swapped.bytesPerLine()).clone(); } // 8-bit, 1 channel case QImage::Format_Indexed8: { cv::Mat mat(inImage.height(), inImage.width(), CV_8UC1, const_cast(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 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_