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

Qt调用FFmpeg库实时播放UDP组播视频流

基于以下参考链接,通过改进实现实时播放UDP组播视频流

https://blog.csdn.net/u012532263/article/details/102736700

源码在windows(qt-opensource-windows-x86-5.12.9.exe)、ubuntu20.04.6(x64)(qt-opensource-linux-x64-5.12.12.run)、以及针对arm64的ubuntu20.04.6(x64)交叉编译环境下编译成功(QT5.12.8, 5.15.13), 可执行程序在windows,ubuntu(x64)、arm64上均可运行。

工程代码见:

https://download.csdn.net/download/daqinzl/90315016

主要代码
videoplayer.cpp

#include "videoplayer.h"
#include <QDebug>
extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
}

#include <stdio.h>
#include<iostream>
using namespace std;
VideoPlayer::VideoPlayer()
{

}

VideoPlayer::~VideoPlayer()
{

}

void VideoPlayer::startPlay()
{
    ///调用 QThread 的start函数 将会自动执行下面的run函数 run函数是一个新的线程
    this->start();

}

void VideoPlayer::run()
{
    /*
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame;
    AVFrame *pFrameRGB;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    avformat_network_init();
    av_register_all();


    //Allocate an AVFormatContext.
    pFormatCtx = avformat_alloc_context();

    // ffmpeg取rtsp流时av_read_frame阻塞的解决办法 设置参数优化
    AVDictionary* avdic = NULL;
    //rtsp
    //av_dict_set(&avdic, "buffer_size", "102400", 0); //设置缓存大小,1080p可将值调大
    //av_dict_set(&avdic, "rtsp_transport", "udp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp

    //rtmp
    //av_dict_set(&avdic, "buffer_size", "8192000", 0); //设置缓存大小,1080p可将值调大
    //av_dict_set(&avdic, "rtsp_transport", "tcp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp

    //udp
    av_dict_set(&avdic, "buffer_size", "8192000", 0); //设置缓存大小,1080p可将值调大
    av_dict_set(&avdic, "rtsp_transport", "udp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp
    //av_dict_set(&avdic, "fflags", "nobuffer", 0); // 设置实时选项
    //av_dict_set(&avdic, "flags", "low_delay", 0); // 设置低延迟选项
    av_dict_set(&avdic, "max_interleave_delta", "40000", 0);


    //av_dict_set(&avdic, "stimeout", "2000000", 0);   //设置超时断开连接时间,单位微秒
    //av_dict_set(&avdic, "max_delay", "500000", 0);   //设置最大时延

    ///rtsp地址,可根据实际情况修改
    //char url[]="rtsp://admin:admin@192.168.1.18:554/h264/ch1/main/av_stream";
    //char url[]="rtsp://192.168.17.112/test2.264";
    //char url[]="rtsp://admin:admin@192.168.43.1/stream/main";
    //char url[]="rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp";

    char url[]="udp://224.1.1.1:5001";

    //char url[]="rtmp://192.168.1.100:1935/live/desktop";

    if (avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) {
        qDebug("can't open the file. \n");
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        qDebug("Could't find stream infomation.\n");
        return;
    }

    videoStream = -1;

    ///循环查找视频中包含的流信息,直到找到视频类型的流
    ///便将其记录下来 保存到videoStream变量中
    ///这里我们现在只处理视频流  音频流先不管他
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    ///如果videoStream为-1 说明没有找到视频流
    if (videoStream == -1) {
        qDebug("Didn't find a video stream.\n");
        return;
    }

    ///查找解码器
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    ///2017.8.9---lizhen
    //pCodecCtx->bit_rate = 0;   //初始化为0
    //pCodecCtx->time_base.num = 1;  //下面两行:一秒钟25帧
    //pCodecCtx->time_base.den = 10;
    //pCodecCtx->frame_number = 1;  //每包一个视频帧

    if (pCodec == NULL) {
        qDebug("Codec not found.\n");
        return;
    }

    ///打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        qDebug("Could not open codec.\n");
        return;
    }

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();

    ///这里我们改成了 将解码后的YUV数据转换成RGB32
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
            AV_PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL);

    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);

    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
    av_new_packet(packet, y_size); //分配packet的数据
    //*/

    //bool CanRun = true;

    //ffmpeg 初始化
    // 初始化注册ffmpeg相关的编码器
    av_register_all();
    avcodec_register_all();
    avformat_network_init();

    //qDebug()<<"1 FFmpeg version info: {  av_version_info() }";
    qDebug()<<"2 FFmpeg version info: { " << av_version_info() << "}";
    qDebug()<<"3 FFmpeg version info:";
    qDebug()<< av_version_info() ;

    char url[]="udp://224.1.1.1:5001";

    // ffmpeg 转码
    // 分配音视频格式上下文
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    AVDictionary* avdic = NULL;
    av_dict_set(&avdic, "buffer_size", "8192000", 0);
    av_dict_set(&avdic, "max_interleave_delta", "40000", 0);

    av_dict_set(&avdic, "analyzeduration", "100000000", 0);
    av_dict_set(&avdic, "probesize", "100000000", 0);

    int ret;
    //打开流
    ret = avformat_open_input(&pFormatCtx, url, NULL, &avdic);
    if (ret != 0){ qDebug("can't open the url. \n"); return; }

    // 读取媒体流信息
    ret = avformat_find_stream_info(pFormatCtx, NULL);
    if (ret != 0) { qDebug("Could't find stream infomation.\n"); return; }

    // 这里只是为了打印些视频参数
    AVDictionaryEntry* tag = NULL;
    while ((tag = av_dict_get(pFormatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)) != NULL)
    {
        char * key = tag->key;
        char * value = tag->value;
        qDebug()<< *key  << *value << "\n";
    }

    // 从格式化上下文获取流索引
    AVStream* pStream = NULL;
    //AVStream* aStream = NULL;
    int videoStream = -1;
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            pStream = pFormatCtx->streams[i];
            videoStream = i;
        }
        //else if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        //{
        //    aStream = pFormatCtx->streams[i];
        //}
    }
    if (pStream == NULL) { qDebug("Didn't find a video stream.\n"); return; }

    // 获取流的编码器上下文
    AVCodecContext codecContext = *pStream->codec;

    qDebug() << "codec name: {" << avcodec_get_name(codecContext.codec_id) << "}\n";
    // 获取图像的宽、高及像素格式
    int width = codecContext.width;
    int height = codecContext.height;
    AVPixelFormat sourcePixFmt = codecContext.pix_fmt;

    // 得到编码器ID
    AVCodecID codecId = codecContext.codec_id;
    // 目标像素格式
    AVPixelFormat destinationPixFmt = AV_PIX_FMT_RGB32;

    // 某些264格式codecContext.pix_fmt获取到的格式是AV_PIX_FMT_NONE 统一都认为是YUV420P
    if (sourcePixFmt == AV_PIX_FMT_NONE && codecId == AV_CODEC_ID_MPEG2TS)
    {
        sourcePixFmt = AV_PIX_FMT_YUV420P;
    }

    static struct SwsContext *img_convert_ctx;

    // 得到SwsContext对象:用于图像的缩放和转换操作
    img_convert_ctx = sws_getContext(width, height, sourcePixFmt,
        width, height, destinationPixFmt,
        SWS_FAST_BILINEAR, NULL, NULL, NULL);
    if (img_convert_ctx == NULL) { qDebug() << "Could not initialize the conversion context.\n" ; return; }

    //分配一个默认的帧对象:AVFrame
    //AVFrame* pConvertedFrame = av_frame_alloc();
    // 目标媒体格式需要的字节长度
    //numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
    int numBytes = avpicture_get_size(destinationPixFmt, width, height);
    // 分配目标媒体格式内存使用
    //out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    uint8_t * out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    //var dstData = new byte_ptrArray4();
    //var dstLinesize = new int_array4();
    AVFrame *pFrameRGB = av_frame_alloc();
    // 设置图像填充参数
    //av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, convertedFrameBufferPtr, destinationPixFmt, width, height, 1);
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, destinationPixFmt,width, height);

    // ffmpeg 解码
    // 根据编码器ID获取对应的解码器
    AVCodec* pCodec = avcodec_find_decoder(codecId);
    if (pCodec == NULL) { qDebug() << "Unsupported codec.\n"; }

    AVCodecContext* pCodecCtx = &codecContext;

    if ((pCodec->capabilities & AV_CODEC_CAP_TRUNCATED) == AV_CODEC_CAP_TRUNCATED)
        pCodecCtx->flags |= AV_CODEC_FLAG_TRUNCATED;

    // 通过解码器打开解码器上下文:AVCodecContext pCodecContext
    ret = avcodec_open2(pCodecCtx, pCodec, NULL);
    if (ret < 0) { qDebug() << ret << "\n"; return; }

    // 分配解码帧对象:AVFrame pDecodedFrame
    AVFrame* pFrame = av_frame_alloc();

    // 初始化媒体数据包
    AVPacket* packet = new AVPacket();
    AVPacket** pPacket = &packet;
    av_init_packet(packet);

    while (1)
    {
        /*
        if (av_read_frame(pFormatCtx, packet) < 0)
        {
            qDebug() << "av_read_frame < 0";
            break; //这里认为视频读取完了
        }

        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);

            if (ret < 0) {
                qDebug("decode error.\n");
                return;
            }

            if (got_picture) {
                sws_scale(img_convert_ctx,
                        (uint8_t const * const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                        pFrameRGB->linesize);

                //把这个RGB数据 用QImage加载
                QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
                emit sig_GetOneFrame(image);  //发送信号
                //emit sig_GetOneFrame(tmpImg);


                //提取出图像中的R数据
                //for(int i=0;i<pCodecCtx->width;i++)
                //{
                //    for(int j=0;j<pCodecCtx->height;j++)
                //    {
                //        QRgb rgb=image.pixel(i,j);
                //        int r=qRed(rgb);
                //        image.setPixel(i,j,qRgb(r,0,0));
                //    }
                //}
                //emit sig_GetRFrame(image);


            }else qDebug() << "got_picture < 0";
        }else qDebug() << "packet->stream_index not video stream";
        av_free_packet(packet); //释放资源,否则内存会一直上升
        //msleep(0.02); //停一停  不然放的太快了
        //*/

        //*
        try
        {
            do
            {
                // 读取一帧未解码数据
                ret = av_read_frame(pFormatCtx, packet);
               // Console.WriteLine(pPacket->dts);
                if (ret == AVERROR_EOF) break;
                if (ret < 0) { qDebug() << "got error "; return; }

                if (packet->stream_index != videoStream) continue;

                // 解码
                ret = avcodec_send_packet(pCodecCtx, packet);
                if (ret < 0) { qDebug() << "got error 2 "; return; }
                // 解码输出解码数据
                ret = avcodec_receive_frame(pCodecCtx, pFrame);
            } while (ret == AVERROR(EAGAIN) && 1);
            if (ret == AVERROR_EOF) break;
            if (ret < 0) { qDebug() << "got error 3 "; return; }

            if (packet->stream_index != videoStream) continue;

            //Console.WriteLine($@"frame: {frameNumber}");
            // YUV->RGB
            sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

            //把这个RGB数据 用QImage加载
            QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
            QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
            emit sig_GetOneFrame(image);  //发送信号
            //emit sig_GetOneFrame(tmpImg);

        }  catch (exception e) {

        }
        {
            av_packet_unref(packet);//释放数据包对象引用
            av_frame_unref(pFrame);//释放解码帧对象引用
        }
        //*/
    }
    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

