当前位置: 首页 > news >正文

QT中使用ffmpeg的api进行视频的播放

在了解ffmpeg使用api进行视频的播放之前,我们首先了解一下视频的播放流程。

一、视频的播放流程

首先是我们最常见的视频文件,在播放流程中首先是要打开视频文件,将视频文件中的数据进行解封装,之后再将解封装之后的视频进行解码。解码之后的视频便是视频帧的数据,之后将视频帧数据一帧一帧的显示在显示器上。

在这里插入图片描述

在使用api进行视频播放的时候也是通过这个流程。接下来我们看具体的实现。

二、ffmpeg中的数据结构体

在了解使用api之前,还需要先了解一下ffmpeg中的相关结构体,在了解了这些结构体之后,可以更容易的理解代码。

AVFormatContext:此结构体存储音视频封装格式中包含的信息,并且这个结构体是贯穿整个播放流程的。在这个结构体中主要包含AVInputFormat,AVOutputFormat、AVStream等。

struct AVInputFormat *iformat; // 输入数据的封装格式
AVIOContext *pb; // 输入数据的缓存
unsigned int nb_streams; // 音视频流的个数
AVStream **streams; // 音视频流
char filename[1024]; // 文件名
int64_t duration; // 时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate; // 比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata; // 元数据

**AVCodecContext:**是一个描述编解码器上下文的结构体,包含了众多编解码器需要的参数信息。

enum AVMediaType codec_type; // 编解码器的类型(视频,音频...)
struct AVCodec  *codec; // 采用的解码器AVCodec(H.264,MPEG2...)
int bit_rate; // 平均比特率
uint8_t *extradata; int extradata_size; // 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
AVRational time_base; // 根据该参数,可以把PTS转化为实际的时间(单位为秒s)
int width, height; // 如果是视频的话,代表宽和高
int refs; // 运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)
int sample_rate; // 采样率(音频)
int channels; // 声道数(音频)
enum AVSampleFormat sample_fmt; // 采样格式
int profile; // 型(H.264里面就有,其他编码标准应该也有)
int level; // 级(和profile差不太多)

AVCodec:是存储编码器信息的结构体。

const char *name; // 编解码器的名字的简称
const char *long_name; // 编解码器名字的全称
enum AVMediaType type; // 指明了类型,是视频,音频,还是字幕
enum AVCodecID id; // ID,不重复
const AVRational *supported_framerates; // 支持的帧率(仅视频)
const enum AVPixelFormat *pix_fmts; // 支持的像素格式(仅视频),如RGB24、YUV420P等。
const int *supported_samplerates; // 支持的采样率(仅音频)
const enum AVSampleFormat *sample_fmts; // 支持的采样格式(仅音频)
const uint64_t *channel_layouts; // 支持的声道数(仅音频)
int priv_data_size; // 私有数据的大小

AVFrame:该结构描述解码的(原始的)音频或视频数据。AVFrame必须使用av_frame_alloc()进行分配。请注意,这只是分配AVFrame本身,必须管理数据的缓冲区通过其他方式。AVFrame必须使用av_frame_free()释放。


AVPacket:是存储压缩编码数据相关信息的结构体。

uint8_t *data; // 压缩编码的数据。
/* 例如对于H.264来说。1个AVPacket的data通常对应一个NAL。注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流。因此在使用FFMPEG进行音视频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到音视频的码流文件。*/
int   size; // data的大小
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int   stream_index; // 标识该AVPacket所属的视频/音频流。

三、ffmpeg函数介绍

