支持YUV和RGB格式两路视频同时播放

1.头文件:

sdlqtrgb.h


#pragma once
#include <QtWidgets/QWidget>
#include "ui_sdlqtrgb.h"
#include <thread>
class SdlQtRGB : public QWidget
{
    Q_OBJECT

public:
    SdlQtRGB(QWidget* parent = Q_NULLPTR);
    ~SdlQtRGB()
    {
        is_exit_ = true;
        //等待渲染线程退出
        th_.join();
    }
    void timerEvent(QTimerEvent* ev) override;
    void resizeEvent(QResizeEvent* ev) override;
    //线程函数,用于刷新视频
    void Main();

signals:
    void ViewS();   //信号函数,将任务放入列表
public slots:
    void View();    //显示的槽函数
    void Open1();
    void Open2();
    void Open(int i);
private:
    std::thread th_;
    bool is_exit_ = false;//处理线程退出
    Ui::SdlQtRGBClass ui;
};

xvideoview.h



#ifndef XVIDEO_VIEW_H
#define XVIDEO_VIEW_H
#include <mutex>
#include <fstream>
struct AVFrame;

void MSleep(unsigned int ms);

//获取当前时间戳 毫秒
long long NowMs();


/// 视频渲染接口类
/// 隐藏SDL实现
/// 渲染方案可替代
// 线程安全
class XVideoView
{
public:
    enum Format  //枚举的值和ffmpeg中一致
    {
        YUV420P = 0,
        ARGB = 25,
        RGBA = 26,
        BGRA = 28
    };
    enum RenderType
    {
        SDL = 0
    };
    static XVideoView* Create(RenderType type = SDL);

    
    /// 初始化渲染窗口 线程安全 可多次调用
    /// @para w 窗口宽度
    /// @para h 窗口高度
    /// @para fmt 绘制的像素格式
    /// @para win_id 窗口句柄,如果为空,创建新窗口
    /// @return 是否创建成功
    virtual bool Init(int w, int h,
        Format fmt = RGBA) = 0;

    //清理所有申请的资源,包括关闭窗口
    virtual void Close() = 0;

    //处理窗口退出事件
    virtual bool IsExit() = 0;

    //
    /// 渲染图像 线程安全
    ///@para data 渲染的二进制数据
    ///@para linesize 一行数据的字节数,对于YUV420P就是Y一行字节数
    /// linesize<=0 就根据宽度和像素格式自动算出大小
    /// @return 渲染是否成功
    virtual bool Draw(const unsigned  char* data, int linesize = 0) = 0;
    virtual bool Draw(
        const unsigned  char* y, int y_pitch,
        const unsigned  char* u, int u_pitch,
        const unsigned  char* v, int v_pitch
    ) = 0;


    //显示缩放
    void Scale(int w, int h)
    {
        scale_w_ = w;
        scale_h_ = h;
    }

    bool DrawFrame(AVFrame* frame);

    int render_fps() { return render_fps_; }

    //打开文件
    bool Open(std::string filepath);


    //
    /// 读取一帧数据,并维护AVFrame空间
    /// 每次调用会覆盖上一次数据
    AVFrame* Read();
    void set_win_id(void* win) { win_id_ = win; }
protected:
    void* win_id_ = nullptr; //窗口句柄
    int render_fps_ = 0;       //显示帧率
    int width_ = 0;             //材质宽高
    int height_ = 0;
    Format fmt_ = RGBA;         //像素格式
    std::mutex mtx_;            //确保线程安全
    int scale_w_ = 0;           //显示大小
    int scale_h_ = 0;
    long long beg_ms_ = 0;       //计时开始时间
    int count_ = 0;              //统计显示次数
private:
    std::ifstream ifs_;
    AVFrame* frame_ = nullptr;
};

#endif

xsdl.h



#pragma once


#include "xvideo_view.h"
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Texture;
class XSDL :public XVideoView
{
public:
    void Close() override;
    