相关文章:

Qt调用FFmpeg库实时播放UDP组播视频流

基于以下参考链接&#xff0c;通过改进实现实时播放UDP组播视频流 https://blog.csdn.net/u012532263/article/details/102736700 源码在windows&#xff08;qt-opensource-windows-x86-5.12.9.exe&#xff09;、ubuntu20.04.6(x64)(qt-opensource-linux-x64-5.12.12.run)、以…...

C# 类与对象详解

.NET学习资料 .NET学习资料 .NET学习资料 在 C# 编程中&#xff0c;类与对象是面向对象编程的核心概念。它们让开发者能够将数据和操作数据的方法封装在一起&#xff0c;从而构建出模块化、可维护且易于扩展的程序。下面将详细介绍 C# 中类与对象的相关知识。 一、类的定义 …...

【Elasticsearch 基础入门】Centos7下Elasticsearch 7.x安装与配置(单机)

Elasticsearch系列文章目录 【Elasticsearch 基础入门】一文带你了解Elasticsearch&#xff01;&#xff01;&#xff01;【Elasticsearch 基础入门】Centos7下Elasticsearch 7.x安装与配置&#xff08;单机&#xff09; 目录 Elasticsearch系列文章目录前言单机模式1. 安装 J…...

大模型本地部署使用方法(Ollama脚手架工具、FisherAI浏览器大模型插件、AnythingLLM大模型集成应用平台)

