yolov8 tracking编码为web 和 rtsp流输出

1 基础工作

打开cmd
输入 conda env list
输入 conda activate py38
查看 nvidia-smi
查看 nvcc,如下图所示 cuda为11.7 ,为确认可以查看program files 下面的cuda 安装,看到11.7 就行了,读者可以自行确认自己的版本。
在这里插入图片描述
查看nvidia-smi
在这里插入图片描述
打开yolov8 track以后,发现速度超级慢, 打开cpu占用率奇高,gpu为零, 打开cmd
import torch
torch.cuda.is_available()
为false
确认pytorch安装的为cpu版本,重新安装pytorch cuda 版本,去pytorch网站,点开后复制安装命令,把cuda版本改成自己本机的版本。

在这里插入图片描述
输入命令
python yolo\v8\detect\detect_and_trk.py model=yolov8s.pt source=“d:\test.mp4” show=True
在这里插入图片描述
gpu占用率上升到40% 左右。
在这里插入图片描述

2 输出到web

接下来我们要做的工作为rtsp server 编码上传, 为了让其他机器得到结果,我们必须编程并且得到图像结果,让后使用实时传输协议直接转成rtp 流,让本机成为rtsp server,使用vlc 等工具,可以直接拉取到编码后的流。
修改代码,上传结果,先把命令修改成为
python yolo\v8\detect\detect_and_trk.py model=yolov8s.pt source=“d:\test.mp4”
然后在代码里面自己用opencv 来show,也就是增加两行代码

        cv2.imshow("qbshow",im0)
        cv2.waitKey(3)

没问题show 出来了,也就是im0 是我们在增加了选框的地方,在show 代码的基础上把图片发送出去就行,我们选择使用websocket 发送,或者socket 直接发送都行,这样我们需要一个客户端还是服务端的选择,我们选择让python成为服务端,目的是为了不让python需要断线重连,python 成为服务端有很多好处,
1 是可以直接给web传送图片
2 是可以直接给

    def write_results(self, idx, preds, batch):

        p, im, im0 = batch
        log_string = ""
        if len(im.shape) == 3:
            im = im[None]  # expand for batch dim
        self.seen += 1
        im0 = im0.copy()
        if self.webcam:  # batch_size >= 1
            log_string += f'{idx}: '
            frame = self.dataset.count
        else:
            frame = getattr(self.dataset, 'frame', 0)
        # tracker
        self.data_path = p
    
        save_path = str(self.save_dir / p.name)  # im.jpg
        self.txt_path = str(self.save_dir / 'labels' / p.stem) + ('' if self.dataset.mode == 'image' else f'_{frame}')
        log_string += '%gx%g ' % im.shape[2:]  # print string
        self.annotator = self.get_annotator(im0)
        
        det = preds[idx]
        self.all_outputs.append(det)
        if len(det) == 0:
            return log_string
        for c in det[:, 5].unique():
            n = (det[:, 5] == c).sum()  # detections per class
            log_string += f"{n} {self.model.names[int(c)]}{'s' * (n > 1)}, "
    
    
        # #..................USE TRACK FUNCTION....................
        dets_to_sort = np.empty((0,6))
        
        for x1,y1,x2,y2,conf,detclass in det.cpu().detach().numpy():
            dets_to_sort = np.vstack((dets_to_sort, 
                        np.array([x1, y1, x2, y2, conf, detclass])))
        
        tracked_dets = tracker.update(dets_to_sort)
        tracks =tracker.getTrackers()
        
        for track in tracks:
            [cv2.line(im0, (int(track.centroidarr[i][0]),
                        int(track.centroidarr[i][1])), 
                        (int(track.centroidarr[i+1][0]),
                        int(track.centroidarr[i+1][1])),
                        rand_color_list[track.id], thickness=3) 
                        for i,_ in  enumerate(track.centroidarr) 
                            if i < len(track.centroidarr)-1 ] 
        

        if len(tracked_dets)>0:
            bbox_xyxy = tracked_dets[:,:4]
            identities = tracked_dets[:, 8]
            categories = tracked_dets[:, 4]
            draw_boxes(im0, bbox_xyxy, identities, categories, self.model.names)
           
        gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh
        cv2.imshow("qbshow",im0)
        cv2.waitKey(3)
        return log_string