void avdevice_register_all(void);
初始化libavdevice并且注册所有的输入和输出设备。
AVFormatContext *avformat_alloc_context(void);
分配AVFormatContext。此函术分配的AVFormatContext结构体需要avformat_free_context()来释放上下文以及框架在其中分配的所有内容。
返回值:分配的AVFormatContext结构体。
int avformat_open_input (AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);
功能:打开输入流并读取标题,并将视频信息写入到AVFormatContext中。
打开输入流并读取标题。编解码器如果未打开。流必须使用avformat_close_input()关闭。
参数:ps:指向用户提供的AVFormatContext(由avformat_alloc_context分配)的指针。可能是指向NULL的指针,在这种情况下,AVFormatContext由该函数分配并写入ps。请注意,用户提供的AVFormatContext将在失败时释放。url:要打开的流的URL。fmt:如果非NULL,此参数将强制使用特定的输入格式。否则将自动检测格式。options:一个充满AVFormatContext和解复用器私有选项的字典。返回时,此参数将被销毁,并替换为包含未找到的选项的dict。可能为NULL。
返回值:成功时为0,失败时为负AVERROR。
into avformat_find_stream_info (AVFormatContext *ic, AVDictionary **options);
功能:读取媒体文件的数据包以获取流信息。
参数:ic:媒体文件上下文options:如果非NULL,则ic.nb_streams指向字典的指针长数组,其中第i个成员包含与第i个流对应的编解码器的选项。返回时,每个字典都将填充未找到的选项。
返回值:如果返回值大于等于0则说明成功,返回其他我失败。
AVCodec* avcodec_find_decoder (enum AVCodecID id);
功能:根据提供的AVCodecID寻找一个已经注册的解码器;
参数:所请求解码器的AVCodecID;
返回值:如果找到返回一个AVCodec,失败则返回nullptr;
int avcodec_open2 (AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
功能:初始化AVCodecContext以使用给定的AVCodec。在使用此函数之前,必须使用avcodec_alloc_text3()分配上下文。
参数:avctx:要初始化的上下文;codec:要为其打开此上下文的编解码器。如果之前已将非NULL编解码器传递给avcodec_alloc_text3()或此上下文,则此参数必须为NULL或等于之前传递的编解码器;options:一个充满AVCodecContext和编解码器专用选项的字典。返回时,此对象将填充未找到的选项。可以为nullptr;
返回值:成功时为零,错误时为负值;
av_frame_alloc:分配AVFrame并将其字段设置为默认值。主要该函数只分配AVFrame的空间,它的data字段的指定的buffer需要其它函数分配。返回为一个AVFream对象。
int av_read_frame (AVFormatContext *s, AVPacket *pkt);
功能:返回流的下一帧。此函数返回文件中存储的内容,并且不验证解码器是否有有效的帧。它会将存储在文件中的内容拆分为多个帧,并为每个调用返回一个帧。它不会省略有效帧之间的无效数据,从而给解码器提供解码所可能的最大信息。
成功后,返回的数据包被引用计数(pkt->buf被设置),并且无限期有效。当不再需要数据包时,必须使用av_packet_unref()释放该数据包。对于视频,数据包只包含一帧。
参数:s:媒体上下文结构体;pkt:返回的数据包
返回值:0(如果正常),<0(如果出现错误或文件结束)。出现错误时,pkt将为空(好像它来自av_packet_alloc())。
int avcodec_send_packet (AVCodecContext *avctx, const AVPacket *avpkt);
功能:将原始数据包数据作为输入提供给解码器。
参数:avctx:编解码器上下文avpkt:输入的AVPacket。通常,这将是一个单独的视频帧,或几个完整的音频帧。数据包的所有权仍然属于调用者,解码器不会写入数据包。解码器可以创建对分组数据的引用(或者如果分组没有被引用计数则复制它);
返回值:成功时为0。否则为负错误代码:AVERROR(EAGAIN):在当前状态下不接受输入-用户必须使用avcodec_receive_frame()	  读取输出(一旦读取了所有输出,则应重新发送数据包,并且使用EAGAIN调用不会失败)。AVERROR_EOF:解码器已被刷新,无法向其发送新的数据包(如果发送了1个以上的刷新数据包,也会返回)			AVERROR(EINVAL):编解码器未打开,它是编码器,或需要刷新AVERROR(ENOMEM):无法将数据包添加到内部队列,或类似的其他错误:合法解码错误
int avcodec_receive_frame (AVCodecContext *avctx, AVFrame *frame);
功能:返回解码器的解码输出数据。
参数:avctx:编解码器上下文frame:这将被设置为由解码器分配的参考计数的视频或音频帧(取决于解码器类型)。请注意,在执行其他操作之前,函数将始终调用av_frame_unref(frame)。这是输出。
返回值:0:成功,返回了一个帧AVERROR(EAGAIN):在这种状态下输出不可用-用户必须尝试发送新的输入       AVERROR_EOF:解码器已完全刷新,将不再有输出帧AVERROR(EINVAL):编解码器未打开,或者是编码器AVERROR_input_CHANGED:当前解码的帧相对于第一个解码的帧更改了参数。设置标志AV_CODEC_flag_DROCHANGED时适用。其他负值:合法解码错误
struct SwsContext* sws_getContext (int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param);
功能:分配并返回SwsContext;
参数:srcW 源图像的宽度;srcH 源图像的高度;srcFormat 源图像格式;dstW 目标图像的宽度;dstH 目标图像的高度;dstFormat 目标图像格式;flags 指定用于重新缩放的算法和选项;srcFilter 可以是nullptr;dstFilter 可以是nullptr;param 用于调整所用缩放器的额外参数对于SWS_BICUBIC param[0]和[1]调整基函数的形状,param[0]调整f(1)和param[1]f´(1)对于SWS_GAUSS param[0]调整指数,因此截止频率对于SWS_LANZOS param[0]调整窗口函数的宽度;
返回值:指向已分配上下文的指针,或者出现错误时为NULL;
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
功能:返回存储具有给定参数的图像所需的数据量的大小(以字节为单位)。
参数:pix_fmt 图像的像素格式;width 以像素为单位的图像宽度;height 以像素为单位的图像高度;align 假定的行大小对齐;
返回值:返回以字节为单位的缓冲区大小,失败时为负错误代码;
void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
功能:分配一个对齐方式适合所有内存访问的内存块(包括CPU上可用的矢量)。
参数:size 要分配的内存块的大小(以字节为单位);
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],const uint8_t *src,enum AVPixelFormat pix_fmt, int width, int height, int align);
功能:根据指定的图像参数和提供的数组设置数据指针和行大小。
参数:st_data 要填写的数据指针;dst_linesize 对要填充的dst_data中的图像进行行化;src 缓冲区,它将包含或包含实际的图像数据,可以为NULL;pix_fmt 图像的像素格式;width 以像素为单位的图像宽度;height 以像素为单位的图像高度;align src中用于行大小对齐的值;
返回值:返回src所需的字节大小,为负错误代码
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],const int srcStride[], int srcSliceY, int srcSliceH,uint8_t *const dst[], const int dstStride[]);
功能:在srcSlice中缩放图像切片,并将生成的缩放切片放在dst中的图像中。切片是图像中连续行的序列。
参数:c 以前使用创建的缩放上下文sws_getContext()srcSlice 包含指向源切片srcStride 数组,该数组包含源图像srcSliceY 切片在源图像中的位置过程,即数字(从零)在切片的第一行的图像中rcSliceH 源切片的高度,即数字切片中的行数dst 包含指向目的地图像dst 遍历包含目的地图像
返回值:输出切片的高度

