【Python】采用OpenCV和Flask来进行网络图像推流的低延迟高刷FPS方法(项目模板)

【Python】采用OpenCV和Flask来进行网络图像推流的低延迟高刷FPS方法(项目模板)

gitee项目模板:
网络图像推流项目模板(采用OpenCV和Flask来进行网络图像推流的低延迟高刷FPS方法)

前文:
【最简改进】基于OpenCV-Python+Flask的人脸检测网络摄像头图像输出(将本地图像转为网络URL地址,可输出带识别框的图像)

文章目录

  • 高刷方式
  • 网络线程
  • 视频线程
  • 整体代码
  • 附录:列表的赋值类型和py打包
    • 列表赋值
      • BUG复现
      • 代码改进
      • 优化
      • 总结
    • py打包

高刷方式

首先 在前文中 我们用OpenCV获取的图像转为bytes类型 然后发送给flask端网页进行图像推流 但由于OpenCV和网络部分都会占用系统资源 所以FPS不高 亦或是延迟较高
尤其是在树莓派等系统资源不够多的系统上运行时 延时非常明显
另外 还可能进行人脸识别、手势识别等功能 所以延迟进一步提高
但采用多线程的方式 可以在以上满足所有条件的情况下 将延时控制在150ms内 切FPS能达到30
在这里插入图片描述
以上还是手机端浏览效果 服务端是由树莓派本身建立的
手机用的自带的浏览器 本身就有一定延迟

网络线程

网络线程其实就一个 就是flask的app.run函数

def start_server():
    web_app.run(host='0.0.0.0', port=local_post)
    
thread_send = threading.Thread(target=start_server)
thread_send.setDaemon(True)
thread_send.start() 

而图像推流依靠以下函数:

def send_img():
    global cam_img
    global pause_all_flag
    while pause_all_flag == 0:
        image = cv2.imencode('.jpg', cam_img)[1].tobytes()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + image + b'\r\n')
        if pause_all_flag == 1:
            break
    return 


@web_app.route('/video_feed')
def video_feed():
    return Response(send_img(), mimetype='multipart/x-mixed-replace; boundary=frame')

另外 表单提取和推送也很简单

@web_app.route('/', methods=['GET', 'POST'])
def index():  
    global kill_all_flag
    global pause_all_flag
    global command_str
    
    if request.method == 'POST':
        command_str = str(request.form.get('WEB_COMMAND'))
        
        if kill_all_flag == 0 and pause_all_flag == 0:
            command_jugg()
        elif kill_all_flag == 0 and pause_all_flag == 1:
            print(command_str)
            if command_str == "继续程序":
                pause_all_flag = 0
                print("Continue")
            else:
                print("程序已暂停,继续请按键")
        else:
            print("程序已终止,请重启程序")
            
    now_today = time.time()
    time_hour = time.localtime(now_today).tm_hour
    time_min = time.localtime(now_today).tm_min
        
    if time_hour < 10:
        time_hour = "0"+str(time_hour)
    if time_min < 10:
        time_min = "0"+str(time_min)
    local_time_str = str(time.localtime(now_today).tm_year)+"-"+str(time.localtime(now_today).tm_mon)+"-"+str(time.localtime(now_today).tm_mday)+" "+str(time_hour)+":"+str(time_min)
                
    data = {
        '当前时间:': [ local_time_str]
        }
        
    return render_template('index.html',data_dict=data)

这三个部分其实是共用一个线程的

视频线程

视频线程的话 如果有检测、识别等部分 可以单独列一个线程 这里推荐使用最纯粹的视频获取线程作为一个单独的线程:
这里我是放在主线程里

