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,问题得到解决。
文件目录如下:
完整代码如下:
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>
参考文献:
5.Qt使用QAbstractVideoSurface捕获视频帧(信号槽方式),并用QPainter画出来
本文地址:https://blog.csdn.net/qq_24815615/article/details/107302759