    /// 初始化渲染窗口 线程安全
    /// @para w 窗口宽度
    /// @para h 窗口高度
    /// @para fmt 绘制的像素格式
    /// @para win_id 窗口句柄,如果为空,创建新窗口
    /// @return 是否创建成功
    bool Init(int w, int h,
        Format fmt = RGBA) override;

    //
    /// 渲染图像 线程安全
    ///@para data 渲染的二进制数据
    ///@para linesize 一行数据的字节数,对于YUV420P就是Y一行字节数
    /// linesize<=0 就根据宽度和像素格式自动算出大小
    /// @return 渲染是否成功
    bool Draw(const unsigned  char* data,
        int linesize = 0) override;
    bool Draw(
        const unsigned  char* y, int y_pitch,
        const unsigned  char* u, int u_pitch,
        const unsigned  char* v, int v_pitch
    ) override;
    bool IsExit() override;
private:
    SDL_Window* win_ = nullptr;
    SDL_Renderer* render_ = nullptr;
    SDL_Texture* texture_ = nullptr;
};

2.源文件:

sdlqtrgb.cpp


#include "sdlqtrgb.h"
#include <fstream>
#include <iostream>
#include <QMessageBox>
#include <thread>
#include <sstream>
#include <iostream>
#include <QSpinBox>
#include <QFileDialog>
#include "xvideo_view.h"
#include <vector>
#include <sstream>
using namespace std;

static std::vector<XVideoView*> views;
SdlQtRGB::SdlQtRGB(QWidget* parent)
    : QWidget(parent)
{
    ui.setupUi(this);

    //绑定渲染信号槽
    connect(this, SIGNAL(ViewS()), this, SLOT(View()));
    views.push_back(XVideoView::Create());// 创建一个对象并用vector 存储
    views.push_back(XVideoView::Create());
    views[0]->set_win_id((void*)ui.video1->winId());//将video1的窗口句柄赋值给xvideoview对象的窗口句柄
    views[1]->set_win_id((void*)ui.video2->winId());
    th_ = std::thread(&SdlQtRGB::Main, this);
}

void SdlQtRGB::timerEvent(QTimerEvent* ev)
{
}

void SdlQtRGB::View()
{

    static int last_pts[32] = { 0 };//last_pts 用于存放每个视图上次渲染的时间戳,lase_pts[0]表示第一个播放窗口
    static int fps_arr[32] = { 0 };//用于存放每个视图的帧率值。
    fps_arr[0] = ui.set_fps1->value();//获取第一个播放窗口的fps,用fps_arr[0]存储
    fps_arr[1] = ui.set_fps2->value();

    for (int i = 0; i < views.size(); i++)//依次渲染容器中的xvideoview对象
    {
        if (fps_arr[i] <= 0) continue;//invaluable data then pass
        //需要间隔时间
        int ms = 1000 / fps_arr[i];// video1's fps = 100,then ms = 10,意思是100fps,一秒渲染100次,每10ms渲染一次

        //判断是否到了可渲染时间
        if (NowMs() - last_pts[i] < ms)//一秒渲染100次,每10ms渲染一次,若时间没达到10ms那么不继续进行渲染
            continue;
        last_pts[i] = NowMs();//记录下此次的渲染时间

        auto frame = views[i]->Read();//read()执行完后 返回读取了yuv数据的avframe对象给frame,此时frame中已经进行了文件读取有了yuv数据,参考read函数即可
        if (!frame)continue;
        views[i]->DrawFrame(frame);//计算了当前帧率并且进行了渲染
        //显示fps
        stringstream ss;
        ss << "fps:" << views[i]->render_fps();
        if (i == 0)
            ui.fps1->setText(ss.str().c_str());
        else
            ui.fps2->setText(ss.str().c_str());

    }
}

void SdlQtRGB::Main()
{
    while (!is_exit_)
    {
        ViewS();//调用一次view槽函数,再没进行Open函数之前是不能把view槽函数执行完的,因为进行draw之前要进行材质的初始化 Init函数,在Open函数之中
                //一旦执行了Open函数,Init即进行完毕,则一直在执行的线程函数Main则会调用view函数,此时view函数中的draw函数可以进行渲染了

        MSleep(10);//每调用一次槽函数就休眠10ms,用于控制帧率
    }
}