def img_main():
    global pause_all_flag
    global kill_all_flag
    global cam_img 
    
    while True:
        time.sleep(0.1)
        while pause_all_flag==0:
            cam_img = cv2.flip(cv2_cap.read()[1],1)
            cv2.imshow("http://"+local_ip+":"+str(local_post)+"/ (img: video_feed)",cam_img)
            # 展示图像            
            if pause_all_flag == 1:
                pause_all_flag = 1
                print("暂停程序")
                cv2.destroyAllWindows()
                break
            if cv2.waitKey(10) == 27:   # 通过esc键退出摄像
                kill_all_flag = 1
                pause_all_flag = 1
                print("结束程序")
                cv2.destroyAllWindows()
                break
        if kill_all_flag == 1:
            break
    cv2_cap.release()
    print("全部退出")
    return 

几乎是感觉不到延迟的:
在这里插入图片描述

整体代码

import cv2
from flask import Flask, render_template, Response, request
import threading
import socket
import time

global pause_all_flag
pause_all_flag = 0
global kill_all_flag
kill_all_flag = 0

global command_str
command_str = None
    
local_post = 1212
while True:
    try:
        web_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        web_socket.connect(("8.8.8.8",80))
        local_ip = str(web_socket.getsockname()[0])
        web_socket.close()
        print("Network Enable")
        break
    except:
        print("Network Error...")
        time.sleep(5)

web_app = Flask(__name__)

cv2_cap = cv2.VideoCapture(1)  # 开启摄像头

global cam_img
ok, cam_img = cv2_cap.read()  # 读取摄像头图像
if ok is False:
    print('无法读取到摄像头1!')
    cv2_cap.release()
    cam_img=None
    
    cv2_cap = cv2.VideoCapture(0)  # 开启摄像头
    ok, cam_img = cv2_cap.read()  # 读取摄像头图像
    if ok is False:
        pause_all_flag = 1
        kill_all_flag = 1
        print('无法读取到摄像头0!')

try:
    high=cam_img.shape[0]
    width=cam_img.shape[1]
except:
    pause_all_flag = 1
    kill_all_flag = 1


def command_jugg():
    global kill_all_flag
    global pause_all_flag
    global command_str
   
    print(command_str)
    command_str = None
    
@web_app.route('/', methods=['GET', 'POST'])
def index():  
    global kill_all_flag
    global pause_all_flag
    global command_str
    
    if request.method == 'POST':
        command_str = str(request.form.get('WEB_COMMAND'))
        
        if kill_all_flag == 0 and pause_all_flag == 0:
            command_jugg()
        elif kill_all_flag == 0 and pause_all_flag == 1:
            print(command_str)
            if command_str == "继续程序":
                pause_all_flag = 0
                print("Continue")
            else:
                print("程序已暂停,继续请按键")
        else:
            print("程序已终止,请重启程序")
            
    now_today = time.time()
    time_hour = time.localtime(now_today).tm_hour
    time_min = time.localtime(now_today).tm_min
        
    if time_hour < 10:
        time_hour = "0"+str(time_hour)
    if time_min < 10:
        time_min = "0"+str(time_min)
    local_time_str = str(time.localtime(now_today).tm_year)+"-"+str(time.localtime(now_today).tm_mon)+"-"+str(time.localtime(now_today).tm_mday)+" "+str(time_hour)+":"+str(time_min)
                
    data = {
        '当前时间:': [ local_time_str]
        }
        
    return render_template('index.html',data_dict=data)


def img_main():
    global pause_all_flag
    global kill_all_flag
    global cam_img 
    
    while True:
        time.sleep(0.1)
        while pause_all_flag==0:
            cam_img = cv2.flip(cv2_cap.read()[1],1)
            cv2.imshow("http://"+local_ip+":"+str(local_post)+"/ (img: video_feed)",cam_img)
            # 展示图像            
            if pause_all_flag == 1:
                pause_all_flag = 1
                print("暂停程序")
                cv2.destroyAllWindows()
                break
            if cv2.waitKey(10) == 27:   # 通过esc键退出摄像
                kill_all_flag = 1
                pause_all_flag = 1
                print("结束程序")
                cv2.destroyAllWindows()
                break
        if kill_all_flag == 1:
            break
    cv2_cap.release()
    print("全部退出")
    return 