2.1 web中显示

在python中加上websocket server的代码,以下是一个示例代码,读者可以自己测试,但是要应用到python 的图像处理server中,我们要做一些修改

import asyncio
import threading
import websockets
import time
CONNECTIONS = set()


async def server_recv(websocket):
    while True:
        try:
            recv_text = await websocket.recv()
            print("recv:", recv_text)
        except websockets.ConnectionClosed as e:
            # 客户端关闭连接,跳出对客户端的读取,结束函数
            print(e.code)
            await asyncio.sleep(0.01)
            break

async def server_hands(websocket):
  
    CONNECTIONS.add(websocket)
    print(CONNECTIONS)
    try:
        await websocket.wait_closed()
    finally:
        CONNECTIONS.remove(websocket) 



def message_all(message):
    websockets.broadcast(CONNECTIONS, message)   

async def handler(websocket, path):
    # 处理新的 WebSocket 连接
    print("New WebSocket route is ",path)

    try:
        await server_hands(websocket) # 握手加入队列
        await server_recv(websocket)  # 接收客户端消息并处理
     
    except websockets.exceptions.ConnectionClosedError as e:
        print(f"Connection closed unexpectedly: {e}")
    finally:
        pass
        # 处理完毕,关闭 WebSocket 连接
    print("WebSocket connection closed")


async def sockrun():
    async with websockets.serve(handler, "", 9090):
        await asyncio.Future()  # run forever

def main_thread(): 
    print("main")
    asyncio.run(sockrun())

在这里插入图片描述

2.2 修改

修改的地方为我们不能让websocket 阻塞主线程,虽然websocket为协程处理,但是依然要放到线程里面

def predict(cfg):
    init_tracker()
    random_color_list()
        
    cfg.model = cfg.model or "yolov8n.pt"
    cfg.imgsz = check_imgsz(cfg.imgsz, min_dim=2)  # check image size
    cfg.source = cfg.source if cfg.source is not None else ROOT / "assets"
    predictor = DetectionPredictor(cfg)
    predictor()


if __name__ == "__main__":
    thread = threading.Thread(target=main_thread)
    thread.start()
    predict()

值得注意的地方是:
很多人喜欢把编码做成base64 ,这样没有必要, 服务程序里面把二进制编码成base64, 消耗了cpu, 然后发送还大了很多,实际上我们直接发送二进制就行了,发送时

   cv2.waitKey(3)
   _, encimg = cv2.imencode('.jpg', im0)
   bys = np.array(encimg).tobytes()
   message_all(bys)

web端测试代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>websocket</title>
    
</head>
<body>
    <div>
        <button onclick="connecteClient()">开始接收</button>
    </div>

    <br>
    <div>
        <img src="" id="python_img" alt="websock" style="text-align:left; width: 640px; height: 360px;"> 
    </div>
    <script>
        function connecteClient() {
            var ws = new WebSocket("ws://127.0.0.1:9090");

            ws.onopen = function () {
                console.log("WebSocket 连接成功");
            };

            ws.onmessage = function (evt) {
                var received_msg = evt.data;
                blobToDataURI(received_msg, function (result) {
                    document.getElementById("python_img").src = result;
                })
            };

            ws.onclose = function () {
                console.log("连接关闭...");
            };
        }

       
        function blobToDataURI(blob, callback) {
            var reader = new FileReader();
            reader.readAsDataURL(blob);
            reader.onload = function (e) {
                callback(e.target.result);
            }
        }
    </script>
</body>
</html>

其中接收到二进制完成以后直接显示,不用服务端编码成base64,下图表明web端可以正常拿到jpg图片显示

在这里插入图片描述

其中message_all 是广播,只要是websocket client 我们就给他发一份,这样做,不但可以发送给web,也可以发送给rtsp server,我们让rtsp server 链接上来,取到jpg后,解码成为rgb24,然后编码成为h264,让vlc 可以链接rtsp server取到rtsp 流

3 rtsp server 编码h264 输出

