Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录
Python|GIF 解析与构建(5):手搓截屏和帧率控制
一、引言
二、技术实现:手搓截屏模块
2.1 核心原理
2.2 代码解析:ScreenshotData类
2.2.1 截图函数:capture_screen
三、技术实现:帧率控制模块
3.1 原理:基于时间差的帧率控制
3.2 代码解析:control_frame类
四、效果验证:截屏与帧率测试
4.1 测试代码
五、优化方向与注意事项
六、总结
Python|GIF 解析与构建(5):手搓截屏和帧率控制
一、引言
在 GIF 构建场景中,实时捕获屏幕画面并精准控制帧率是核心需求之一。本文将基于 Windows API,通过ctypes
库实现自定义截屏功能,并结合时间管理实现帧率控制,为后续 GIF 动态图生成奠定基础。
二、技术实现:手搓截屏模块
2.1 核心原理
利用 Windows API 中的 GDI(图形设备接口)实现屏幕像素数据抓取,主要流程:
- 获取屏幕设备上下文(DC)
- 创建兼容位图(Bitmap)存储截图数据
- 通过
BitBlt
函数复制屏幕区域像素 - 解析位图数据获取 RGB 像素值
2.2 代码解析:ScreenshotData
类
class ScreenshotData():def __init__(self):# 加载系统库self.gdi32 = ctypes.windll.gdi32self.user32 = ctypes.windll.user32# 获取屏幕尺寸(含DPI缩放)SM_CXSCREEN, SM_CYSCREEN = 0, 1hdc = self.user32.GetDC(None)try:dpi = self.gdi32.GetDeviceCaps(hdc, 88) # 获取DPIzoom = dpi / 96.0 # 计算缩放比例finally:self.user32.ReleaseDC(None, hdc)self.screenWidth = int(self.user32.GetSystemMetrics(SM_CXSCREEN) * zoom)self.screenHeight = int(self.user32.GetSystemMetrics(SM_CYSCREEN) * zoom)
- 关键点:
- 通过
GetDeviceCaps
获取 DPI,解决高分屏缩放问题 GetSystemMetrics
获取原始屏幕分辨率,结合缩放比例计算实际尺寸
- 通过
2.2.1 截图函数:capture_screen
def capture_screen(self, x, y, width, height):hwnd = self.user32.GetDesktopWindow() # 获取桌面窗口句柄hdc_src = self.user32.GetDC(hwnd) # 获取源设备上下文# 创建兼容内存DC和位图hdc_dest = self.gdi32.CreateCompatibleDC(hdc_src)bmp = self.gdi32.CreateCompatibleBitmap(hdc_src, width, height)old_bmp = self.gdi32.SelectObject(hdc_dest, bmp)# 复制屏幕区域(SRCCOPY为直接像素复制)self.gdi32.BitBlt(hdc_dest, 0, 0, width, height, hdc_src, x, y, 0x00CC0020)# 解析位图像素数据(24位RGB格式)class RGBQUAD(ctypes.Structure): ... # 颜色结构体定义class BITMAPINFOHEADER(ctypes.Structure): ... # 位图信息头定义bmi = BITMAPINFO()bmi.bmiHeader = BITMAPINFOHEADER(ctypes.sizeof(BITMAPINFOHEADER), width, -height, 1, 24, 0, 0, 0, 0, 0, 0)pixel_data = (ctypes.c_ubyte * (width * height * 3))() # 3字节/像素(RGB)# 获取像素数据if not self.gdi32.GetDIBits(hdc_src, bmp, 0, height, pixel_data, ctypes.byref(bmi), 0):print("GetDIBits failed:", ctypes.WinError())# 资源释放self.gdi32.SelectObject(hdc_dest, old_bmp)self.gdi32.DeleteDC(hdc_dest)self.user32.ReleaseDC(hwnd, hdc_src)self.gdi32.DeleteObject(bmp)return pixel_data
- 核心步骤:
BitBlt
实现屏幕区域拷贝(参数0x00CC0020
为 SRCCOPY 模式)- 通过
BITMAPINFOHEADER
定义位图格式(24 位真彩色,负高度表示自底向上存储) GetDIBits
获取原始像素数据(BGR 顺序,需后续转换为 RGB)
三、技术实现:帧率控制模块
3.1 原理:基于时间差的帧率控制
通过记录每帧开始时间,计算实际耗时并与目标帧率时间比较,通过sleep
补正时间差,公式:
- 目标单帧时间:
time_one_frame = 1 / fps
- 实际耗时:
spend = 当前时间 - 开始时间
- 补正时间:
max(0, time_one_frame - spend)
3.2 代码解析:control_frame
类
class control_frame():def __init__(self):self.start_time = 0.0 # 帧开始时间self.fps = 10 # 目标帧率self.time_one_frame = 1/self.fps # 单帧时间self.fps_count = 0 # 总帧数self.time_all = time.time() # 启动时间def start(self):self.start_time = time.time() # 记录帧开始时间self.fps_count += 1def spend(self):return time.time() - self.start_time # 计算已用时间def wait(self):spend = self.spend()# 动态计算实际帧率(用于监控)true_frame = self.fps_count / (time.time() - self.time_all)if true_frame > self.fps:# 补正时间差,确保不超过目标帧率if self.time_one_frame - spend > 0:time.sleep(self.time_one_frame - spend)
- 优势:
- 动态监控实际帧率(
true_frame
) - 自适应系统负载,通过
sleep
柔性控制帧率
- 动态监控实际帧率(
四、效果验证:截屏与帧率测试
4.1 测试代码
s = ScreenshotData()
wait = control_frame()
wait.fps = 20 # 设置目标帧率20FPS
frame_number = 100 # 捕获100帧
st = time.time()for i in range(1, frame_number+1):wait.start()# 捕获屏幕区域(0,0)到(20,20)data = s.capture_screen(0, 0, 20, 20)wait.wait()# 输出实时帧率print(f"第{i}帧 | 实时帧率:{1 / ((time.time()-st)/i):.2f} FPS")print(f"总耗时:{time.time()-st:.2f}秒 | 平均帧率:{frame_number/(time.time()-st):.2f} FPS")
五、优化方向与注意事项
-
截图性能优化:
- 使用
GetDC
/ReleaseDC
时避免频繁调用,可改为缓存设备上下文 - 尝试
CreateDIBSection
替代GetDIBits
,减少内存拷贝
- 使用
-
帧率控制增强:
- 增加帧率平滑算法(如指数移动平均)
- 支持动态帧率调整(根据系统负载自动降帧)
-
跨平台适配:
当前仅支持 Windows
六、总结
本文通过ctypes
实现了 Windows 下的自定义截屏,并基于时间管理实现了帧率控制,下一个就是关于tk的录制软件制作。
以下是完整代码:
import ctypes# 获取屏幕数据 class ScreenshotData():def __init__(self):self.gdi32 = ctypes.windll.gdi32self.user32 = ctypes.windll.user32# 定义常量SM_CXSCREEN = 0SM_CYSCREEN = 1# 缩放比例zoom = 1hdc = self.user32.GetDC(None)try:dpi = self.gdi32.GetDeviceCaps(hdc, 88)zoom = dpi / 96.0finally:self.user32.ReleaseDC(None, hdc)self.screenWidth = int(self.user32.GetSystemMetrics(SM_CXSCREEN) * zoom)self.screenHeight = int(self.user32.GetSystemMetrics(SM_CYSCREEN) * zoom)# 屏幕截取def capture_screen(self, x, y, width, height):# 获取桌面窗口句柄hwnd = self.user32.GetDesktopWindow()# 获取桌面窗口的设备上下文hdc_src = self.user32.GetDC(hwnd)if len(str(hdc_src)) > 16:return 0# 创建一个与屏幕兼容的内存设备上下文hdc_dest = self.gdi32.CreateCompatibleDC(hdc_src)# 创建一个位图bmp = self.gdi32.CreateCompatibleBitmap(hdc_src, width, height)# 将位图选入内存设备上下文old_bmp = self.gdi32.SelectObject(hdc_dest, bmp)# 定义SRCCOPY常量SRCCOPY = 0x00CC0020# 捕获屏幕self.gdi32.BitBlt(hdc_dest, 0, 0, width, height, hdc_src, x, y, SRCCOPY)"""gdi32.BitBlt(hdc_src, # 目标设备上下文 x_dest, # 目标矩形左上角的x坐标 y_dest, # 目标矩形左上角的y坐标 width, # 宽度 height, # 高度 hdc_dest, # 源设备上下文 x_src, # 源矩形左上角的x坐标(通常是0) y_src, # 源矩形左上角的y坐标(通常是0) SRCCOPY) # 复制选项"""# 定义 RGBQUAD 结构体class RGBQUAD(ctypes.Structure):_fields_ = [("rgbBlue", ctypes.c_ubyte),("rgbGreen", ctypes.c_ubyte),("rgbRed", ctypes.c_ubyte),("rgbReserved", ctypes.c_ubyte)]# 定义 BITMAPINFOHEADER 结构体class BITMAPINFOHEADER(ctypes.Structure):_fields_ = [("biSize", ctypes.c_uint),("biWidth", ctypes.c_int),("biHeight", ctypes.c_int),("biPlanes", ctypes.c_ushort),("biBitCount", ctypes.c_ushort),("biCompression", ctypes.c_uint),("biSizeImage", ctypes.c_uint),("biXPelsPerMeter", ctypes.c_int),("biYPelsPerMeter", ctypes.c_int),("biClrUsed", ctypes.c_uint),("biClrImportant", ctypes.c_uint)]# 定义 BITMAPINFO 结构体class BITMAPINFO(ctypes.Structure):_fields_ = [("bmiHeader", BITMAPINFOHEADER),("bmiColors", RGBQUAD * 3)] # 只分配了3个RGBQUAD的空间BI_RGB = 0DIB_RGB_COLORS = 0# 分配像素数据缓冲区(这里以24位位图为例,每个像素3字节)pixel_data = (ctypes.c_ubyte * (width * height * 3))() # 4# 填充 BITMAPINFO 结构体bmi = BITMAPINFO()bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)bmi.bmiHeader.biWidth = widthbmi.bmiHeader.biHeight = -height # 注意:负高度表示自底向上的位图bmi.bmiHeader.biPlanes = 1bmi.bmiHeader.biBitCount = 24 # 24即3*8 32bmi.bmiHeader.biCompression = BI_RGB # 无压缩# 调用 GetDIBits 获取像素数据ret = self.gdi32.GetDIBits(hdc_src, bmp, 0, height, pixel_data, ctypes.byref(bmi), DIB_RGB_COLORS)if ret == 0:print("GetDIBits failed:", ctypes.WinError())# 恢复设备上下文self.gdi32.SelectObject(hdc_dest, old_bmp)# 删除内存设备上下文self.gdi32.DeleteDC(hdc_dest)# 释放桌面窗口的设备上下文self.user32.ReleaseDC(hwnd, hdc_src)# bmp已经被处理,现在删除它self.gdi32.DeleteObject(bmp)return pixel_dataimport time# 控制帧率 class control_frame():def __init__(self):self.start_time = float() # 每次启动时间self.fps = int(10) # fpsself.time_one_frame = 1 / self.fps # 补正时间self.fps_count = 0 # 总帧率self.time_all = time.time() # 启动时间# 启动def start(self):self.start_time = time.time()self.fps_count += 1# 花销def spend(self):spend = time.time() - self.start_timereturn spend# 等待def wait(self):spend = self.spend()true_frame = self.fps_count / (time.time() - self.time_all)if true_frame > self.fps:if self.time_one_frame - spend > 0:time.sleep(self.time_one_frame - spend)s = ScreenshotData() wait = control_frame() # 帧率 wait.fps = 20 # 总帧率 frame_number = 100 st = time.time() for i in range(1, frame_number + 1):wait.start()data = s.capture_screen(0, 0, 20, 20)wait.wait()print("帧率输出:", 1 / ((time.time() - st) / i)) print("花费时间:", time.time() - st)
import ctypes# 获取屏幕数据
class ScreenshotData():def __init__(self):self.gdi32 = ctypes.windll.gdi32self.user32 = ctypes.windll.user32# 定义常量SM_CXSCREEN = 0SM_CYSCREEN = 1# 缩放比例zoom = 1hdc = self.user32.GetDC(None)try:dpi = self.gdi32.GetDeviceCaps(hdc, 88)zoom = dpi / 96.0finally:self.user32.ReleaseDC(None, hdc)self.screenWidth = int(self.user32.GetSystemMetrics(SM_CXSCREEN) * zoom)self.screenHeight = int(self.user32.GetSystemMetrics(SM_CYSCREEN) * zoom)# 屏幕截取def capture_screen(self, x, y, width, height):# 获取桌面窗口句柄hwnd = self.user32.GetDesktopWindow()# 获取桌面窗口的设备上下文hdc_src = self.user32.GetDC(hwnd)if len(str(hdc_src)) > 16:return 0# 创建一个与屏幕兼容的内存设备上下文hdc_dest = self.gdi32.CreateCompatibleDC(hdc_src)# 创建一个位图bmp = self.gdi32.CreateCompatibleBitmap(hdc_src, width, height)# 将位图选入内存设备上下文old_bmp = self.gdi32.SelectObject(hdc_dest, bmp)# 定义SRCCOPY常量SRCCOPY = 0x00CC0020# 捕获屏幕self.gdi32.BitBlt(hdc_dest, 0, 0, width, height, hdc_src, x, y, SRCCOPY)"""gdi32.BitBlt(hdc_src, # 目标设备上下文 x_dest, # 目标矩形左上角的x坐标 y_dest, # 目标矩形左上角的y坐标 width, # 宽度 height, # 高度 hdc_dest, # 源设备上下文 x_src, # 源矩形左上角的x坐标(通常是0) y_src, # 源矩形左上角的y坐标(通常是0) SRCCOPY) # 复制选项"""# 定义 RGBQUAD 结构体class RGBQUAD(ctypes.Structure):_fields_ = [("rgbBlue", ctypes.c_ubyte),("rgbGreen", ctypes.c_ubyte),("rgbRed", ctypes.c_ubyte),("rgbReserved", ctypes.c_ubyte)]# 定义 BITMAPINFOHEADER 结构体class BITMAPINFOHEADER(ctypes.Structure):_fields_ = [("biSize", ctypes.c_uint),("biWidth", ctypes.c_int),("biHeight", ctypes.c_int),("biPlanes", ctypes.c_ushort),("biBitCount", ctypes.c_ushort),("biCompression", ctypes.c_uint),("biSizeImage", ctypes.c_uint),("biXPelsPerMeter", ctypes.c_int),("biYPelsPerMeter", ctypes.c_int),("biClrUsed", ctypes.c_uint),("biClrImportant", ctypes.c_uint)]# 定义 BITMAPINFO 结构体class BITMAPINFO(ctypes.Structure):_fields_ = [("bmiHeader", BITMAPINFOHEADER),("bmiColors", RGBQUAD * 3)] # 只分配了3个RGBQUAD的空间BI_RGB = 0DIB_RGB_COLORS = 0# 分配像素数据缓冲区(这里以24位位图为例,每个像素3字节)pixel_data = (ctypes.c_ubyte * (width * height * 3))() # 4# 填充 BITMAPINFO 结构体bmi = BITMAPINFO()bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)bmi.bmiHeader.biWidth = widthbmi.bmiHeader.biHeight = -height # 注意:负高度表示自底向上的位图bmi.bmiHeader.biPlanes = 1bmi.bmiHeader.biBitCount = 24 # 24即3*8 32bmi.bmiHeader.biCompression = BI_RGB # 无压缩# 调用 GetDIBits 获取像素数据ret = self.gdi32.GetDIBits(hdc_src, bmp, 0, height, pixel_data, ctypes.byref(bmi), DIB_RGB_COLORS)if ret == 0:print("GetDIBits failed:", ctypes.WinError())# 恢复设备上下文self.gdi32.SelectObject(hdc_dest, old_bmp)# 删除内存设备上下文self.gdi32.DeleteDC(hdc_dest)# 释放桌面窗口的设备上下文self.user32.ReleaseDC(hwnd, hdc_src)# bmp已经被处理,现在删除它self.gdi32.DeleteObject(bmp)return pixel_dataimport time# 控制帧率
class control_frame():def __init__(self):self.start_time = float() # 每次启动时间self.fps = int(10) # fpsself.time_one_frame = 1 / self.fps # 补正时间self.fps_count = 0 # 总帧率self.time_all = time.time() # 启动时间# 启动def start(self):self.start_time = time.time()self.fps_count += 1# 花销def spend(self):spend = time.time() - self.start_timereturn spend# 等待def wait(self):spend = self.spend()true_frame = self.fps_count / (time.time() - self.time_all)if true_frame > self.fps:if self.time_one_frame - spend > 0:time.sleep(self.time_one_frame - spend)s = ScreenshotData()
wait = control_frame()
# 帧率
wait.fps = 20
# 总帧率
frame_number = 100
st = time.time()
for i in range(1, frame_number + 1):wait.start()data = s.capture_screen(0, 0, 20, 20)wait.wait()print("帧率输出:", 1 / ((time.time() - st) / i))
print("花费时间:", time.time() - st)
相关文章:
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

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> …...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...