void SdlQtRGB::Open1()
{
    Open(0);
}
void SdlQtRGB::Open2()
{
    Open(1);
}
void SdlQtRGB::Open(int i)
{
    QFileDialog fd;
    auto filename = fd.getOpenFileName();
    if (filename.isEmpty())return;
    cout << filename.toLocal8Bit().data() << endl;
    //打开文件
    if (!views[i]->Open(filename.toLocal8Bit().toStdString()))
    {
        return;
    }
    int w = 0;
    int h = 0;
    QString pix = 0;  //YUV420P RGBA
    if (i == 0)//下拉框的下标0,表示YUV420P,下标1表示RGBA以此类推
    {
        w = ui.width1->value();
        h = ui.height1->value();
        pix = ui.pix1->currentText(); //像素格式
    }
    else
    {
        w = ui.width2->value();
        h = ui.height2->value();
        pix = ui.pix2->currentText();
    }
    XVideoView::Format fmt = XVideoView::YUV420P;
    if (pix == "YUV420P")
    {

    }
    else if (pix == "RGBA")
    {
        fmt = XVideoView::RGBA;
    }
    else if (pix == "ARGB")
    {
        fmt = XVideoView::ARGB;
    }
    else if (pix == "BGRA")
    {
        fmt = XVideoView::BGRA;
    }
    //初始化窗口和材质
    views[i]->Init(w, h, fmt);
}




void SdlQtRGB::resizeEvent(QResizeEvent* ev)
{
    //ui.label->resize(size());
    //ui.label->move(0, 0);
    //view->Scale(width(), height());
}

xvideoview.cpp



#include "xsdl.h"
#include <thread>
#include <iostream>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
}
#pragma comment(lib,"avutil.lib")

void MSleep(unsigned int ms)
{
	auto beg = clock();
	for (int i = 0; i < ms; i++)
	{
		this_thread::sleep_for(1ms);
		if ((clock() - beg) / (CLOCKS_PER_SEC / 1000) >= ms)
			break;
	}
}
long long NowMs()
{
	return clock() / (CLOCKS_PER_SEC / 1000);//获取当前时间戳 毫秒
}
AVFrame* XVideoView::Read()
{
	if (width_ <= 0 || height_ <= 0 || !ifs_)return NULL;
	//AVFrame空间已经申请,如果参数发生变化,需要释放空间
	if (frame_)//若分配了
	{
		if (frame_->width != width_
			|| frame_->height != height_
			|| frame_->format != fmt_)
		{
			//释放AVFrame对象空间,和buf引用计数减一
			av_frame_free(&frame_);
		}
	}
	if (!frame_)//若没分配则分配
	{
		//分配对象空间和像素空间
		frame_ = av_frame_alloc();
		frame_->width = width_;
		frame_->height = height_;
		frame_->format = fmt_;
		frame_->linesize[0] = width_ * 4;
		if (frame_->format == AV_PIX_FMT_YUV420P)//YUV420P单独处理,因为他不是平面存储是多层存储需要三个数组存储三个平面
		{
			frame_->linesize[0] = width_; // Y
			frame_->linesize[1] = width_ / 2;//U
			frame_->linesize[2] = width_ / 2;//V
		}

		//生成AVFrame空间,使用默认对齐
		auto re = av_frame_get_buffer(frame_, 0);
		if (re != 0)
		{
			char buf[1024] = { 0 };
			av_strerror(re, buf, sizeof(buf) - 1);
			cout << buf << endl;
			av_frame_free(&frame_);
			return NULL;

		}
	}
	if (!frame_)return NULL;

	//读取一帧数据
	if (frame_->format == AV_PIX_FMT_YUV420P)
	{
		ifs_.read((char*)frame_->data[0],
			frame_->linesize[0] * height_);	//Y
		ifs_.read((char*)frame_->data[1],
			frame_->linesize[1] * height_ / 2);	//U
		ifs_.read((char*)frame_->data[2],
			frame_->linesize[2] * height_ / 2);	//V
	}//执行完后 avframe中就保存了从2.yuv中读取的yuv数据了
	else	//RGBA ARGB BGRA 32
	{
		ifs_.read((char*)frame_->data[0], frame_->linesize[0] * height_);
	}

	if (ifs_.gcount() == 0)
		return NULL;
	return frame_;




}

