原文链接
前言
在前文交叉编译armv7运行环境以及嵌入式opencv的编译示例以及实现嵌入式设备中的人脸检测中,我们都是在Linux
环境中工作的。如果需要引用摄像头等多媒体测试,Linux
编译机就没有那么易用了,这里我们以windows
举例演示opencv
库的引用及二维码的识别
环境
- 编译器:
Qt
(MinGW
或者MSVC
)或者VS
- 视觉库:opencv source,opencv document
opencv编译
前文中已经提及了关于opencv
的交叉编译,本地编译只需要修改编译文件即可。如果懒的编译的小伙伴可以直接从github
上下载官方已经编译完成的release
资源,opencv release。如果你用是的MSVC
那么是可以直接使用的,如果你使用的是MinGW
是无法使用的,此时我们可以找其他人编译好的版本,比如说OpenCV-MinGW-Build,选择对应的编译器位数下载即可。如果需要人脸识别等附加功能,由于目前人脸识别不在opencv
基本库中,而是处于opencv contrib中,此时我们需要选择带contrib
编译的包,如果是自己编译的小伙伴也需要相应修改编译命令
这里我们以32位带contrib
的4.5.0
版本举例
此时我们有{path}/x64/mingw/lib
,{path}/x64/mingw/bin
的动态库,以及{path}/include/opencv2
的头文件
Qt构建
.pro
文件中引入库以及头文件
INCLUDEPATH += $${YOUR_PATH}/include
LIBS+= $${YOUR_PATH}/x64/mingw/bin/libopencv_*.dll
然后随便写个程序测试一下
//main
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
//.h
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
virtual ~Widget() = default;
private:
bool inCodeParse = false;
QVideoProbe* m_probe = nullptr;
QVBoxLayout* m_mainLayout = nullptr;
QCameraViewfinder* m_viewfinder = nullptr;
QScopedPointer<QCamera> m_camera;
public slots:
void slotOnProbeFrame(const QVideoFrame& frame);
};
//cpp
Widget::Widget(QWidget* parent) : QWidget(parent)
{
this->resize(750, 500);
m_mainLayout = new QVBoxLayout(this);
m_mainLayout->setSpacing(0);
m_mainLayout->setContentsMargins(0, 0, 0, 0);
m_viewfinder = new QCameraViewfinder(this);
m_viewfinder->show();
m_mainLayout->addWidget(m_viewfinder);
QCameraInfo defualtCamera = QCameraInfo::defaultCamera();
if (defualtCamera.isNull())
{
// todo throw
}
m_camera.reset(new QCamera(defualtCamera, this));
m_camera.data()->setViewfinder(m_viewfinder);
// probe
m_probe = new QVideoProbe();
connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(slotOnProbeFrame(QVideoFrame)), Qt::QueuedConnection);
m_probe->setSource(m_camera.data());
// start
m_camera->start();
}
cv::Mat qImg2Mat(QImage image)
{
cv::Mat mat;
switch (image.format())
{
case QImage::Format_ARGB32:
case QImage::Format_RGB32:
case QImage::Format_ARGB32_Premultiplied:
mat = cv::Mat(image.height(), image.width(), CV_8UC4, ( void* )image.constBits(), image.bytesPerLine());
break;
case QImage::Format_RGB888:
mat = cv::Mat(image.height(), image.width(), CV_8UC3, ( void* )image.constBits(), image.bytesPerLine());
cv::cvtColor(mat, mat, CV_BGR2RGB);
break;
case QImage::Format_Indexed8:
mat = cv::Mat(image.height(), image.width(), CV_8UC1, ( void* )image.constBits(), image.bytesPerLine());
break;
}
return mat;
}
cv::Mat qVideoFrame2Mat(const QVideoFrame& buffer)
{
cv::Mat mat;
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(buffer.pixelFormat());
switch (imageFormat)
{
case QImage::Format_ARGB32:
case QImage::Format_RGB32:
case QImage::Format_ARGB32_Premultiplied:
mat = cv::Mat(buffer.height(), buffer.width(), CV_8UC4, ( void* )buffer.bits(), buffer.bytesPerLine());
break;
case QImage::Format_RGB888:
mat = cv::Mat(buffer.height(), buffer.width(), CV_8UC3, ( void* )buffer.bits(), buffer.bytesPerLine());
cv::cvtColor(mat, mat, CV_BGR2RGB);
break;
case QImage::Format_Indexed8:
mat = cv::Mat(buffer.height(), buffer.width(), CV_8UC1, ( void* )buffer.bits(), buffer.bytesPerLine());
break;
}
return mat;
}
void Widget::slotOnProbeFrame(const QVideoFrame& buffer)
{
if (!inCodeParse)
{
QtConcurrent::run([=]() {
inCodeParse = true;
QVideoFrame frame(buffer);
frame.map(QAbstractVideoBuffer::ReadOnly);
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat());
QImage img(frame.bits(), frame.width(), frame.height(), imageFormat);
img = img.mirrored();
frame.unmap();
cv::Mat mat = qImg2Mat(img);
cv::QRCodeDetector detector;
std::string outStr = detector.detectAndDecode(mat, cv::noArray(), cv::noArray());
if (0 != outStr.length())
{
std::cout << "code is " << outStr << std::endl;
}
inCodeParse = false;
});
}
}
注意img = img.mirrored();
这里是由于某些windows
机器摄像机型号设置的问题需要进行镜像反转,否则直接使用qVideoFrame2Mat
转换为cv::Mat
即可,不需要从QImage
走一层
此时编译是没有问题了,但是由于我们使用的是动态库,所以需要将这些动态库放在windows
系统库文件夹内,或者置于应用程序运行文件夹内以供调用,如果小伙伴们是自行编译的静态库,使用静态链接后则不需要再配置动态库了
这样我们运行程序将二维码置于摄像头前,即可完成二维码的检测以及解析