一、Ollama &#xff08;一&#xff09;Ollama简介 Ollama是一个专为在本地环境中运行和定制大型语言模型而设计的工具。它提供简单高效的接口&#xff0c;用于创建、运行和管理这些模型&#xff0c;方便用户直接使用&#xff0c;也方便用作后台服务支撑其它应用程序。熟悉网…...

【华为OD-E卷 - 报数游戏 100分(python、java、c++、js、c)】

【华为OD-E卷 - 报数游戏 100分&#xff08;python、java、c、js、c&#xff09;】 题目 100个人围成一圈&#xff0c;每个人有一个编码&#xff0c;编号从1开始到100。 他们从1开始依次报数&#xff0c;报到为M的人自动退出圈圈&#xff0c;然后下一个人接着从1开始报数&…...

深入理解Spring框架:从基础到实践

前言 Spring框架是一个开源的企业级应用开发框架&#xff0c;它为Java开发者提供了灵活的架构支持&#xff0c;特别是在依赖注入&#xff08;IOC&#xff09;和面向切面编程&#xff08;AOP&#xff09;方面。本文将通过具体的示例&#xff0c;带你从Spring框架的概述、IOC容器…...

一觉醒来全球编码能力下降100000倍,新手小白的我决定科普C语言——函数

1. 函数的概念 数学中我们其实就⻅过函数的概念&#xff0c;⽐如&#xff1a;⼀次函数 y kx b &#xff0c;k和b都是常数&#xff0c;给⼀个任意的 x&#xff0c;就得到⼀个y值。其实在C语⾔也引⼊函数&#xff08;function&#xff09;的概念&#xff0c;有些翻译为&#xf…...