四、ffmpeg api播放视频的函数调用流程

在这里插入图片描述

五、代码

打开文件代码

#ifndef FFMPEGAPIOPENDEVICE_H
#define FFMPEGAPIOPENDEVICE_H#include <QObject>
#include <QDebug>
#include <QTime>
extern "C"{
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
}class ffmpegApiOpenDevice : public QObject
{Q_OBJECT
public:explicit ffmpegApiOpenDevice(QObject *parent = nullptr);~ffmpegApiOpenDevice();void initFfmpeg(QString filePath = "");
private:int openVideoDevice(AVFormatContext *pIFormatCtx,QString filePath);void openStream(AVFormatContext *pIFormatCtx,int videoindex);
private:AVFormatContext *m_pIfmtCtx = nullptr;    //AVFormatContext是一个贯穿ffmpeg整个流程的结构体,其中包含了其他的几个结构体int m_videoStreamindex = -1;                //流indexAVCodecContext *m_pICodecCtx = nullptr;   //编码上下文结构体AVCodec *m_pICodec = nullptr;             //编码AVFrame *m_pIFrame = nullptr;             //AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM)AVPacket *m_pIPacket = nullptr;           //AVPacket是FFmpeg中很重要的一个数据结构,bool isOpenFile = false;
signals:void sendFrameSignal(AVCodecContext *pICodecCtx,AVFrame *pIFrame);
public slots:void displayVideo();
};#endif // FFMPEGAPIOPENDEVICE_H
#include "ffmpegapiopendevice.h"
#include "video/ffmpegapisavevideo.h"
ffmpegApiOpenDevice::ffmpegApiOpenDevice(QObject *parent) : QObject(parent)
{avdevice_register_all();
}ffmpegApiOpenDevice::~ffmpegApiOpenDevice()
{avcodec_close(m_pICodecCtx);av_frame_free(&m_pIFrame);av_packet_free(&m_pIPacket);avformat_close_input(&m_pIfmtCtx);
}void ffmpegApiOpenDevice::initFfmpeg(QString filePath)
{//创建一个AVFormatContext结构体,它是一个贯穿ffmpeg整个流程的结构体,其中包含了其他的几个结构体m_pIfmtCtx = avformat_alloc_context();//打开设备m_videoStreamindex = openVideoDevice(m_pIfmtCtx,filePath);//打开流openStream(m_pIfmtCtx,m_videoStreamindex);//至此,流的通路已经打通//创建AVPacketint y_size = m_pICodecCtx->width * m_pICodecCtx->height;m_pIPacket = static_cast<AVPacket *>(av_malloc(sizeof(AVPacket))); //分配一个packetav_new_packet(m_pIPacket, y_size); //分配packet的数据
}void ffmpegApiOpenDevice::displayVideo()
{while(1){if(m_pIPacket == nullptr){continue;}//获取像素帧到frame中m_pIFrame = av_frame_alloc();//将读取的帧数据存储到m_pIPacket中if (av_read_frame(m_pIfmtCtx, m_pIPacket) < 0)   //从设备中读取数据写入到AVPacket{break; //这里认为视频读取完了}if (m_pIPacket->stream_index == m_videoStreamindex) {    //判断流是不是我们需要的流int ret;ret = avcodec_send_packet(m_pICodecCtx, m_pIPacket);av_packet_unref(m_pIPacket);if(ret!=0){return;}ret = avcodec_receive_frame(m_pICodecCtx, m_pIFrame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)continue;if(ret!=0){qDebug()<<"avcodec_receive_frame failed !";return;}emit sendFrameSignal(m_pICodecCtx,m_pIFrame);}if(isOpenFile){//这里是为播放视频做的延时,延时31ms差不多就是25帧QThread::msleep(25);}}}int ffmpegApiOpenDevice::openVideoDevice(AVFormatContext *pIFormatCtx,QString filePath)
{//使用libavdevice读取数据,和直接打开视频文件比较类似,使用libavdevice的时候,唯一的不同在于需要首先查找用于输入的设备AVInputFormat *ifmt;int videoindex = -1;//码流的索引//2、根据输入格式的短名称查找AVInputFormat。ifmt = av_find_input_format("vfwcap");//3、根据上一个函数获取到的输入格式,打开摄像机设备。并将摄像机的相关信息写入到pIFormatCtx中。int ret = 0;if(filePath.isEmpty()){isOpenFile = false;ret = avformat_open_input(&pIFormatCtx,"0",ifmt,nullptr);}else{isOpenFile = true;ret = avformat_open_input(&pIFormatCtx,filePath.toUtf8(),nullptr,nullptr);}if(ret != 0){qDebug() << "Couldn't open input stream.\n";return -1;}//4、根据avformat_open_input打开设备的信息寻找pIFormatCtx中是否有数据流。if(avformat_find_stream_info(pIFormatCtx,nullptr) < 0){qDebug() << "Couldn't find stream information.\n";return -1;}else{qDebug() << "Success find stream information!\n";}//5、在pIFormatCtx中循环查找数据包包含的流信息,直到找到视频类型的流,便将流ID记录 videoindex中for(int i = 0; i < static_cast<int>(pIFormatCtx->nb_streams); i++){if(static_cast<int>(pIFormatCtx->streams[i]->codecpar->codec_type) == AVMEDIA_TYPE_VIDEO){videoindex=i;break;}}if(videoindex==-1){qDebug() << "Couldn't find a video stream.\n";}else{qDebug() << "Success find a video stream!\n";}return videoindex;
}void ffmpegApiOpenDevice::openStream(AVFormatContext *pIFormatCtx,int videoindex)
{//获取流中的编码上下文m_pICodecCtx = pIFormatCtx->streams[videoindex]->codec;//根据六种的编码上下文获取编码器IDm_pICodec = avcodec_find_decoder(m_pICodecCtx->codec_id);
//    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);//软编码
//    AVCodec * codec = avcodec_find_encoder_by_name("nvenc_h264");//硬编码if(m_pICodec == nullptr){qDebug() << ("Codec not found.\n");}else{qDebug() << "Codec found Successfuly!\n";}//8、打开解码器if(avcodec_open2(m_pICodecCtx, m_pICodec,nullptr)<0){qDebug() << ("Could not open codec.\n");}else{qDebug() << "Success open codec!\n";}
}

显示代码

#ifndef FFMPEGAPIDISPLAY_H
#define FFMPEGAPIDISPLAY_H#include <QObject>
#include <QDebug>
#include <QThread>
#include <QVector>
#include <QImage>
extern "C"{
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
}#define MaxFrameNum 10
class ffmpegApiDisplay : public QObject
{Q_OBJECTpublic:explicit ffmpegApiDisplay(QObject *parent = nullptr);void initDisplay(AVCodecContext *pCodecCtx);void insertFrame(AVFrame *frame);void stopDisplay();
private:SwsContext* img_convert_ctx;AVFrame* m_pIFrameRGB = nullptr;uint8_t *pIBuffer;  //开辟存储像素点的存储地址AVCodecContext *m_pCodecCtx;QVector<AVFrame *> m_frameVector;QImage m_image;bool state = false;bool photograph = false;signals:void sendImageSignal(QImage img);
public slots:void display();
};#endif // FFMPEGAPIDISPLAY_H
#include "ffmpegapidisplay.h"ffmpegApiDisplay::ffmpegApiDisplay(QObject *parent) : QObject(parent)
{}void ffmpegApiDisplay::initDisplay(AVCodecContext *pCodecCtx)
{m_pCodecCtx = pCodecCtx;img_convert_ctx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height,m_pCodecCtx->pix_fmt, m_pCodecCtx->width, m_pCodecCtx->height,AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);int pixSize = av_image_get_buffer_size(AV_PIX_FMT_RGB32, m_pCodecCtx->width,  m_pCodecCtx->height,16);//创建保存空间,底层使用malloc进行内存空间的开辟。pIBuffer = static_cast<uint8_t *>(av_malloc(static_cast<size_t>(pixSize)));//创建图像转换之后的帧m_pIFrameRGB = av_frame_alloc();av_image_fill_arrays(m_pIFrameRGB->data,m_pIFrameRGB->linesize,pIBuffer,AV_PIX_FMT_RGB32,m_pCodecCtx->width,m_pCodecCtx->height,16);state = true;
}void ffmpegApiDisplay::insertFrame(AVFrame *frame)
{if(m_frameVector.length()>MaxFrameNum){m_frameVector.pop_front();}m_frameVector.append(frame);
}void ffmpegApiDisplay::stopDisplay()
{state = false;
}void ffmpegApiDisplay::display()
{while(state){if(m_frameVector.isEmpty()){continue;}AVFrame *pIFrame = m_frameVector.front();int length = m_frameVector.length();m_frameVector.pop_front();if(pIFrame == nullptr){continue;}static int i=0;i++;qDebug()<<"ffmpegApiDisplay::display() 输出frame :"<<i;sws_scale(img_convert_ctx,static_cast<uint8_t const * const *>(pIFrame->data),pIFrame->linesize, 0, m_pCodecCtx->height, m_pIFrameRGB->data,m_pIFrameRGB->linesize);QImage tmpImg(static_cast<uchar *>(pIBuffer),m_pCodecCtx->width,m_pCodecCtx->height,QImage::Format_RGB32);QImage image = tmpImg.copy();//把图像复制一份 传递给界面显示if(photograph){//此部分和拍照功能相关m_image = tmpImg.copy();photograph = false;}emit sendImageSignal(image);  //发送信号}sws_freeContext(img_convert_ctx);av_frame_free(&m_pIFrameRGB);
}

完整代码路径:https://download.csdn.net/download/qq_43812868/88157743?spm=1001.2014.3001.5503

相关文章:

QT中使用ffmpeg的api进行视频的播放

在了解ffmpeg使用api进行视频的播放之前&#xff0c;我们首先了解一下视频的播放流程。 一、视频的播放流程 首先是我们最常见的视频文件&#xff0c;在播放流程中首先是要打开视频文件&#xff0c;将视频文件中的数据进行解封装&#xff0c;之后再将解封装之后的视频进行解码…...

使用idea实现git操作大全(在项目开发中遇到的实际情况

使用idea实现git操作大全&#xff08;在项目开发中遇到的实际情况&#xff09; 1.安装git插件2.在开发中切记拉一个自己的分支 1.安装git插件 2.在开发中切记拉一个自己的分支 选中需要拉的分支&#xff0c;右键该分支&#xff0c;选中new breach from “分支”&#xff0c;点…...

SQL面试题:一个优化案例

问题描述 假如存在以下两个表&#xff1a; CREATE TABLE customer ( C_CUSTKEY int NOT NULL, C_NAME varchar(25) NOT NULL, C_ADDRESS varchar(40) NOT NULL, C_NATIONKEY int NOT NULL, C_PHONE char(15) NOT NULL, C_ACCTBAL decimal(15,2) NOT NULL, C_MKTSEGMENT char(…...

链表的总体涵盖以及无哨兵位单链表实现——【数据结构】

&#x1f60a;W…Y&#xff1a;个人主页 在学习之前看一下美丽的夕阳&#xff0c;也是很不错的。 如果觉得博主的美景不错&#xff0c;博客也不错的话&#xff0c;关注一下博主吧&#x1f495; 在上一期中&#xff0c;我们说完了顺序表&#xff0c;并且提出顺序表中的问题 1. 中…...

网页版Java五子棋项目(一)websocket【服务器给用户端发信息】

网页版Java五子棋项目&#xff08;一&#xff09;websocket【服务器给用户端发信息】 一、为什么要用websocket二、websocket介绍原理解析 三、代码演示1. 创建后端api&#xff08;TestAPI&#xff09;新增知识点&#xff1a;extends TextWebSocketHandler重写各种方法 2. 建立…...

企业大数据可视化案例专题分享-入门

一、什么是数据可视化&#xff1f; 基本概念&#xff1a;数据可视化是以图示或图形格式表示的数据。让决策者可以看到以直观方式呈现的分析&#xff0c;以便他们可以掌握困难的概念或识别新的模式。借助交互式可视化&#xff0c;可以使用技术深入挖掘图表和图形以获取更多详细…...

GoogLeNet卷积神经网络-笔记

GoogLeNet卷积神经网络-笔记 GoogLeNet是2014年ImageNet比赛的冠军&#xff0c; 它的主要特点是网络不仅有深度&#xff0c; 还在横向上具有“宽度”。 由于图像信息在空间尺寸上的巨大差异&#xff0c; 如何选择合适的卷积核来提取特征就显得比较困难了。 空间分布范围更广的…...

腾讯云TencentOS Server镜像系统常见问题解答

腾讯云TencentOS Server镜像是腾讯云推出的Linux操作系统&#xff0c;完全兼容CentOS生态和操作方式&#xff0c;TencentOS Server操作系统为云上运行的应用程序提供稳定、安全和高性能的执行环境&#xff0c;TencentOS可以运行在腾讯云CVM全规格实例上&#xff0c;包括黑石物理…...

【项目 进程13】2.28共享内存(1) 2.29共享内存(2)

文章目录 2.28共享内存&#xff08;1&#xff09;共享内存&#xff08;效率最高&#xff0c;比内存映射更高。因为内存映射还需一个文件做载体&#xff09;共享内存使用步骤共享内存操作函数头文件 2.29共享内存&#xff08;2&#xff09;共享内存相关问题共享内存和内存映射的…...

Flask框架-流量控制:flask-limiter的使用

一、flask使用flask-limiter存在版本问题 Flask1.1.4 Flask-Bootstrap3.3.7.1 Flask-Caching1.9.0 Flask-Cors3.0.10 Flask-Limiter1.4 Flask-Migrate2.5.3 Flask-RESTful0.3.8 Flask-Script2.0.6 Flask-SocketIO5.0.1 Flask-Sockets0.2.1 Flask-SQLAlchemy2.4.4 Jinjia22.11.…...

【机器学习】西瓜书习题3.5Python编程实现线性判别分析,并给出西瓜数据集 3.0α上的结果

参考代码 结合自己的理解&#xff0c;添加注释。 代码 导入相关的库 import numpy as np import pandas as pd import matplotlib from matplotlib import pyplot as plt导入数据&#xff0c;进行数据处理和特征工程 得到数据集 D { ( x i , y i ) } i 1 m , y i ∈ { 0 ,…...

Elasticsearch:通过动态修剪实现更快的基数聚合

作者&#xff1a;Adrien Grand Elasticsearch 8.9 通过支持动态修剪&#xff08;dynamic pruning&#xff09;引入了基数聚合加速。 这种优化需要满足特定的条件才能生效&#xff0c;但一旦实现&#xff0c;通常会产生惊人的结果。 我们观察到&#xff0c;通过此更改&#xff0…...

Webpack5 生产模式压缩图片ImageMinimizerPlugin

文章目录 一、 ImageMinimizerPlugin是什么&#xff1f;二、已经有了asset&#xff0c;为什么需要ImageMinimizerPlugin&#xff1f;三、怎么使用ImageMinimizerPlugin&#xff1f;四、ImageMinimizerPlugin压缩的成果 一、 ImageMinimizerPlugin是什么&#xff1f; 它的实际依…...

时序预测 | Matlab实现基于BP神经网络的电力负荷预测模型

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 时序预测 | Matlab实现基于BP神经网络的电力负荷预测模型 BP神经网络是一种多层的前馈神经网络,其主要的特点是:信号是前向传播的,而误差是反向传播的。B...

基于回溯算法实现八皇后问题

八皇后问题是一个经典的计算机科学问题&#xff0c;它的目标是将8个皇后放置在一个大小为88的棋盘上&#xff0c;使得每个皇后都不会攻击到其他的皇后。皇后可以攻击同一行、同一列和同一对角线上的棋子。 一、八皇后问题介绍 八皇后问题最早由国际西洋棋大师马克斯贝瑟尔在18…...

Linux【网络编程】之深入理解TCP协议

Linux【网络编程】之深入理解TCP协议 TCP协议TCP协议段格式4位首部长度---TCP报头长度信息 TCP可靠性&#xff08;确认应答&#xff09;&& 提高传输效率确认应答(ACK)机制32位序号与32为确认序号 16位窗口大小---自己接收缓冲区剩余空间的大小16位紧急指针---紧急数据处…...

如何克服看到别人优于自己而感到的焦虑和迷茫?

文章目录 每日一句正能量前言简述自己的感受怎么做如何调整自己的心态后记 每日一句正能量 行动是至于恐惧的良药&#xff0c;而犹豫、拖延&#xff0c;将不断滋养恐惧。 前言 虽然清楚知识需要靠时间沉淀&#xff0c;但在看到自己做不出来的题别人会做&#xff0c;自己写不出的…...

浅谈React中的ref和useRef

目录 什么是useRef&#xff1f; 使用 ref 访问 DOM 元素 Ref和useRef之间的区别 Ref和useRef的使用案例 善用工具 结论 在各种 JavaScript 库和框架中&#xff0c;React 因其开发人员友好性和支持性而得到认可。 大多数开发人员发现 React 非常舒适且可扩展&#xff0c;…...

Linux C 获取主机网卡名及 IP 的几种方法

在进行 Linux 网络编程时&#xff0c;经常会需要获取本机 IP 地址&#xff0c;除了常规的读取配置文件外&#xff0c;本文罗列几种个人所知的编程常用方法&#xff0c;仅供参考&#xff0c;如有错误请指出。 方法一&#xff1a;使用 ioctl() 获取本地 IP 地址 Linux 下可以使用…...

解密外接显卡:笔记本能否接外置显卡?如何连接外接显卡?

伴随着电脑游戏和图形处理的需求不断增加&#xff0c;很多笔记本电脑使用者开始考虑是否能够通过外接显卡来提升性能。然而&#xff0c;外接显卡对于笔记本电脑是否可行&#xff0c;以及如何连接外接显卡&#xff0c;对于很多人来说仍然是一个迷。本文将为您揭秘外接显卡的奥秘…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

2.2.2 ASPICE的需求分析

ASPICE的需求分析是汽车软件开发过程中至关重要的一环&#xff0c;它涉及到对需求进行详细分析、验证和确认&#xff0c;以确保软件产品能够满足客户和用户的需求。在ASPICE中&#xff0c;需求分析的关键步骤包括&#xff1a; 需求细化&#xff1a;将从需求收集阶段获得的高层需…...

比特币:固若金汤的数字堡垒与它的四道防线

第一道防线&#xff1a;机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”&#xff08;Hashing&#xff09;就是一种军事级的加密术&#xff08;SHA-256&#xff09;&#xff0c;能将信函内容&#xff08;交易细节&#xf…...