//打开文件
bool XVideoView::Open(std::string filepath)
{
	if (ifs_.is_open())
	{
		ifs_.close();
	}
	ifs_.open(filepath, ios::binary);
	return ifs_.is_open();//ifs_绑定了所选的文件 如 2.yuv
}
XVideoView* XVideoView::Create(RenderType type)
{
	switch (type)
	{
	case XVideoView::SDL:
		return new XSDL();
		break;
	default:
		break;
	}
	return nullptr;
}

bool XVideoView::DrawFrame(AVFrame* frame)
{
	if (!frame || !frame->data[0])return false;
	count_++;
	if (beg_ms_ <= 0)
	{
		beg_ms_ = clock();
	}
	//计算显示帧率
	else if ((clock() - beg_ms_) / (CLOCKS_PER_SEC / 1000) >= 1000) //一秒计算一次fps
	{
		render_fps_ = count_;
		count_ = 0;
		beg_ms_ = clock();
	}

	switch (frame->format)
	{
	case AV_PIX_FMT_YUV420P:
		return Draw(frame->data[0], frame->linesize[0],//Y
			        frame->data[1], frame->linesize[1],	//U
			        frame->data[2], frame->linesize[2]	//V
		);
	case AV_PIX_FMT_BGRA:
	case AV_PIX_FMT_ARGB:
	case AV_PIX_FMT_RGBA:
		return Draw(frame->data[0], frame->linesize[0]);
	default:
		break;
	}
	return false;
}

xsdl.cpp



#include "xsdl.h"
#include "sdl/SDL.h"
#include <iostream>
using namespace std;
#pragma comment(lib,"SDL2.lib")
static bool InitVideo()
{
    static bool is_first = true;
    static mutex mux;
    unique_lock<mutex> sdl_lock(mux);
    if (!is_first)return true;
    is_first = false;
    if (SDL_Init(SDL_INIT_VIDEO))
    {
        cout << SDL_GetError() << endl;
        return false;
    }
    //设定缩放算法,解决锯齿问题,线性插值算法
    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
    return true;
}
bool XSDL::IsExit()
{
    SDL_Event ev;
    SDL_WaitEventTimeout(&ev, 1);
    if (ev.type == SDL_QUIT)
        return true;
    return false;
}
void XSDL::Close()
{
    //确保线程安全
    unique_lock<mutex> sdl_lock(mtx_);
    if (texture_)
    {
        SDL_DestroyTexture(texture_);
        texture_ = nullptr;
    }
    if (render_)
    {
        SDL_DestroyRenderer(render_);
        render_ = nullptr;
    }
    if (win_)
    {
        SDL_DestroyWindow(win_);
        win_ = nullptr;
    }
}