def send_img():
    global cam_img
    global pause_all_flag
    while pause_all_flag == 0:
        image = cv2.imencode('.jpg', cam_img)[1].tobytes()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + image + b'\r\n')
        if pause_all_flag == 1:
            break
    return 


@web_app.route('/video_feed')
def video_feed():
    return Response(send_img(), mimetype='multipart/x-mixed-replace; boundary=frame')

def start_server():
    web_app.run(host='0.0.0.0', port=local_post)

def main():
    thread_send = threading.Thread(target=start_server)
    thread_send.setDaemon(True)
    thread_send.start() 
    
    img_main()
    
    time.sleep(1)
    print("已退出所有程序")
    return 

if __name__ == "__main__":    
    main()
    
<html>
    <!--meta http-equiv="refresh" content="5"--> 
    <head>
        <title>OpenCV网络控制系统</title>
    </head>    
    <body>
        <h1>OpenCV网络控制系统</h1>
        <form action="/" method="post" style="float:left">
            <p>
                <input type="submit" style="font-size:100px" name="WEB_COMMAND" value="刷新网页">
            </p>	
            <p>
            <table>
                {% for k,v in data_dict.items() %}
                    <tr>
                        <td>{{k}}</td>
                        <td>{{v[0]}}</td>
                        <td>{{v[1]}}</td>
                        <td>{{v[2]}}</td>
                        <td>{{v[3]}}</td>
                        <td>{{v[4]}}</td>
                    </tr>
                    {% endfor %}
                </table>   
            </p> 
        </form>        
        <img src="{{ url_for('video_feed') }}" height="540" style="float:left"> 
        
    </body>
</html>
    

附录:列表的赋值类型和py打包

列表赋值

BUG复现

闲来无事写了个小程序 代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021

@author: 16016
"""

a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):
    for j in range(16):
        a_list[j]=str(a_list[j])+'_'+str(j)
        print("序号:",j)
        print('a_list:\n',a_list)
        
        
        c_list[j]=a_list
        print('c_list[0]:\n',c_list[0])
        print('\n')
#        b_list[j]=a_list[7],a_list[8]
#        print(b_list[j])
        # 写入到Excel:
#print(c_list,'\n')    

我在程序中 做了一个16次的for循环 把列表a的每个值后面依次加上"_"和循环序号
比如循环第x次 就是把第x位加上_x 这一位变成x_x 我在输出测试中 列表a的每一次输出也是对的
循环16次后列表a应该变成[‘0_0’, ‘1_1’, ‘2_2’, ‘3_3’, ‘4_4’, ‘5_5’, ‘6_6’, ‘7_7’, ‘8_8’, ‘9_9’, ‘10_10’, ‘11_11’, ‘12_12’, ‘13_13’, ‘14_14’, ‘15_15’] 这也是对的

同时 我将每一次循环时列表a的值 写入到空列表c中 比如第x次循环 就是把更改以后的列表a的值 写入到列表c的第x位
第0次循环后 c[0]的值应该是[‘0_0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘10’, ‘11’, ‘12’, ‘13’, ‘14’, ‘15’] 这也是对的
但是在第1次循环以后 c[0]的值就一直在变 变成了c[x]的值
相当于把c_list[0]变成了c_list[1]…以此类推 最后得出的列表c的值也是每一项完全一样
我不明白这是怎么回事
我的c[0]只在第0次循环时被赋值了 但是后面它的值跟着在改变

如图:
在这里插入图片描述
第一次老出bug 赋值以后 每次循环都改变c[0]的值 搞了半天都没搞出来
无论是用appen函数添加 还是用二维数组定义 或者增加第三个空数组来过渡 都无法解决

代码改进

后来在我华科同学的指导下 突然想到赋值可以赋的是个地址 地址里面的值一直变化 导致赋值也一直变化 于是用第二张图的循环套循环深度复制实现了

代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021

@author: 16016
"""