我们的rtsp server 端使用c++ 编写,为了加快速度,使用了内存共享,而没有使用websocket client,当然是可以的,考虑到这里要解码,又要编码输出,还有可能需要使用硬件编码,为了提高效率,就这么做了,而且还简单,缺点就是这样多了一个问题,rtspserve只能启动在本机上。先编写一个h264Frame, h265 是一样的道理,后面再更改,暂时运行在windows上,等完成了再修改。注意以下代码只能运行在windows上

class H264Frame :public TThreadRunable
{
	const wchar_t*  sharedMemoryMutex = L"shared_memory_mutex";
	const wchar_t*  sharedMemoryName = L"shared_memory";
	LPVOID v_lpBase = NULL;
	HANDLE v_hMapFile = NULL;
	HANDLE 	m_shareMutex = NULL;
	std::shared_ptr<xop::RtspServer> v_server;
	std::string v_livename;
	std::wstring v_pathname;
	std::wstring v_pathmutex_name;

	int v_fps = 10;

	Encoder v_encoder;
	xop::MediaSessionId v_id;
	int v_init = 0;
	int v_quality = 28;
public:
	H264Frame(std::shared_ptr<xop::RtspServer> s, const char* livename, const wchar_t* pathname, xop::MediaSessionId session_id)
	{
		v_server = s;
		v_livename = livename;
		v_pathname = pathname;
		if (pathname != NULL)
		{
			v_pathmutex_name = pathname;
			v_pathmutex_name += L"_mutex";
		}
		v_id = session_id;
	}
	~H264Frame() {}

	bool Open()
	{
		if (v_pathname.empty())
			v_hMapFile = OpenFileMappingW(FILE_MAP_ALL_ACCESS, NULL, sharedMemoryName);
		else
			v_hMapFile = OpenFileMappingW(FILE_MAP_ALL_ACCESS, NULL, v_pathname.c_str());

		if (v_hMapFile == NULL)
		{
			//printf(" Waiting shared memory creation......\n");
			return false;
		}
		return true;
	}
	void Close()
	{
		if (m_shareMutex != NULL)
			ReleaseMutex(m_shareMutex);
		if (v_hMapFile != NULL)
			CloseHandle(v_hMapFile);
	}

	bool IsOpened() const
	{
		return (v_hMapFile != NULL);
	}

