欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-22 08:33:57
折腾了几天,终于有结果了,Qt对android开发的支持还可以,就是Qt进行android开发没有anroid studio主流,用Qt进行android开发,基本都是在写C++ ,还蛮棒的。网上找了好一会才找到参考文献5,问题得到解决。文件目录如下:完整代码如下:camera_widgets_display.proQT += core gui multimedia multimediawidgetsgreaterThan(QT_MAJOR_VERSION, 4):...

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

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

文件目录如下:

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

完整代码如下:

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画出来

 

 

本文地址:https://blog.csdn.net/qq_24815615/article/details/107302759