CentOS 上安装 Go (Golang)

1. 检查系统环境 确保系统为 CentOS 7 或 CentOS 8&#xff0c;或者其他兼容的 Linux 发行版。 cat /etc/os-release2. 安装依赖 安装一些必要的工具&#xff1a; sudo yum update -y sudo yum install -y wget tar3. 下载 Go 从 Go 官方下载页面获取适用于 Linux 的最新版…...

软件模拟I2C案例前提须知——EEPROM芯片之M24C02

引言 了解了I2C的基础知识后&#xff0c;我们将来使用一个I2C案例实践来深入理解I2C通讯&#xff0c;即软件模拟I2C。顾名思义&#xff0c;就是利用软件方式通过模拟I2C协议要求的时序或者说一些相关规定来实现一个I2C通讯协议&#xff0c;然后利用模拟出的I2C协议来实现两个设…...

GIS教程:全国数码商城系统

文章目录 注册高德地图API普通网页中测试地图加载地图添加标记地图配置点标记 Marker添加弹框创建vue项目并添加高德地图创建项目加载高德地图项目首页布局封装axios和配置代理服务器获取城市热门信息获取城市区县信息获取区县商城信息获取指定城市区县的经纬度坐标将地图缩放到…...

BroadCom-RDMA博通网卡如何进行驱动安装和设置使得对应网口具有RDMA功能以适配RDMA相机

