24/11/25 视觉笔记 深度传感器和手势识别
本章的目的是开发一个应用程序,使用深度传感器的输出实时检测和跟踪简单的手势。该应用程序将分析每个已捕捉的帧。并执行以下任务。
手部区域分割:通过分析Kinect传感器的深度图输出,在每一帧中提取用户的手部区域,这是通过阈值化、应用一些形态学操作,并找到相连组件来完成。
手形分析:将通过确定轮廓、凸包和凸缺陷来分析分割后的手部区域的形状。
手势识别:将根据手部轮廓的凸缺陷确定伸展手指的数量,并相应地对手势进行分类(没有伸展的手指时,对应的就是拳头;有5根伸展的手指则对应张开的手)
我们先设计一个函数,该函数将从传感器读取一帧并将其转换为所需的格式,然后返回该帧以及一个成功状态,如下所示:
def read_frame(): -> Tuple[bool,np.ndarray]:
该函数包括以下执行步骤
(1)捕获frame,如果未捕捉到帧,则终止函数
frame,timestamp = freenect.sync_get_depth()
if frame is None:return False,None
sync_get_depth方法可同时返回深度图和时间戳。默认情况下,深度图为11为格式。传感器的后10位用于描述深度,当第一位等于1时,表示距离估计不成功。
(2)将数据标准化为 8 位精度格式是一个好主意,因为11位格式不适用于cv2.imshow的可视化。我们可能要使用一些不同格式返回不同的传感器
np.clip(depth,0,2**10-1,depth)
depth>>=2
- 首先,将
depth
数组中的值限制在0到1023的范围内。 - 然后,将
depth
数组中的每个值除以4,并将结果存储回depth
数组。
(3)最后将图像转化为8位无符号整数数组,并返回结果。
return True,depth.astype(np.uint8)
现在,depth图像可以按以下方式可视化。
cv2.imshow("depth",read_frame()[1])
使用与OpenNI兼容的传感器
在此应用程序中,为了使用兼容OpenNI的传感器而不是Kinect3D传感器,必须完成以下操作
(1)创建一个视频捕获,以连接到与OpenNI兼容的传感器
device = cv2.cv.CV_CAP_OPENNI
capture = cv2.VideoCapture(device)
(2)将输入帧大小更改为标准的视频图形阵列(VGA),分辨率(640*480像素)
capture.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH,640)
capture.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,480)
(3)在前面我们设计了read_frame函数,该函数可使用freenect访问Kinect传感器。为了从视频捕获中读取深度图像,在此必须将read_frame函数修改为以下形式
def read_frame():if not capture.grab():return False,Nonereturn capture.retrieve(cv2.CAP_OENNI_DEPTH_MAP)
这里我们使用了grab和retrieve而不是方法read方法,原因是当我们需要同步一组摄像头或多头摄像机(如Kinect)时,cv2.VideoCapture的read方法不合适。
当我们需要同步一组摄像头时,可以在某个时刻使用grab方法从多个传感器捕获帧,然后使用retrieve方法检索感兴趣的传感器的数据,例如,在你自己的应用程序中,你可能还需要检索BGR帧,这可以通过将cv2.CAP_OPENNI_BGR_IMAGE传递给retrieve方法来完成。
运行应用程序和主函数例程
chapter2.py脚本负责运行该程序,它首先导入以下模块
import cv2
import numpy as np
from gestures import recognize
from frame_reader import read_frame
recognize函数负责识别手势
为了简化手部区域分割任务,可以指示用户将手放在屏幕中央。为了提供视觉帮助
我们创建了draw_helpers函数
def draw_helpers(img_draw:np.ndarray)->None:#为了正确定位手部,绘制一些辅助线height,width = img_draw.shape[:2]color = (0,102,255)cv2.circle(img_draw,(width//2,height//2), 3,color,2)cv2.rectangle(img_draw,(width//3,height//3),(width*2//3,height*2//3),color,2)
所有的繁琐的工作都是由main函数完成的,如以下
def main():for _,frame in iter(read_frame,(False,None)):
#该函数在Kinect(动作捕捉识别器)的灰度帧上进行迭代,并在每次迭代中涵盖以下步骤#使用recognize函数识别手势,还函数将返回已伸展手指的估计数和带注解的BGR彩色图像num_fingers,img_draw = recognize(frame)
#在带注解的BGR图像上调用drwa_helpers函数,以便为手的位置提供视觉帮助draw_helpers(img_draw)
#main函数在带注解的frame上绘制手指的数量,使用cv2.imshow显示结果,并设置终止条件
#如:#输出图像上已经伸展的手指数cv2.putText(img_draw,str(num_fingers),(30,30),#在图像上添加文本显示cv2.FONT_HERSHEY_SIMPLEX , 1,(255,255,255))cv2.imshow("frame",img_draw)
#按ESC退出
if cv2.waitKey(10) ==27:break
接下来实现recognize函数来跟踪手势
实现跟踪手势
recognize函数将处理从原始灰度图像到已识别手势的整个流程。它将返回手指的数量和帧指示图
(1) 通过分析深度图提取用户的手部区域,并返回手部区域蒙版
def recognize(img_gray:np.ndarray) ->Tuple[int,np.ndarray]:#划手部区域segment = segment_arm(img_gray)
(2) 在手部区域蒙版上执行contour分析,然后返回在图像中找到的最大轮廓(contour)和任何缺陷(defects)
#找到分段区域的凸包
#并根据该区域找到凸包
contour,defects = find_hull_defects(segment)
(3) 根据找到的轮廓和凸缺陷,检测图像中伸展的手指的数量(num_fingers).然后,使用蒙版(segment)图像作为模板创建指示图像(img_draw),并使用contour和defects点对其进行注释
img_draw = cv2.cvtColor(segment,cv2.COLOR_GRAY2RGB)
num_fingers,img_draw = detect_num_fingers(contour,defects,img_draw)
(4) 返回估计的手指的伸展数(num_fingers)以及包含注解的输出图像(img_draw)
return num_fingers,img_draw
接下来将讨论如何完成手部区域的分割
了解手部区域的分割
通过组合有手部的形状和颜色的信息,我们可以自动检测手臂,且设计其不同的复杂程度,当然在恶劣的环境下或当用户戴手套使,使用肤色作为在视觉场景中发现手的决定性特征可能会严重失败。或者可以通过深度图中的形状来识别用户的手
找到图像中心区域最突出的深度
一旦用户将手部大致放置在屏幕中心,我们就可以开始查找与手位于同一深度平面上的所有图像像素:
(1) 确定图像中心区域的最突出深度值,最简单的方法使仅查看中心像素的depth值。
width,height = depth.shape
center_pixel_depth = depth[width/2,height/2]
(2) 创建一个蒙版,其中深度为center_pixel_depth的所有像素均为白色,而所有其他像素均为黑色
import numpy as np
depth_mask = np.where(depth == center_pixel_depth,255,0).astype(np.uint8)
当然这个方法不是很可靠,因为他会受到以下因素的影响
1.你的手不会完全平行于Kinect传感器放置
2.你的手不会平放
3.Kinect传感器的值会有噪声
因此,你的手的不同区域将具有稍微不同的深度值
segment_arm的方法采用的方法稍微好一些,它将查看图像中心的一个小邻域,并确定深度的中值
(1) 找到图像帧的中心区域
def segment_arm(frame: np.ndarray,abs_depth_dev: int =14)->
np.ndarray:height,width = frame.shape#找到图像帧的中心区域center_half = 10 #half_width of 21 is 21/2-1center = frame[height//2 - center_helf:height//2 + center_half,width//2 - center_half:width//2 + center_half]
(2) 确定深度的中值med_val
med_val = np.median(center)
(3) 现在可以将med_val与图像中所有像素的深度值比较,并创建一个蒙版,其中深度值在特定范围[med_val-abs_depth_dev,med_val + abs_depth_dev]内所有像素均为白色,而所有其他像素均为黑色。
当然由于某些原因,我们需要将像素绘制为灰色
frame = np.where(abs(frame - med_val) <= abs_depth_dev,128,0).astype(np.uint8)
可以看见分割的蒙版并不平滑,特别是,它在深度传感器无法做出预测的点处还包含小孔,接下来我们将介绍如何应用形态学闭合操作来平滑分段蒙版。
应用形态学闭合操作平滑蒙版
分割的一个常见问题就是会出现小孔,这些小孔可以通过形态学上的开放和闭合来解决,开放时,它会从前景中删除小对象,而闭合时则会删除小孔。
这意味着我们可以通过使用较小的3*3像素内核应用形态学闭合来消除蒙版中很小的黑色区域
kernel = np.ones((3,3),np.uint8)
frame = cv2.morphologyEX(frame,cv2.MORPH_CLOSE,kernel)
这行代码使用了 OpenCV 库中的 cv2.morphologyEX
函数来对图像 frame
进行形态学操作,具体来说是闭运算(closing operation)。闭运算是一种常用的图像预处理技术,用于填充前景物体内部的小孔和连接邻近的物体,同时保持物体的总面积大致不变。
可以看见该蒙版仍然包含不属于手臂的区域,例如左侧似乎出现了膝盖,而右侧可能是一些家具。
这些物体恰好和手臂出现在同一深度层上,如果可能的话,现在可以将深度信息与另一个描述符(手形分类器)结合使用,以剔除所有非皮肤信息。
在分割蒙版中查找连接的组件
我们已经知道中心区域属于手,对于这种情况,我们可以简单地应用cv2.floodfill来查找所有连接在一起的图像区域。
在此之前,我们要确定floodfill填充的种子点属于正确的蒙版区域。这可以这可以通过种子点分配一个分配128灰度值来实现,但是,我们还想确保中心的像素不会·由于任何巧合而位于形态操作无法闭合的空腔内。
因此,我们可以设置一个很小的7*7像素区域,其灰度值为128.
samll_kernel = 3
frame[height // 2 - small_kernel:height // 2 + small_kernel,width // 2 - small_kernel:width // 2 + small_kernel] = 128
因为洪水填充FloodFilling以及形态学操作具有潜在的危险,因此opencv需要指定一个蒙版,以免洪水淹没整幅图像。此蒙版的宽度和高度必须比原始图像多2个像素,并且必须与cv2.FLOODFILL_MASK_ONLY标志结合使用。
将洪水填充限制在图像的一个很小的区域或特定轮廓可能非常有帮助,这样就不必连接两个本来据不应该连接的相邻区域。
尝试将mask完全设为黑色
mask = np.zeros((height + 2,width + 2),np.uint8)
然后将泛填充应用于中心像素,并将所有连接区域绘制为白色。
flood = frame.copy()
cv2.floodFill(flood,mask,(width // 2,height // 2),255,flags = 4| (255<<8))
到了这里就理解为什么要灰色蒙版了,现在我们有一个蒙版,其中包含白色区域(手臂和手)、灰色区域(除手臂和手之外其他在同一深度平面上的物体或对象)和黑色区域(所有区域),有了这些设置后,我们可以轻松应用一个简单的二进制threshold(阈值) 来仅突出显示预分段深度平面的相关区域。
ret , flooded = cv2.threshold(flood,129,,255,cv2.THRESH_BINARY
#进行二值化处理
生成的蒙版将如图
接下来将生成的分割蒙版返回到redognize函数,在其中它将用作find_hull_defects函数的输入,以及用于绘制最终输出图像(img_draw)的画布。
find_hull_defects 函数将用于分析手的形状,以便检测与手相对应的图缺陷。
执行手形分析
确定分割后的手部轮廓
使用cv2.findContours,该函数作用在二进制图像上,并返回被认为是轮廓的一部分的一组点。
由于图像中可能存在多个轮廓,因此可以检索轮廓的整个层次结构。
检测图像中轮廓的凸包缺陷函数
def find_hull_defects(segmnet: np.ndarray) -> Tuple[np.ndarray,np.ndarray]:contours,hierarchy = cv2.findContours(segment,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
凸包绘制原理:Graham 扫描法
- 首先选择 y 方向上最低的点作为起始点
p0
。 - 然后以
p0
为原点,建立极坐标系,做逆时针极坐标扫描,依次添加凸包点p1
,p2
...pn
(排序顺序根据极坐标角度大小)。 - 若当前扫描点与下一个点构成的直线为逆时针转向,且转角大于 180°,则将该点添加到凸包点集合,否则忽略。
我们不知道要寻找哪个轮廓,因此必须要做一个假设来清理轮廓结果。
max_contour = max(contours, key=cv2.contourArea)
我们找到的轮廓可能仍然有太多的角,为了解决该问题,可以用相似的contour近似轮廓,近似值小于轮廓周长的1%
epsilon = 0.01 * cv2.arcLength(max_contour,True)
max_contour = cv2.approxPolyDP(max_contour,epsilon,True)
个过程的目的是减少轮廓点的数量,同时保持轮廓的基本结构。
查找轮廓区域的凸包
凸包基本上是轮廓区域的包裹,可以直接从最大轮廓线(max_contour)获得凸包。
hull = cv2.convexHull(max_contour,returnPoints = False)
围绕分段的手部区域以黄色线绘制凸包
寻找凸包的凸缺陷
可以通过查看最大轮廓(max_contour)和相应的凸包(hull)来发现这些凸缺陷
defects = cv2.convexityDefects(max_contour,hull)
返回最大轮廓和凸包
return max_contour,defects
现在我们找到了凸缺陷,接下来需要了解如何使用凸缺陷执行手势识别。
执行手势识别
接下来我们将区分不同凸缺陷情形。
要区分凸缺陷的原因,技巧是查看凸缺陷中距凸包点最远的点与凸缺陷的起点和终点之间的角度。
我们需要一个实际函数来计算两个任意值之间的角度
def angle_rad(v1,v2):return np.arctan2(np.linalg.norm(np.cross(v1,v2)),np.dot(v1,v2))
此方法使用叉积来计算角度
计算两个向量v1,v2之间的角度的标准是计算它们的点积,然后将其除以v1的范数和v2的范数,但是此方法有两个缺点:
1.如果v1 的范数或v2的范数为零,则必须手动避免被零除。
2.对于小角度,该方法返回的结果相对不太准确。
类似的我们提供了一个简单的函数来将角度从度转换为弧度
def deg2rad(angle_deg):return abgle_deg/180.0*np.pi
接下来我们将讨论如何根据伸出的手指迭代数量对手势进行分类。
根据伸出的手指数对手势进行分类
剩下要做的是根据伸出的手指实例的数量对手势进行分类
def defect_num_fingers(contour:np.ndarray,defects:np.ndarray,img_draw:np.ndarray,thresh_deg:float=80.0)->
Tuple[int,np.ndarray]:
该函数接受检测到的轮廓,凸缺陷,用于绘图的画布以及可以用作阈值的临界角度,以判断凸缺陷是否由伸出的手指引起。
(1) 关注一下特殊情况,如果没有发现任何凸缺陷,则意味着我们可能在凸包计算过程中犯了一个错误,或者帧中根本没有伸出的手指,因此返回0作为检测到的手指的数量。
if defects is None:return [0,img_draw]
(2) 我们还可以进一步拓展这一思路,由于手臂通常比手或拳头更苗条一些。因此可以假定手的几何形状总是会产生至少两个凸缺陷。因此,如果没有其他缺陷,则意味着没有伸展的手指。
if len(defects)<=2:return [0,img_draw]
(3) 现在我们已经排除了所有特殊情况,接下来就可以开始计算真手指了。如果有足够数量的缺陷,我们将在每对手指之间发现一个缺陷,因此获得准确的数量(num_finger),我们应该从1开始计数
num_fingers = 1
(4) 我们开始遍历所有缺陷,对于每个凸缺陷,我们将提取3个点并绘制其凸包以用可视化
#凸缺陷形状(num_defects,1,4)
for defect in defects[:,0,:]:#每个凸缺陷都是一个包含4个整数的数组#前3个分别是起点。终点和最远点的索引start,end,far = [contour[i][0] for i in defect[:3]]#绘制凸包cv2.line(img_draw,tuple(start),tuple(end),(0,255,0),2)
(5) 计算从far到start以及从far到end的两条边之间的角度,如果角度小thresh_deg度数,则意味着正在处理的凸缺陷很可能是由伸出的手指引起的。在这种情况下,我们要递增检测到的手指的数量,并用绿色绘制该点。否则就用红色绘制。
#如果角度小于thresh_deg度数
#则凸缺陷属于两根伸出的手指
if angle_rad(start - far,end - far) < deg2rad(thresh_deg):#手指数递增1num_fingers += 1#将点绘制为绿色cv2.circle(img_draw,tuple(far),5,(0,255,0),-1)
else:#将点绘制为红色cv2.circle(img_draw,tuple(far),5,(0,0,255),-1)
(6) 遍历所有凸缺陷
return min(5,num_fingers),img_draw
我们的应用程序能够在各种手部形状中检测到正确的伸展手指数量。伸展手指之间的缺陷点可以通过该算法轻松分类,而其他缺陷点则可以成功忽略。
相关文章:

24/11/25 视觉笔记 深度传感器和手势识别
本章的目的是开发一个应用程序,使用深度传感器的输出实时检测和跟踪简单的手势。该应用程序将分析每个已捕捉的帧。并执行以下任务。 手部区域分割:通过分析Kinect传感器的深度图输出,在每一帧中提取用户的手部区域,这是通过阈值…...

迄今为止的排序算法总结
迄今为止的排序算法总结 7.10 迄今为止的排序算法总结复杂度和稳定性时间复杂度测试程序sortAlgorithm.hsortAlgorithm.cpptest.cpp 时间复杂度测试结果 7.10 迄今为止的排序算法总结 复杂度和稳定性 排序算法平均情况最好情况最坏情况稳定性空间复杂度选择排序O(n^2)O(n^2)O…...

HTML和CSS 表单、表格练习
HTML和CSS 表格练习 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>HTML表格练习</title>…...

H5流媒体播放器EasyPlayer.js网页直播/点播播放器如果H.265视频在播放器上播放不流畅,可以考虑的解决方案
随着流媒体技术的迅速发展,H5流媒体播放器已成为现代网络视频播放的重要工具。其中,EasyPlayer.js网页直播/点播播放器作为一款功能强大的H5播放器,凭借其全面的协议支持、多种解码方式以及跨平台兼容性,赢得了广泛的关注和应用。…...

Http 转 https 中 Nginx 的详细配置过程
摘要 本节将简要介绍从 HTTP 到 HTTPS 的配置过程,并完整展示 Nginx 的相关配置信息。 经过两天断断续续的调试,终于将 http 变成 https 了。现在说说这个安装 ssl 证书的过程。 服务器是在某云上。这个过程大致分为三个步骤:申请 ssl 证书、…...

【测试工具JMeter篇】JMeter性能测试入门级教程(二)出炉,测试君请各位收藏了!!!
上篇文章:CSDN 我们介绍了JMeter的一些原理介绍,以及安装配置和启动流程,本文我们就来讲讲JMeter如何使用。 一、JMeter目录结构组成 1. 根目录 Jmeter安装包解压后的根目录如下图: 1.1 backups目录:脚本备份目录&am…...

Otter 安装流程
优质博文:IT-BLOG-CN 一、背景 随着公司的发展,订单库的数据目前已达到千万级别,需要进行分表分库,就需要对数据进行迁移,我们使用了otter,这里简单整理下,otter 的安装过程,希望对…...

一文学会Golang里拼接字符串的6种方式(性能对比)
g o l a n g golang golang的 s t r i n g string string类型是不可修改的,对于拼接字符串来说,本质上还是创建一个新的对象将数据放进去。主要有以下几种拼接方式 拼接方式介绍 1.使用 s t r i n g string string自带的运算符 ans ans s2. 使用…...

【笔记】Linux下编译Python3.10.15为动态库同时正确处理OpenSSL3依赖
之前自己第一次编译Python后发现pip会提示无法使用SSL,后来了解到是自己编译时没有配置OpenSSL。这个过程有点曲折,里面有一个坑,怕忘记于是写博客记录一下。 首先是下载OpenSSL,Python3.10.15支持此时最新版的OpenSSL 3.4.0&…...

Go语言获取客户端真实IP
在一些需求中,服务器需要记录客户端的ip地址,要获取ip地址,则需要有http.Request的对象参数传入,以下代码直接放在util中使用。 文件名:ip_utils.go package utilsimport ("context""github.com/spf1…...

大模型论文速递(11.23-11.25)
BlueLM-V3B 关键词:动态分辨率,图像放大,适应性网格化方法 研究问题:如何改进现有的动态分辨率匹配方法以减少在模型训练和部署中的计算复杂度? 方法: 分析现有动态分辨率匹配算法(如LLaVA-…...

维护在线重做日志(二)
迁移和重命名 可以使用操作系统命令重新定位重做日志,然后使用ALTER DATABASE语句使数据库知道它们的新名称(位置)。这个过程是必要的,例如,如果当前用于一些重做日志文件的磁盘将被删除,或者如果数据文件…...

.net core MVC入门(一)
文章目录 项目地址一、环境配置1.1 安装EF core需要包1.2 配置数据库连接二、使用EF创建表2.1 整体流程梳理2.1 建表详细流程三、添加第一个视图3.1整体流程梳理3.1 添加视图,并显示在web里四、使用EF增加Catogory数据,并且读取数据到页面4.1整体流程梳理4.2 实现五、增加Cat…...

802.11协议
802.11协议是由美国电气和电子工程师协会(IEEE)制定的无线局域网(WLAN)标准。以下是关于802.11协议的详细介绍: 一、定义与背景 定义:IEEE802.11是美国电机电子工程师协会(IEEE)为…...

【Linux】线程ID与互斥、同步(锁、条件变量)
作者主页: 作者主页 本篇博客专栏:Linux 创作时间 :2024年11月24日 线程ID及进程地址空间布局 先看一下这段代码: 运行一下: 运行这个代码之后,我们看到的这个很大的数字就是线程id,然后…...

Android 13 编译Android Studio版本的Launcher3
Android 13 Aosp源码 源码版本 Android Studio版本 Launcher3QuickStepLib (主要代码) Launcher3ResLib(主要资源) Launcher3IconLoaderLib(图片加载,冲突资源单独新建) 需要值得注意的是: SystemUISharedLib.jar 有kotlin和java下的,在 Lau…...

burp功能介绍
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...

Android12 的 Vold梳理
1.代码位置 system/vold/ 路径下,查看bp文件,发现是编译system/vold/main.cpp编译生成可执行文件vold 2.app侧调用代码流程 2.1 整体框架 #mermaid-svg-lqO8phN62rKNW407 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#…...

[pdf,epub]162页《分析模式》漫谈合集01-35提供下载
《分析模式》漫谈合集01-35的pdf、epub文件,已上传至本号的CSDN资源。 如果CSDN资源下载有问题,可到umlchina.com/url/ap.html。 已排版成适合手机阅读,pdf的排版更好一些。 ★UMLChina为什么叒要翻译《分析模式》? ★[缝合故事…...

Vue2教程003:Vue指令之v-bind和v-for
文章目录 2.6 v-bind2.7 图片轮播案例2.8 v-for2.9 图书管理案例 2.6 v-bind 作用:动态设置html的标签属性->src、url、title…语法:v-bind:属性名"表达式" 动态设置img标签的src属性: <body> <div id"app&quo…...

Pathlib操作文件IN Python
系列文章目录 文章目录 目录 系列文章目录 文章目录 前言 一、Pathlib是什么? 二、使用步骤 前言 pathlib 是 Python 标准库中用于操作文件和目录路径的模块,自 Python 3.4 起引入。它提供了一种面向对象的方式处理路径,使路径操作更加简洁、…...

AOC显示器915Sw按键失灵维修记
大家好,我是 程序员码递夫 今天给大家分享的是自己维修老古董AOC液晶显示器按键失灵的的过程,实属DIY记录。 1、引子 家里有台老古董的19寸AOC液晶显示器(型号915Sw), 一直作为我的副显示器陪伴着左右,显示还正常&a…...

霍曼转移方法介绍
霍曼转移方法介绍 背景 在航天工程中,轨道转移是指航天器从一个轨道移动到另一个轨道的过程。为了高效利用燃料并缩短转移时间,科学家们开发了多种轨道转移方法。其中,霍曼转移(Hohmann Transfer)因其燃料效率高、计…...

我的创作之路:机缘、收获、日常与未来的憧憬
目录 前言机缘收获 日常成就一个优化后的二分查找实现 憧憬 前言 每个人的成长旅程都有它独特的轨迹,而我的这段技术创作之路,则源于一次再普通不过的项目分享。 机缘 一切的开始其实是偶然。在一次项目中,我遇到了一个棘手的问题…...

《硬件架构的艺术》笔记(六):处理字节顺序
介绍 本章主要介绍字节顺序的的基本规则。(感觉偏软件了,不知道为啥那么会放进《硬件架构的艺术》这本书)。 定义 字节顺序定义数据在计算机系统中的存储格式,描述存储器中的MSB和LSB的位置。对于数据始终以32位形式保存在存储器…...

AddIPAddress添加临时IP后,socket bind失败
问题描述 在Win10\Win11下使用addIPAddress添加临时IP成功后,立即创建socket,bind失败 if(!m_socket->bind(QHostAddress(m_localIP), listenPort)) {qCritical() << QString("bind error %1").arg(m_socket->errorString());re…...

关于IDE的相关知识之一【使用技巧】
成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于ide使用技巧的相关内容! 关于…...

线性代数在人工智能领域中的实践
一、机器学习中的线性代数应用 在机器学习中,线性代数主要用于构建和训练各种模型,如线性回归、逻辑回归、支持向量机等。这些模型在数据的特征提取、降维处理以及分类等方面发挥了重要作用。 线性回归:线性回归是最简单的机器学习算法之一…...

图片生成视频-右进
右侧进入 ffmpeg -loop 1 -i image.jpg -f lavfi -i colorcblack:s1280x720:d20 -filter_complex "[1:v]formatrgba[bg];[0:v]formatrgba,scale1280:720[img];[bg][img]overlayxif(lt(t,3),W,if(lt(t,8),W-(t-3)*W/5,0)):y(H-h)/2:enablegte(t,3)" -c:v libx264 -t 2…...

3、集线器、交换机、路由器、ip的关系。
集线器、交换机、路由器三者的关系 1、集线器2、交换机(每个交换机是不同的广播域,ip地址起到划分广播域的作用)3、 路由器4、ip地址 所有图片和资料均来源于B站:网络安全收藏家 1、集线器 一开始两台电脑通信就需要网线就可以&a…...