a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):
    for j in range(16):
        a_list[j]=str(a_list[j])+'_'+str(j)
        print("序号:",j)
        print('a_list:\n',a_list)
        
        
        for i in range(16):
            c_list[j].append(a_list[i])
        print('c_list[0]:\n',c_list[0])
        print('\n')
#        b_list[j]=a_list[7],a_list[8]
#        print(b_list[j])
        # 写入到Excel:
print(c_list,'\n')    

解决了问题

在这里插入图片描述

优化

第三次是请教了老师 用copy函数来赋真值

代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021

@author: 16016
"""

a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):
    for j in range(16):
        a_list[j]=str(a_list[j])+'_'+str(j)
        print("序号:",j)
        print('a_list:\n',a_list)
        
        
        c_list[j]=a_list.copy()
        print('c_list[0]:\n',c_list[0])
        print('\n')
#        b_list[j]=a_list[7],a_list[8]
#        print(b_list[j])
        # 写入到Excel:
#print(c_list,'\n')    

同样能解决问题
在这里插入图片描述
最后得出问题 就是指针惹的祸!

a_list指向的是个地址 而不是值 a_list[i]指向的才是单个的值 copy()函数也是复制值而不是地址

如果这个用C语言来写 就直观一些了 难怪C语言是基础 光学Python不学C 遇到这样的问题就解决不了

C语言yyds Python是什么垃圾弱智语言

总结

由于Python无法单独定义一个值为指针或者独立的值 所以只能用列表来传送
只要赋值是指向一个列表整体的 那么就是指向的一个指针内存地址 解决方法只有一个 那就是将每个值深度复制赋值(子列表内的元素提取出来重新依次连接) 或者用copy函数单独赋值

如图测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
部分代码:

# -*- coding: utf-8 -*-
"""
Created on Sat Nov 20 16:45:48 2021