bool XSDL::Init(int w, int h, Format fmt)
{
    if (w <= 0 || h <= 0)return false;
    //初始化SDL 视频库
    InitVideo();

    //确保线程安全
    unique_lock<mutex> sdl_lock(mtx_);
    width_ = w;
    height_ = h;
    fmt_ = fmt;

    if (texture_)
        SDL_DestroyTexture(texture_);
    if (render_)
        SDL_DestroyRenderer(render_);

    ///1 创建窗口
    if (!win_)
    {
        if (!win_id_)
        {
            //新建窗口
            win_ = SDL_CreateWindow("",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
            );
        }
        else
        {
            //渲染到控件窗口
            win_ = SDL_CreateWindowFrom(win_id_);
        }
    }
    if (!win_)
    {
        cerr << SDL_GetError() << endl;
        return false;
    }

    /// 2 创建渲染器

    render_ = SDL_CreateRenderer(win_, -1, SDL_RENDERER_ACCELERATED);
    if (!render_)
    {
        cerr << SDL_GetError() << endl;
        return false;
    }
    //创建材质 (显存)
    unsigned int sdl_fmt = SDL_PIXELFORMAT_RGBA8888;
    switch (fmt)
    {
    case XVideoView::RGBA:
        sdl_fmt = SDL_PIXELFORMAT_RGBA32;
        break;
    case XVideoView::BGRA:
        sdl_fmt = SDL_PIXELFORMAT_BGRA32;
        break;
    case XVideoView::ARGB:
        sdl_fmt = SDL_PIXELFORMAT_ARGB32;
        break;
    case XVideoView::YUV420P:
        sdl_fmt = SDL_PIXELFORMAT_IYUV;
        break;
    default:
        break;
    }

    texture_ = SDL_CreateTexture(render_,
        sdl_fmt,                        //像素格式
        SDL_TEXTUREACCESS_STREAMING,    //频繁修改的渲染(带锁)
        w, h                            //材质大小
    );
    if (!texture_)
    {
        cerr << SDL_GetError() << endl;
        return false;
    }
    return true;
}
bool XSDL::Draw(
    const unsigned  char* y, int y_pitch,
    const unsigned  char* u, int u_pitch,
    const unsigned  char* v, int v_pitch
)
{
    //参数检查
    if (!y || !u || !v)return false;
    unique_lock<mutex> sdl_lock(mtx_);
    if (!texture_ || !render_ || !win_ || width_ <= 0 || height_ <= 0)
        return false;

    //复制内存到显显存
    auto re = SDL_UpdateYUVTexture(texture_,
        NULL,
        y, y_pitch,
        u, u_pitch,
        v, v_pitch);
    if (re != 0)
    {
        cout << SDL_GetError() << endl;
        return false;
    }
    //清空屏幕
    SDL_RenderClear(render_);

    //材质复制到渲染器

    SDL_Rect rect;
    SDL_Rect* prect = nullptr;
    if (scale_w_ > 0)  //用户手动设置缩放
    {
        rect.x = 0; rect.y = 0;
        rect.w = scale_w_;//渲染的宽高,可缩放
        rect.h = scale_w_;
        prect = &rect;
    }
    re = SDL_RenderCopy(render_, texture_, NULL, prect);
    if (re != 0)
    {
        cout << SDL_GetError() << endl;
        return false;
    }
    SDL_RenderPresent(render_);
    return true;
}
bool XSDL::Draw(const unsigned char* data, int linesize)
{
    if (!data)return false;
    unique_lock<mutex> sdl_lock(mtx_);
    if (!texture_ || !render_ || !win_ || width_ <= 0 || height_ <= 0)
        return false;
    if (linesize <= 0)
    {
        switch (fmt_)
        {
        case XVideoView::RGBA:
        case XVideoView::ARGB:
            linesize = width_ * 4;
            break;
        case XVideoView::YUV420P:
            linesize = width_;
            break;
        default:
            break;
        }
    }
    if (linesize <= 0)
        return false;
    //复制内存到显显存
    auto re = SDL_UpdateTexture(texture_, NULL, data, linesize);
    if (re != 0)
    {
        cout << SDL_GetError() << endl;
        return false;
    }
    //清空屏幕
    SDL_RenderClear(render_);

    //材质复制到渲染器

    SDL_Rect rect;
    SDL_Rect* prect = nullptr;
    if (scale_w_ > 0)  //用户手动设置缩放
    {
        rect.x = 0; rect.y = 0;
        rect.w = scale_w_;//渲染的宽高,可缩放
        rect.h = scale_w_;
        prect = &rect;
    }
    re = SDL_RenderCopy(render_, texture_, NULL, prect);
    if (re != 0)
    {
        cout << SDL_GetError() << endl;
        return false;
    }
    SDL_RenderPresent(render_);
    return true;
}

