Qt 实现android camera摄像头的preview和拍照

19 篇文章 1 订阅

折腾了几天,终于有结果了,Qt对android开发的支持还可以,就是Qt进行android开发没有anroid studio主流,用Qt进行android开发,基本都是在写C++ ,还蛮棒的。

网上找了好一会才找到参考文献5,问题得到解决。

文件目录如下:

完整代码如下:

camera_widgets_display.pro

QT       += core gui multimedia multimediawidgets

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    myvideosurface.cpp \
    widget.cpp

HEADERS += \
    myvideosurface.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

myvideosurface.h

#ifndef MYVIDEOSURFACE_H
#define MYVIDEOSURFACE_H

#include <QAbstractVideoSurface>
#include <QDebug>
#include <QVideoSurfaceFormat>
#include <QVideoFrame>

class MyVideoSurface : public QAbstractVideoSurface
{
    Q_OBJECT
public:
    MyVideoSurface();

    QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const Q_DECL_OVERRIDE;
    bool isFormatSupported(const QVideoSurfaceFormat &) const Q_DECL_OVERRIDE;    //将视频流中像素格式转换成格式对等的图片格式,若无对等的格式,返回QImage::Format_Invalid
    bool start(const QVideoSurfaceFormat &) Q_DECL_OVERRIDE;                       //只要摄像头开,就会调用
    bool present(const QVideoFrame &) Q_DECL_OVERRIDE;                             //每一帧画面将回到这里处理
    void stop() Q_DECL_OVERRIDE;

signals:
    void frameAvailable(QVideoFrame cloneFrame);
};

#endif // MYVIDEOSURFACE_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QDebug>
#include <QCamera>
#include <QCameraInfo>
#include <QCameraViewfinder>
#include <QCameraViewfinderSettings>
#include <QCameraImageCapture>
#include <myvideosurface.h>
#include <QImage>
#include <QPainter>
#include <QScreen>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

void NV21_T_RGB(unsigned int width , unsigned int height , unsigned char *yuyv , unsigned char *rgb);

private:
    Ui::Widget *ui;
    QCamera *camera;
    QCameraImageCapture *capture;
    QCameraViewfinder *viewfinder;
    MyVideoSurface *mySurface;
    QImage videoImg;

public slots:
    void displayImage(int ,QImage image);
    void rcvFrame(QVideoFrame);                            //接收图像帧数据
    void paintEvent(QPaintEvent *event);

private slots:
    void on_pushButton_clicked();
};
#endif // WIDGET_H

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

myvideosurface.cpp

#include "myvideosurface.h"

MyVideoSurface::MyVideoSurface()
{

}

//支持的像素格式
QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
    if(handleType == QAbstractVideoBuffer::NoHandle){
        return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32
                                                 << QVideoFrame::Format_ARGB32
                                                 << QVideoFrame::Format_ARGB32_Premultiplied
                                                 << QVideoFrame::Format_RGB565
                                                 << QVideoFrame::Format_NV21
                                                 << QVideoFrame::Format_RGB555;
    }
    else {
        return QList<QVideoFrame::PixelFormat>();
    }
}

//将视频流中像素格式转换成格式对等的图片格式,若无对等的格式,返回QImage::Format_Invalid
bool MyVideoSurface::isFormatSupported(const QVideoSurfaceFormat &videoformat) const
{
    //imageFormatFromPixelFormat()-----返回与视频帧像素格式等效的图像格式
    //pixelFormat()-----返回视频流中帧的像素格式
    return QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat()) != QImage::Format_Invalid;
}

//这些虚函数,会自动被调用,start检测图像是否可以对等转换,每一帧有没有
bool MyVideoSurface::start(const QVideoSurfaceFormat &videoformat)
{
//    qDebug() << QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat());              //格式是RGB32
//    if(QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat()) != QImage::Format_Invalid && !videoformat.frameSize().isEmpty()){
//        QAbstractVideoSurface::start(videoformat);
//        return true;
//    }
    QAbstractVideoSurface::start(videoformat);
    return false;
}