@author: 16016
"""

def text1():
    A=[1,2,3]
    B=[[],[],[]]
    for i in range(len(A)):
        A[i]=A[i]+i
        B[i]=A
        print(B)

def text2():
    A=[1,2,3]
    B=[[],[],[]]
    
    A[0]=A[0]+0
    B[0]=A
    print(B)
    A[1]=A[1]+1
    B[1]=A
    print(B)
    A[2]=A[2]+2
    B[2]=A
    print(B)
    
if __name__ == '__main__':
    text1()
    print('\n')
    text2()

py打包

Pyinstaller打包exe(包括打包资源文件 绝不出错版)

依赖包及其对应的版本号

PyQt5 5.10.1
PyQt5-Qt5 5.15.2
PyQt5-sip 12.9.0

pyinstaller 4.5.1
pyinstaller-hooks-contrib 2021.3

Pyinstaller -F setup.py 打包exe

Pyinstaller -F -w setup.py 不带控制台的打包

Pyinstaller -F -i xx.ico setup.py 打包指定exe图标打包

打包exe参数说明:

-F:打包后只生成单个exe格式文件;

-D:默认选项,创建一个目录,包含exe文件以及大量依赖文件;

-c:默认选项,使用控制台(就是类似cmd的黑框);

-w:不使用控制台;

-p:添加搜索路径,让其找到对应的库;

-i:改变生成程序的icon图标。

如果要打包资源文件
则需要对代码中的路径进行转换处理
另外要注意的是 如果要打包资源文件 则py程序里面的路径要从./xxx/yy换成xxx/yy 并且进行路径转换
但如果不打包资源文件的话 最好路径还是用作./xxx/yy 并且不进行路径转换

def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

而后再spec文件中的datas部分加入目录
如:

a = Analysis(['cxk.py'],
             pathex=['D:\\Python Test\\cxk'],
             binaries=[],
             datas=[('root','root')],
             hiddenimports=[],
             hookspath=[],
             hooksconfig={},
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

而后直接Pyinstaller -F setup.spec即可

如果打包的文件过大则更改spec文件中的excludes 把不需要的库写进去(但是已经在环境中安装了的)就行

这些不要了的库在上一次编译时的shell里面输出
比如:
在这里插入图片描述

在这里插入图片描述
然后用pyinstaller --clean -F 某某.spec

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

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

相关文章

短剧小程序开发:打造全新用户体验

随着移动互联网的普及&#xff0c;小程序作为一种轻量级的应用程序形式&#xff0c;已经成为了现代人生活中不可或缺的一部分。短剧小程序作为其中的一种&#xff0c;更是以其独特的魅力&#xff0c;吸引了大量用户。本文将探讨短剧小程序的发展背景、优势、开发流程和未来趋势…

【java面试】常见问题(超详细)

目录 一、java常见问题JDK和JRE的区别是什么&#xff1f;Java中的String类是可变的还是不可变的&#xff1f;Java中的equals方法和hashCode方法有什么关系&#xff1f;Java中什么是重载【Overloading】&#xff1f;什么是覆盖【Overriding】&#xff1f;它们有什么区别&#xf…

Beego之Beego MVC架构介绍

1、beego MVC架构介绍 beego 是一个典型的 MVC 框架&#xff0c;它的整个执行逻辑如下图所示&#xff1a; 通过文字来描述如下&#xff1a; 1、在监听的端口接收数据&#xff0c;默认监听在 8080 端口。 2、用户请求到达 8080 端口之后进入 beego 的处理逻辑。 3、初始化 C…

【每日一题】4.LeetCode——杨辉三角

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

idea连接docker

idea 插件无法连接docker问题 原文&#xff1a;idea 插件无法连接docker问题 // 修改docker配置 vi /usr/lib/systemd/system/docker.service // 加上该段配置允许任何ip访问 -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock // 重启docker即可 systemctl restart dock…

图像处理------调整色调

什么是色调&#xff1f; 色调&#xff0c;在画面上表现思想、感情所使用的色彩和色彩的浓淡。分为暖色调和冷色调。 from cv2 import destroyAllWindows, imread, imshow, waitKey#创建棕褐色色调 def make_sepia(img, factor: int):pixel_h, pixel_v img.shape[0], img.shap…

OSPF协议解析及相关技术探索(C/C++代码实现)

OSPF&#xff08;开放最短路径优先&#xff09;是一种用于自治系统&#xff08;AS&#xff09;内部的路由协议&#xff0c;它是基于链路状态算法的。OSPF的设计目的是为了提供一种可扩展、快速收敛和高效的路由解决方案。 OSPF概念和特点 概念 自治系统&#xff08;AS&#…

二叉树的最大深度[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个二叉树root&#xff0c;返回其最大深度。 二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a…

高水平 ICT 实验实训平台建设

一、平台建设概述 1.1 人工智能仿真实验实训平台 建设高水平 ICT 实验实训平台–人工智能仿真实验实训平台&#xff0c;是为了提供学生在人工智能领域深入学习和实践的机会。承载《人工智能基础》《人工智能应用》《移动机器人技术应用》《视觉开源机器人》《深度学习与神经网…

C. Doremy‘s City Construction(二分图问题)

思路&#xff1a;把集合划分成两部分,一部分中每个数都比另一部分小,这两部分连成一个完全二分图,这种情况是最优的,还需要特判所有数都相等的情况. 代码&#xff1a; void solve(){int n;cin >> n;vector<int>a(n 1);for(int i 1;i < n;i )cin >> a[…

RK3399平台开发系列讲解(PCIE篇)PCIE 配置过程

🚀返回专栏总目录 文章目录 一、硬件拓扑结构二、配置过程演示沉淀、分享、成长,让自己和他人都能有所收获!😄 一、硬件拓扑结构 以下图中的设备的配置过程为例,给大家做示范。 二、配置过程演示 下文中BDF表示Bus,Device,Function,用这三个数值来表示设备。 软件设置…

上海亚商投顾:沪指涨超3%收复2900点 多只中字头股涨停

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日放量反弹&#xff0c;尾盘一度涨超3%&#xff0c;收复2900点关口&#xff0c;深成指涨2%&#xff0c;…

解决Windows系统本地端口被占用

目录 一、被程序占用端口 1.通过终端杀掉占用端口的进程 2.任务管理器 二、被系统列为保留端口 前言&#xff1a; 首先了解为什么会出现端口被占用的情况 端口被占用的情况可能出现的原因有很多&#xff0c;主要有以下几点&#xff1a; 1.多个应用程序同时启动&…

【STM32】STM32学习笔记-硬件SPI读写W25Q64(40)

00. 目录 文章目录 00. 目录01. SPI简介02. W25Q64简介03. SPI相关API3.1 SPI_Init3.2 SPI_Cmd3.3 SPI_I2S_SendData3.4 SPI_I2S_ReceiveData3.5 SPI_I2S_GetFlagStatus3.6 SPI_I2S_ClearFlag3.7 SPI_InitTypeDef 04. 硬件SPI读写W25Q64接线图05. 硬件SPI读写W25Q64示例06. 程序…

FPGA高端项目:Xilinx Artix7系列FPGA多路视频拼接 工程解决方案 提供4套工程源码和技术支持

目录 1、前言版本更新说明给读者的一封信FPGA就业高端项目培训计划免责声明 2、相关方案推荐我已有的FPGA视频拼接叠加融合方案本方案在Xilinx Kintex7 系列FPGA上的应用本方案在Xilinx Zynq7000系列FPGA上的应用 3、设计思路框架视频源选择ov5640 i2c配置及采集动态彩条多路视…

【VSAN数据恢复】VSAN数据重构迁移失败的数据恢复案例

VSAN简介&#xff1a; VSAN存储是一个对象存储&#xff0c;以文件系统呈现给在vSphere主机上。这个对象存储服务会从VSAN集群中的每台主机上加载卷&#xff0c;将卷展现为单一的、在所有节点上都可见的分布式共享数据存储。 对于虚拟机来说&#xff0c;只有一个数据存储&#x…

cg插画设计行业怎么样,如何学习插画设计

插画设计行业是一个充满创意和艺术性的行业&#xff0c;随着数字化时代的不断发展&#xff0c;cg插画的应用范围越来越广泛&#xff0c;市场需求也在逐年增长。以下是一些关于acg插画设计行业的现状和发展趋势&#xff1a; 市场需求不断增长&#xff1a;随着广告、媒体、影视、…

使用Apache POI 创建和读取excel表

目录 1. Apache POI 中文使用手册 1.1 Apache POI 项目介绍 1.2 处理组件 1.2.1 Excel 文件处理组件 1.2.2 Word 文件处理组件 1.2.3 PPT 文件处理组件 1.2.4 文档属性组件 1.2.5 Visio 文件处理组件 1.2.6 Microsoft Publisher 98&#xff08;-2007&#xff09;文件处…

HMI-Board以太网数据监视器(二)MQTT和LVGL

E ∫ d E ∫ k d q r 2 k L ∫ d q r 2 E \int dE \int \frac{kdq}{r^2} \frac{k}{L} \int \frac{dq}{r^2} E∫dE∫r2kdq​Lk​∫r2dq​ E Q 2 π ϵ L 2 E \frac{Q}{2\pi\epsilon L^2} E2πϵL2Q​ Γ ( n ) ( n − 1 ) ! ∀ n ∈ N \Gamma(n) (n-1)!\quad\forall n…

电脑自动开机播放PPT的解决方案

客户有个需求&#xff0c;要求与LED大屏幕连接的电脑定时自动播放PPT。为了安全电脑在不播放的时段&#xff0c;必须关机。 目录 1、使用“时控插座”并进行设置 2、戴尔电脑BIOS设置&#xff08;上电开机&#xff09; 3、设置Windows自动登录 4、任务计划设置 5、启动Au…
最新文章