main.cpp



#include "sdlqtrgb.h"
#include <QtWidgets/QApplication>

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

3.运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/697245.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

现实转虚拟:Video2Game引领3D互动体验

在当今数字化时代&#xff0c;虚拟环境的创建对于游戏开发、虚拟现实应用和自动驾驶模拟器等多个领域至关重要。然而&#xff0c;传统的虚拟环境创建过程不仅复杂而且成本高昂&#xff0c;通常需要专业人员和专业软件开发工具的参与。例如&#xff0c;著名的《侠盗猎车手V》以其…

「51媒体」江苏媒体宣传报道,邀请媒体报道资源汇总

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 江苏作为中国东部的重要省份&#xff0c;拥有丰富的媒体资源&#xff0c;包括电视台、广播电台、报纸以及网络媒体。 电视台 江苏卫视&#xff1a;作为江苏省唯一的省级卫视台&#xff…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:桥梁结构安全监测

中国铁路设计集团有限公司&#xff08;简称中国铁设&#xff09;&#xff0c;原铁道第三勘察设计院集团有限公司&#xff08;铁三院&#xff09;&#xff0c;是中国国家铁路集团有限公司所属的唯一设计企业&#xff0c;成立于1953年&#xff0c;总部位于天津市&#xff0c;是以…

基于机器学习的锂电池RUL SOH预测

数据集为NASA锂电池数据集。 import datetimeimport numpy as npimport pandas as pdfrom scipy.io import loadmatfrom sklearn.preprocessing import MinMaxScalerfrom sklearn.metrics import mean_squared_errorfrom sklearn import metricsimport matplotlib.pyplot as p…

python tushare股票量化数据处理:笔记

1、安装python和tushare及相关库 matplotlib pyplot pandas pandas_datareader >>> import matplotlib.pyplot as plt >>> import pandas as pd >>> import datetime as dt >>> import pandas_datareader.data as web 失败的尝试yf…

01——生产监控平台——WPF