bool MyVideoSurface::present(const QVideoFrame &frame)
{
//    qDebug() << frame.size();
    if (frame.isValid()){
        QVideoFrame cloneFrame(frame);                                      //每一帧视频都会进入present中,内部机制
        emit frameAvailable(cloneFrame);                                    //直接把视频帧发送出去
        return true;
    }
    stop();
    return false;
}

void MyVideoSurface::stop()
{
    QAbstractVideoSurface::stop();
}


widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    camera=new QCamera;//摄像头
    capture = new QCameraImageCapture(camera);
    viewfinder=new QCameraViewfinder(this);//取景器

    QObject::connect(capture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(displayImage(int,QImage)));
    camera->setCaptureMode(QCamera::CaptureStillImage);
//    camera->setViewfinder(viewfinder);
    mySurface = new MyVideoSurface();
    camera->setViewfinder(mySurface);
    //处理myvideosurface中每一帧视频
    connect(mySurface, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(rcvFrame(QVideoFrame)), Qt::DirectConnection);

    camera->start(); //启动摄像头

    //获取摄像头支持的分辨率、帧率等参数
//    QList<QCameraViewfinderSettings > ViewSets = camera->supportedViewfinderSettings();
//    int i = 0;
//    qDebug() << "viewfinderResolutions sizes.len = " << ViewSets.length();
//    foreach (QCameraViewfinderSettings ViewSet, ViewSets) {
//        qDebug() << i++ <<" max rate = " << ViewSet.maximumFrameRate() << "min rate = "<< ViewSet.minimumFrameRate() << "resolution "<<ViewSet.resolution()<<\
//                    "Format="<<ViewSet.pixelFormat()<<""<<ViewSet.pixelAspectRatio();
//    }

    //设置摄像头参数
    QCameraViewfinderSettings camerasettings;
    camerasettings.setResolution(640,480);
    camerasettings.setPixelFormat(QVideoFrame::Format_NV21);
    camerasettings.setMaximumFrameRate(30);
    camerasettings.setMinimumFrameRate(30);

    camera->setViewfinderSettings(camerasettings);

}

Widget::~Widget()
{
    delete ui;
}


void Widget::displayImage(int ,QImage image)
{
    image=image.convertToFormat(QImage::Format_RGB888);
    ui->label->setPixmap(QPixmap::fromImage(image));
    qDebug() << image.size();
}

void Widget::rcvFrame(QVideoFrame m_currentFrame)
{
    qDebug() << "received" << m_currentFrame.size();

    m_currentFrame.map(QAbstractVideoBuffer::ReadOnly);
//    unsigned char *pix_ptr = m_currentFrame.bits();
//    qDebug("%d %d %d %d", *pix_ptr, *(pix_ptr+1), *(pix_ptr+2), *(pix_ptr+3));
        //将视频帧转化成QImage,devicePixelRatio设备像素比,bytesPerLine一行的像素字节(1280*4=5120)

    videoImg = QImage(m_currentFrame.width(), m_currentFrame.height(), QImage::Format_RGB888);
    NV21_T_RGB(m_currentFrame.width(), m_currentFrame.height(), m_currentFrame.bits(), videoImg.bits());
//    videoImg =  QImage(m_currentFrame.bits(),
//                   m_currentFrame.width(),
//                   m_currentFrame.height(),
//                   QImage::Format_Grayscale8).copy();       //这里要做一个copy,因为char* pdata在emit后释放了
//    videoImg = videoImg.mirrored(true, false);                          //水平翻转,原始图片是反的

//    qDebug() <<  "image" << videoImg;  //可以看看输出啥东西
//    QString currentTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
//    QString savefile = QString("E:/study/QT/opencamera/opencamera/%1.jpg").arg(currentTime);
//    qDebug() << videoImg.save(savefile);
    m_currentFrame.unmap();                                         //释放map拷贝的内存
    QCameraInfo cameraInfo(*camera); // needed to get the camera sensor position and orientation

//    // Get the current display orientation
//    const QScreen *screen = QGuiApplication::primaryScreen();
//    const int screenAngle = screen->angleBetween(screen->nativeOrientation(), screen->orientation());

//    int rotation;
//    if (cameraInfo.position() == QCamera::BackFace) {
//        rotation = (cameraInfo.orientation() - screenAngle) % 360;
//    } else {
//        // Front position, compensate the mirror
//        rotation = (360 - cameraInfo.orientation() + screenAngle) % 360;
//    }

//    // Rotate the frame so it always shows in the correct orientation
    videoImg = videoImg.transformed(QTransform().rotate(90));

    QWidget::update();                                              //更新了,就会触发paintEvent画图
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QRect rect(0, 0, 480, 640);

    //设置画笔
    QPen pen;
    pen.setWidth(5);                    //线宽
    pen.setColor(Qt::red);
    pen.setStyle(Qt::SolidLine);        //实线
    pen.setCapStyle(Qt::FlatCap);       //线端点样式
    pen.setJoinStyle(Qt::BevelJoin);    //线连接点样式
    painter.setPen(pen);
    painter.drawRect(rect);

    //qDebug() <<  "image" << videoImg;     //第一次输出的QImage(null)
    //只要窗口启动,就会触发paintEvent,所以第一次是null,不可以画图。
    if(videoImg != QImage(nullptr)){
        painter.drawImage(rect, videoImg);
    }
}