BroadCom-RDMA博通网卡如何进行驱动安装和设置使得对应网口具有RDMA功能以适配RDMA相机 BroadCom-RDMA 博通网卡Baumer-RDMA 万兆网相机Baumer工业相机RDMA功能的技术背景BroadCom-RDMA博通网卡如何进行驱动安装和设置具有RDMA功能一、安装ZVA-BroadCom-RDMA网卡二、设备管理器…...

分布式微服务系统架构第90集:现代化金融核心系统

#1.1 深化数字化转型&#xff0c;核心面临新挑战 1、架构侧&#xff1a;无法敏捷协同数字金融经营模式转型。 2、需求侧&#xff1a;业务需求传导低效始终困扰金融机构。 3、开发侧&#xff1a;创新产品上市速度低于期望。 4、运维侧&#xff1a;传统面向资源型监控体系难以支撑…...

进阶数据结构——双向循环链表

目录 前言一、定义与结构二、特点与优势三、基本操作四、应用场景五、实现复杂度六、动态图解七、代码模版&#xff08;c&#xff09;八、经典例题九、总结结语 前言 这一期我们学习双向循环链表。双向循环链表不同于单链表&#xff0c;双向循环链表是一种特殊的数据结构&…...

记录一次,PyQT的报错,多线程Udp失效,使用工具如netstat来检查端口使用情况。

1.问题 报错Exception in thread Thread-1: Traceback (most recent call last): File "threading.py", line 932, in _bootstrap_inner File "threading.py", line 870, in run File "main.py", line 456, in udp_recv IndexError: list…...

安装anaconda3 后 电脑如何单独运行python,python还需要独立安装吗?

安装anaconda3 后 电脑如何单独运行python&#xff0c;python还需要独立安装吗? 电脑第一此安装anaconda用于jupyter notebook使用。 但是在运行cmd的时候&#xff0c;输入python --version 显示未安装或跳转商店提示安装。 明明我可以运行python但是为什么cmd却说我没安装呢…...

电子电气架构 --- 汽车电子拓扑架构的演进过程

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…...

ASP.NET Core 中使用依赖注入 (DI) 容器获取并执行自定义服务

目录 一、ASP.NET Core 中使用依赖注入 (DI) 容器获取并执行自定义服务 1. app.Services 2. GetRequiredService() 3. Init() 二、应用场景 三、依赖注入使用拓展 1、使用场景 2、使用步骤 1. 定义服务接口和实现类 2. 注册服务到依赖注入容器 3. 使用依赖注入获取并…...

leetcode——验证二叉搜索树(java)

给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含小于当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff1a; 输入…...

搜索引擎快速收录:关键词布局的艺术

本文来自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/21.html 搜索引擎快速收录中的关键词布局&#xff0c;是一项既精细又富有策略性的工作。以下是对关键词布局艺术的详细阐述&#xff1a; 一、关键词布局的重要性 关键词布局影响着后期页面…...

VLN视觉语言导航基础

0 概述 视觉语言导航模型旨在构建导航决策模型 π π π&#xff0c;在 t t t时刻&#xff0c;模型能够根据指令 W W W、历史轨迹 τ { V 1 , V 2 , . . . , V t − 1 } \tau\{V_1,V_2,...,V_{t-1}\} τ{V1​,V2​,...,Vt−1​}和当前观察 V t { P t , R t , N ( V t ) } V_…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...