	void sendFrame(uint8_t* data ,int size, int sessionid)
	{
		xop::AVFrame videoFrame = { 0 };
		videoFrame.type = 0;
		videoFrame.size = size;// sizeofdata;// frame_size;
		videoFrame.timestamp = xop::H264Source::GetTimestamp();
		//videoFrame.notcopy = data;
		videoFrame.buffer.reset(new uint8_t[videoFrame.size]);
		std::memcpy(videoFrame.buffer.get(), data, videoFrame.size);
		v_server->PushFrame(sessionid, xop::channel_0, videoFrame);
	}
	int ReadFrame(int sessionid)
	{
		if (v_hMapFile)
		{

			v_lpBase = MapViewOfFile(v_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
			unsigned char* pMemory = (unsigned char*)v_lpBase;

			int w = 0, h = 0;
			int frame_size = 0;
			std::memcpy(&w, pMemory, 4);
			std::memcpy(&h, pMemory + 4, 4);
			std::memcpy(&frame_size, pMemory + 8, 4);
			//printf("read the width %d height %d framesize %d\n", imageWidth,imageHeight, frame_size);
			uint8_t* rgb = pMemory + 12;
			if (v_init == 0)
			{
				v_encoder.Encoder_Open(v_fps, w, h, w, h, v_quality);
				v_init = 1;
			}
			
			v_encoder.RGB2YUV(rgb, w, h);
			AVPacket * pkt = v_encoder.EncodeVideo();
			//while (!*framebuf++);
			if (pkt!=NULL) {
				uint8_t* framebuf = pkt->data;
				frame_size = pkt->size;
				uint8_t* spsStart = NULL;
				int spsLen = 0;
				uint8_t* ppsStart = NULL;
				int ppsLen = 0;
				uint8_t* seStart = NULL;
				int seLen = 0;
				uint8_t* frameStart = 0;//非关键帧地址或者关键帧地址
				int frameLen = 0;
				AnalyseNalu_no0x((const uint8_t*)framebuf, frame_size, &spsStart, spsLen, &ppsStart, ppsLen, &seStart, seLen, &frameStart, frameLen);
				if (spsStart != NULL && ppsStart!=NULL)
				{
					sendFrame(spsStart, spsLen, sessionid);
					sendFrame(ppsStart, ppsLen, sessionid);
				}
				if (frameStart != NULL)
				{
					sendFrame(frameStart, frameLen, sessionid);
				}


				av_packet_free(&pkt);
			}
			UnmapViewOfFile(pMemory);
		}
		return -1;
	}

	void Run()
	{
		m_shareMutex = CreateMutexW(NULL, false, sharedMemoryMutex/*v_pathmutex_name.c_str()*/);		v_encoder.Encoder_Open(10, 4096, 1080, 4096, 1080, 27);
		while (1)
		{
			if (Open() == true)
			{
				break;
			}
			else
			{
				printf("can not open share mem error\n");
				Sleep(3000);
			}
		}
		printf("wait ok\n");
		//检查是否改变参数
		//重新开始
		int64_t m_start_clock = 0;
		float delay = 1000000.0f / (float)(v_fps);//微妙

		//合并的rgb
		uint8_t * rgb = NULL;
		uint8_t * cam = NULL;

		int64_t start_timestamp = av_gettime_relative();//  GetTimestamp32();
		while (1)
		{
			if (IsStop())
				break;
			//获取一帧
			//rgb = 
			if (v_hMapFile)
			{
				//printf("start to lock sharemutex\n");
				WaitForSingleObject(m_shareMutex, INFINITE);

				//printf("read frame\n");
				ReadFrame(v_id);

				ReleaseMutex(m_shareMutex);
			}
			else
			{
				printf("Shared memory handle error\n");
				break;
			}
			
			
			int64_t tnow = av_gettime_relative();// GetTimestamp32();
			int64_t total = tnow - start_timestamp; //總共花費的時間
			int64_t fix_consume = ++m_start_clock * delay;
			if (total < fix_consume) {
				int64_t diff = fix_consume - total;
				if (diff > 0) { //相差5000微妙以上
					std::this_thread::sleep_for(std::chrono::microseconds(diff));
				}
			}
		}
		if (v_init == 0)
		{
			v_encoder.Encoder_Close();
			v_init = 1;
		}
	}

};


int main(int argc, char **argv)
{	
	
	std::string suffix = "live";
	std::string ip = "127.0.0.1";
	std::string port = "8554";
	std::string rtsp_url = "rtsp://" + ip + ":" + port + "/" + suffix;
	
	std::shared_ptr<xop::EventLoop> event_loop(new xop::EventLoop());
	std::shared_ptr<xop::RtspServer> server = xop::RtspServer::Create(event_loop.get());

	if (!server->Start("0.0.0.0", atoi(port.c_str()))) {
		printf("RTSP Server listen on %s failed.\n", port.c_str());
		return 0;
	}

#ifdef AUTH_CONFIG
	server->SetAuthConfig("-_-", "admin", "12345");
#endif

	xop::MediaSession *session = xop::MediaSession::CreateNew("live"); 
	session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); 
	//session->StartMulticast(); 
	session->AddNotifyConnectedCallback([] (xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port){
		printf("RTSP client connect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port);
	});
   
	session->AddNotifyDisconnectedCallback([](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) {
		printf("RTSP client disconnect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port);
	});

	xop::MediaSessionId session_id = server->AddSession(session);
      
	H264Frame h264frame(server, suffix.c_str(), L"shared_memory",session_id);

	//std::thread t1(SendFrameThread, server.get(), session_id, &h264_file);
	//t1.detach(); 

	std::cout << "Play URL: " << rtsp_url << std::endl;
	h264frame.Start();
	while (1) {
		xop::Timer::Sleep(100);
	}
	h264frame.Join();
	getchar();
	return 0;
}

完成以后,打开python 服务端,打开rtsp服务端,最后打开vlc查看
在这里插入图片描述
这样就完成了python端使用pytorch ,yolo 等等共享给c++ 的内存

其中python端的内存共享示例使用如下

import mmap
import contextlib
import time

while True:
    with contextlib.closing(mmap.mmap(-1, 25, tagname='test', access=mmap.ACCESS_READ)) as m:
        s = m.read(1024)#.replace('\x00', '')
        print(s)
    time.sleep(1)

读者可自行完成剩余的代码

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

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

相关文章

【深度解析C++】const成员函数

系列文章目录 &#x1f308;座右铭&#x1f308;&#xff1a;人的一生这么长、你凭什么用短短的几年去衡量自己的一生&#xff01; &#x1f495;个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C,自顶向下看Java-CSDN博客 ❤️相关文章❤️&#xff1a;Cthis指针&#xf…

【Redis技术专区】「原理分析」探讨Redis 6.0为何需要启用多线程?

探讨Redis 6.0为何需要启用多线程 背景介绍开启多线程多线程的CPU核心配置IO多线程模式单线程处理方式多线程处理方式 为什么要开启多线程&#xff1f;充分利用多核CPU提高网络I/O效率响应现代应用需求 多线程实现启用多线程 最后总结 背景介绍 在Redis 6.0版本中&#xff0c;…

期权二叉树估值与图计算

传统期权二叉树的算法都是基于数组的&#xff0c;对于没有编程基础的人来说非常不直观。二叉树是一种特殊的图&#xff0c;可以用python networkx这个图算法库实现&#xff0c;这个库不仅包含常用的图算法&#xff0c;还包含简单的绘图功能&#xff0c;非常适合研究分析使用。 …

git分支场景操作,应用场景

文章目录 git分支操作1.git branch--目前处在的分支上2.git checkout--要切换的分支名字3.git merge--要合并的分支名字4.git branch -d--要删除的分支名字 git分支操作 假设目前我们目前有三个版本 1.git branch–目前处在的分支上 现在要开发一个新功能在新的分支上 新建一…

Python序列之字典

系列文章目录 Python序列之列表Python序列之元组Python序列之字典&#xff08;本篇文章&#xff09;Python序列之集合 Python序列之字典 系列文章目录前言一、字典是什么&#xff1f;二、字典的操作1.创建&#xff08;1&#xff09;通过{}、dict()创建&#xff08;2&#xff0…

Cisco无线Mobility Express配置Image TFTP服务器

思科的无线AP&#xff1a; 1800&#xff0c; 2800&#xff0c; 3800系列 这一类的AP本身可以做为无线控制器使用&#xff0c;被称为Mobility Express&#xff0c;简称为ME 可以管理多少AP 最多可管理 25个 是否需要license才能管理 不需要license 支持哪些型号的AP注册 只要…

Rust学习笔记003:语句和表达式+函数+控制语句:IF,MATCH,LOOP

语句&#xff08;Statements&#xff09;和表达式&#xff08;Expressions&#xff09; 1. 语句&#xff08;Statements&#xff09;&#xff1a; 语句是执行一些操作但不返回值的代码单元。例如&#xff0c;声明变量、赋值、函数调用、宏调用等都是语句。 // 声明变量的语句…

Python教程(19)——python异常处理

异常处理 什么是异常异常处理方式try-except语句捕获异常类型 相关的异常类型 什么是异常 在计算机编程中&#xff0c;异常&#xff08;Exception&#xff09;是指在程序执行过程中发生的错误或异常情况。当出现异常时&#xff0c;程序无法正常继续执行&#xff0c;因此需要采…

【Redis-05】Redis如何实现保存键值对的保存及过期键的管理策略

在之前的文章我们介绍过&#xff0c;Redis服务器在启动之初&#xff0c;会初始化RedisServer的实例&#xff0c;在这个实例中存在很多重要的属性结构&#xff0c;同理本篇博客中介绍的数据库实现原理也会和其中的某些属性相关&#xff0c;我们继续看一下吧。 1.服务器和客户端…

JavaSE语法之十二:Object类

文章目录 一、概念二、获取对象信息三、对象比较equals方法四、hashcode方法 一、概念 Object是Java默认提供的一个类。Java里面除了Object类&#xff0c;所有的类都是存在继承关系的&#xff0c;默认会继承Object父类&#xff0c;即所有的类的对象都可以使用Object的引用进行…

安装与部署Hadoop

一、前置安装准备1、机器2、java3、创建hadoop用户 二、安装Hadoop三、环境配置1、workers2、hadoop-env.sh3、core-site.xml4、hdfs-site.xml5、linux中Hadoop环境变量 四、启动hadoop五、验证 一、前置安装准备 1、机器 主机名ip服务node1192.168.233.100NameNode、DataNod…

纯CSS的华为充电动画,它来了

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; Krpano专栏&#xff1a;想学Krpano的&#xff0c;冲 &#x1f514…

准备好通过 “Breakin’ B.I.G.” 在嘻哈音乐界大放异彩吧!

在 The Sandbox 推出人物化身系列后&#xff0c;是时候通过 “Breakin’ B.I.G.” 重返嘻哈音乐的黄金时代了。该体验于 12 月 20 日推出&#xff0c;一直持续到 1 月 3 日&#xff0c;让玩家回到 20 世纪 90 年代&#xff0c;体验以 Notorious B.I.G 为主角的 2D 街舞游戏。 获…

【深度学习-目标检测】01 - R-CNN 论文学习与总结

论文地址&#xff1a;Rich feature hierarchies for accurate object detection and semantic segmentation 论文学习 摘要&#xff08;Abstract&#xff09; 对象检测性能的现状&#xff1a; 在PASCAL VOC数据集上测量的对象检测性能在过去几年已经达到了一个高点。最佳性能…

python使用openpyxl为excel模版填充数据,生成多个Sheet页面

目标&#xff1a;希望根据一个给定的excel模版&#xff0c;生成多个Sheet页面&#xff0c;比如模版&#xff1a; 示例程序 import openpyxlexcel_workbook openpyxl.load_workbook("模版.xlsx") for _i in range(3): # 比如填充3个页面# 复制模版sheet页&#x…

arkts状态管理使用(@State、@Prop、@Link、@Provide、@Consume、@objectLink和@observed)

一、状态管理 1.在声明式UI中&#xff0c;是以状态驱动视图更新&#xff1a; ①状态&#xff08;State&#xff09;:指驱动视图更新的数据&#xff08;被装饰器标记的变量&#xff09; ②视图&#xff08;View&#xff09;:基于UI描述渲染得到用户界面 注意&#xff1a; ①…

机场信息集成系统系列介绍(6):机场协同决策支持系统ACDM*续集

目录 1、A-CDM实施效果评估背景 2、评估核心指标项 &#xff08;1&#xff09;机位效率 &#xff08;2&#xff09;登机效率 &#xff08;3&#xff09;推出效率 &#xff08;4&#xff09;滑行效率 &#xff08;5&#xff09;协同效率 3、其他指标项 &#xff08;1&a…

MongoDB Certified Associate Developer 认证考试心得

介绍 前段时间通过了 MongoDB Associate Developer 考试&#xff0c;也记下了一些心得&#xff0c;结果忘记发出来了&#xff0c;现在重新整理下。通过考试后证书是这样的: MongoDB 目前有两个认证证书 1. MongoDB Associate Developer 认证掌握使用MongoDB 来构建现代应用…

kivy开发一个登陆界面

Kivy Kivy是一个用于开发跨平台移动应用&#xff08;如Android和iOS&#xff09;以及桌面应用&#xff08;如Windows、Linux和macOS&#xff09;的Python框架。它采用开源许可证&#xff08;MIT许可证&#xff09;&#xff0c;提供了丰富的图形界面组件和工具&#xff0c;以便…

Tensorflow2.X的GPU版框架最快最稳搭建方法

一、环境基础 Windows10以上 已装Anaconda 支持GPU 二、搭建步骤 1. 在Anaconda中创建并进入虚拟环境 conda create -n envname python3.8 conda activate envname 注意&#xff1a;envname 替换为你自己想命名的&#xff0c;下文将以“Ljdenv”出现 2.安…