void Widget::on_pushButton_clicked()
{
    capture->capture();
}

void Widget::NV21_T_RGB(unsigned int width , unsigned int height , unsigned char *yuyv , unsigned char *rgb)
{
    const int nv_start = width * height ;
    unsigned int i, j, index = 0, rgb_index = 0;
    unsigned char y, u, v;
    int r, g, b, nv_index = 0;

    for(i = 0; i < height; i++){
        for(j = 0; j < width; j ++){
            //nv_index = (rgb_index / 2 - width / 2 * ((i + 1) / 2)) * 2;
            nv_index = i / 2  * width + j - j % 2;

            y = yuyv[rgb_index];
            u = yuyv[nv_start + nv_index ];
            v = yuyv[nv_start + nv_index + 1];

            r = y + (140 * (v-128))/100;  //r
            g = y - (34 * (u-128))/100 - (71 * (v-128))/100; //g
            b = y + (177 * (u-128))/100; //b

            if(r > 255)   r = 255;
            if(g > 255)   g = 255;
            if(b > 255)   b = 255;
            if(r < 0)     r = 0;
            if(g < 0)     g = 0;
            if(b < 0)     b = 0;

            index = rgb_index % width + (height - i - 1) * width;
            //rgb[index * 3+0] = b;
            //rgb[index * 3+1] = g;
            //rgb[index * 3+2] = r;

            //颠倒图像
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 1] = b;
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 2] = g;
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 3] = r;

            //正面图像
            rgb[i * width * 3 + 3 * j + 0] = b;
            rgb[i * width * 3 + 3 * j + 1] = g;
            rgb[i * width * 3 + 3 * j + 2] = r;

            rgb_index++;
        }
    }
//    return 0;
}

widget.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1080</width>
    <height>1920</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>640</y>
     <width>480</width>
     <height>640</height>
    </rect>
   </property>
   <property name="text">
    <string>Image</string>
   </property>
  </widget>
  <widget class="QPushButton" name="pushButton">
   <property name="geometry">
    <rect>
     <x>40</x>
     <y>1520</y>
     <width>281</width>
     <height>291</height>
    </rect>
   </property>
   <property name="text">
    <string>PushButton</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

 

参考文献:

1.NV21与I420

2.C++ NV21转RGB

3.Camera Overview

4.QAbstractVideoSurface Class

5.Qt使用QAbstractVideoSurface捕获视频帧(信号槽方式),并用QPainter画出来

 

 