生产监控平台—— 一、介绍 VS2022 .net core(net6版本&#xff09; 1、文件夹&#xff1a;MVVM /静态资源&#xff08;图片、字体等&#xff09; 、用户空间、资源字典等。 2、图片资源库&#xff1a; https://www.iconfont.cn/ ; 1.资源字典Dictionary 1、…

攻防演练之-动员大会

清晨的阳光透过薄雾洒在甲方的攻防演练中心。由于国家对于重点行业的数据灾备的要求。因此每一家企业都会选择在不同的地理位置建多个数据中心&#xff0c;包括一个生产中心、一个同城灾难备份中心、一个异地灾难备份中心。通过这种方式将业务分布在不同地理位置的数据中心&…

PowerDesigner 16.5安装教程

&#x1f4d6;PowerDesigner 16.5安装教程 ✅1. 下载✅2. 安装 ✅1. 下载 官网地址&#xff1a;https://www.powerdesigner.biz/EN/powerdesigner/powerdesigner-licensing-history.php 云盘下载&#xff1a;https://www.123pan.com/s/4brbVv-aUoWA.html ✅2. 安装 1.运行P…

Linux网络诊断工具mtr命令详解

目录 一、mtr概述 二、mtr的特点 1、动态路由显示 2、数据包类型 3、显示延迟和丢包 4、过滤和日志 5、网络探测 三、基本用法 1、基本语法 2、帮助 3、常用选项 四、输出解释 1、常见mtr命令及其输出 2、输出解释 四、命令实例 1. 最基本的用法 2. 显示报告形式…

Leetcode3170. 删除星号以后字典序最小的字符串

Every day a Leetcode 题目来源&#xff1a;3170. 删除星号以后字典序最小的字符串 解法1&#xff1a;栈 由于要去掉最小的字母&#xff0c;为了让字典序尽量小&#xff0c;相比去掉前面的字母&#xff0c;去掉后面的字母更好。 从左到右遍历字符串 s&#xff0c;用 26 个栈…

Collections工具类及其案例

package exercise;public class Demo1 {public static void main(String[] args) {//可变参数//方法形参的个数是可以发生变化的//格式&#xff1a;属性类型...名字//int...argsint sum getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);System.out.println(sum);}//底层&#xff1a;可…

嵌入式仪器模块:DMM LCR SMU 及自动化测试软件

• 6 位数字表显示 • 24 位分辨率 • 250 KSPS 采样率 • 电源和数字 I/O 均采用隔离抗噪技术 应用场景 • 电压、电流、电阻、电感、电容的高精度测量 • 二极管/三极管测试 通道1222输入阻抗电压10 MΩHigh-Z, 10 MΩHigh-Z电流10 Ω50 mΩ / 2 Ω / 2 KΩ2 KΩ / 2 M…

Upscayl:款利用人工智能技术,深度学习算法,实现图像无损放大和增强的强大工具。

Upscayl AI&#xff1a; Upscayl AI是一款基于先进的人工智能技术&#xff0c;特别是深度学习算法开发的图像增强工具。它能够智能地分析并改善图像质量&#xff0c;实现无损放大、细节重建和模糊消除&#xff0c;让老旧、低分辨率或模糊的照片焕发新生&#xff0c;达到高清画…

23.在游戏中按下Home键呼出辅助窗口

上一个内容&#xff1a;22.钩子注入原理 在 22.钩子注入原理 它的代码上进行修改 效果图&#xff1a; 首先在CWndMain.h文件中添加下图红框里的东西 ChangeShowState函数的实现 void CWndMain::ChangeShowState() {UiShow !UiShow;ShowWindow(UiShow); } OnInitDialog函数…

在 Android App 里使用 C 代码 - NDK

原生开发套件 (NDK) 是一套工具&#xff0c;使能够在 Android 应用中使用 C 和 C 代码&#xff0c;并提供众多平台库&#xff0c;可使用这些平台库管理原生 activity 和访问实体设备组件&#xff0c;例如传感器和触控输入。 NDK 可能不适合大多数 Android 编程初学者&#xff…

保存图片奇怪的bug

今天发现一个奇怪的bug 这个的dpi是100de ,但是我取完切片之后&#xff0c;发现这个结果就变了

Word中插入Mathtype右编号,调整公式与编号的位置

当你已经将mathtype内置于word后&#xff0c;可以使用右编号快速插入公式 但是往往会出现公式和编号出现的位置或之间的距离不合适 比如我在双栏下插入公式&#xff0c;会发现插入的公式与编号是适用于单栏的 解决办法&#xff1a; 开始->样式->MTDisplayLquation -&g…

基于Java+SpringBoot制作一个景区导览小程序

基于Java+SpringBoot制作一个景区导览小程序。其中系统前端功能包括注册登录、景区采风、旅游导览、地图导航、发布采风、门票预订、修改个人信息;系统后台功能包括用户管理、景区管理、采风管理等模块。 摘要一、小程序1. 创建小程序2. 首页3. 景区采风页4. 旅游导览页5. 发布…

让GNSSRTK不再难【第二天-第3部分】

第11讲 定位方程构建以及最小二乘 11.1 定位方程重构 历史讲中我们已经初步构建了单点定位的先验残差&#xff1a; p i s P i s − ( X s − X 0 ) 2 ( Y s − Y 0 ) 2 ( Z s − Z 0 ) 2 c δ t r − I i s − T i s − ϵ P i s p_i^s P_i^s - \sqrt{(X^s - X_0)^2 (Y…

学生信息管理(C语言)

学生信息管理 (1)问题描述 学生信息包括:学号,姓名,年龄,性别,出生年月,地址,电话,E-mail等。试设计一学生信息管理系统,使之能提供以下功能: 系统以菜单方式工作学生信息录入功能(学生信息用文件保存)---输入学生信息浏览功能---输出查询、排序功能---算法1、…