### 回答1: Qt for Android可以通过Qt Multimedia模块来调用安卓手机摄像头实现拍照和录像功能。 首先,需要在.pro文件中添加multimedia模块的引用,即将`QT += multimedia`添加到.pro文件中。 接下来,创建一个QCamera对象,可以使用默认摄像头或指定摄像头。通过调用`QCameraInfo::defaultCamera()`可以获取默认摄像头的信息,或者使用`QCameraInfo::availableCameras()`获取所有可用摄像头列表。然后,调用`setCaptureMode()`方法来设置摄像头的捕获模式,可以选择使用`QCamera::CaptureStillImage`来拍照,或使用`QCamera::CaptureVideo`来录制视频。 如果需要显示摄像头的即时预览画面,可以创建一个QCameraViewfinder对象,并将其设置为QCamera的视图finder,然后将QCameraViewfinder设置为显示在窗口上。可以使用QGraphicsView或QWidget来显示摄像头的预览画面。 在拍照时,可以使用QCameraCaptureSession或直接使用QCamera的capture()方法来捕获静态图像。捕获的图像可以使用QCameraImageCapture类获取,并保存到本地文件中。 在录制视频时,可以使用QMediaRecorder来进行视频录制,首先创建一个QMediaRecorder对象,并使用setMedia()方法设置录制的媒体文件名和格式。然后,设置视频编码器、分辨率、比特率等参数,并调用record()方法开始录制,调用stop()方法停止录制。 最后,记得在AndroidManifest.xml文件中添加相应的权限,例如访问相机、录音和存储等权限。 通过以上步骤,就可以在Qt for Android中成功调用手机摄像头实现拍照和录像功能了。 ### 回答2: 在Qt for Android开发中,要调用手机摄像头,可以使用Qt Multimedia模块中的QCamera类。 首先,需要在.pro文件中添加对Multimedia模块的依赖: ``` QT += multimedia ``` 然后,在代码中引入QCamera和QCameraViewfinder类: ``` #include <QCamera> #include <QCameraViewfinder> ``` 接下来,创建一个QCamera对象并设置使用后置摄像头: ``` QCamera* camera = new QCamera; camera->setCaptureMode(QCamera::CaptureStillImage); // 设置为拍照模式 camera->setCaptureMode(QCamera::CaptureVideo); // 设置为录像模式 QCameraViewfinder* viewfinder = new QCameraViewfinder; camera->setViewfinder(viewfinder); QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); foreach (const QCameraInfo& cameraInfo, cameras) { if (cameraInfo.position() == QCamera::BackFace) { // 后置摄像头 camera->setCamera(cameraInfo); // 设置为后置摄像头 break; } } ``` 然后,可以在需要调用摄像头的地方,调用QCamera的相关方法,比如开始预览、拍照或录像: ``` camera->start(); camera->searchAndLock(); camera->unlock(); camera->searchAndCapture(); ``` 最后,需要在界面上显示摄像头预览画面,可以将QCameraViewfinder设置为QWidget的子控件,并将其显示出来: ``` QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(viewfinder); setLayout(layout); ``` 以上就是利用Qt for Android调用手机摄像头的基本步骤,开发者可以根据实际需求,进一步对摄像头功能进行扩展和定制。 ### 回答3: 在Qt中调用Android手机的摄像头可以通过Qt Multimedia模块来实现。首先,确保已经正确配置了Qt for Android开发环境,并在.pro文件中添加了对Qt Multimedia模块的依赖,类似于:QT += multimedia。 接下来,创建一个Qt Quick界面来显示摄像头捕获的图像。可以使用Camera类型的对象来控制摄像头,并将摄像头的图像显示在Qt Quick界面上。 具体的步骤如下: 1. 在Qt Creator中创建一个新的Qt Quick项目。 2. 在qml文件中添加一个Item,用于显示摄像头图像,例如: Item { id: cameraView width: 640 height: 480 visible: camera.available anchors.centerIn: parent } 3. 在C++代码中创建一个Camera对象并连接到cameraView的source属性上: QCamera *camera = new QCamera(this); camera->setViewfinder(cameraView); camera->start(); 4. 在AndroidManifest.xml文件中添加相机权限,例如: <uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera.autofocus"/> <uses-feature android:name="android.hardware.camera.flash"/> 5. 在运行项目之前,将生成的apk安装到Android手机上,并确保手机上有可用的摄像头。 通过以上步骤,你的Qt for Android应用程序现在可以调用Android手机的摄像头并显示捕获的图像了。你还可以通过QCameraViewfinderSettings类来更改摄像头的设置,例如分辨率、帧率等。除了显示图像,你还可以使用QCamera类提供的其他函数来控制摄像头拍照、录像等功能。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值