|
文章目录1、光流2、Opencv中光流的实现3、稀疏光流4、密集光流4.1、farneback4.2、lucaskanade_dense4.3、rlof5、涉及到的库5.1、cv2.goodFeaturesToTrack5.2、cv2.calcOpticalFlowPyrLK5.3、cv2.optflow.calcOpticalFlowSparseToDense5.4、cv2.calcOpticalFlowFarneback5.5、cv2.optflow.calcOpticalFlowDenseRLOF参考1、光流光流(OpticalFlow)是计算机视觉和图像处理中的一个重要概念,它描述了连续帧图像中像素点随时间的运动轨迹和速度的二维矢量场。以下是关于光流的详细解释:一、光流的概念定义:光流是指空间运动物体在观察成像平面上的像素运动瞬时速度。它利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息。起源:光流的概念最早由Gibson在1950年提出,用于描述视觉场景中物体的运动信息。稀疏光流:仅计算图像中选定特征点(如角点、边缘点等)的光流。密集光流:计算图像中每个像素点的光流,从而得到整个图像的光流场。二、光流的基本假设光流算法通常基于以下几个基本假设:亮度恒定:即同一物体在不同帧间运动时,其亮度不会发生改变。这是光流法的基本假定,用于推导光流法的基本方程。时间连续或“小运动”:即时间的变化不会引起目标位置的剧烈变化,相邻帧之间位移要比较小。这保证了在短时间内,像素的运动可以被认为是连续的。空间一致性:一个场景中同一表面上邻近的点具有相近的运动,在图像平面上的投影也在邻近区域。这一假设有助于在局部区域内对像素运动进行建模。三、光流的应用场景光流法因其实时性和计算简单的特点,在计算机视觉和机器人视觉中有许多应用场景:视频稳定:通过光流法分析视频帧中像素的运动,可以稳定视频画面,减少运动模糊和震动对视觉感知的影响。目标跟踪:通过分析连续帧图像中物体的运动情况,光流法可以追踪目标的位置和速度,实现对目标的跟踪和监控。动作识别:在人体动作识别中,光流法可以提取人体在连续帧图像中的运动信息,用于动作分析和识别。三维重建:通过分析连续帧图像中物体的运动情况,光流法可以恢复出物体的三维结构和形状,实现物体的三维重建和建模。智能驾驶:在智能驾驶系统中,光流法可以用于车辆感知和环境感知,提取道路和物体的运动特征,帮助车辆做出决策。视觉导航:在机器人的视觉导航中,光流法可以分析连续帧图像中地面和障碍物的运动情况,实现机器人对环境的感知和定位。四、光流的计算方法光流的计算方法多种多样,主要包括以下几种:基于梯度(微分)的方法:通过计算图像序列中像素的灰度梯度来估计光流。基于匹配的方法:包括基于特征和基于区域的方法,通过匹配相邻帧中的特征或区域来计算光流。基于能量(频率)的方法:首先对输入图像序列进行时空滤波处理,然后利用滤波结果来计算光流。基于相位的方法:利用带通滤波器输出的相位特性来确定光流的速度和方向。神经动力学方法:利用神经网络等机器学习方法来模拟生物视觉系统的功能,实现光流的计算。五、总结光流作为计算机视觉和图像处理中的一个重要工具,具有广泛的应用前景。通过分析连续帧图像中像素的运动信息,光流法可以实现对物体运动和环境变化的感知和分析,为智能控制和决策提供支持。随着计算机视觉技术的不断发展,光流法的计算精度和效率将不断提高,其应用场景也将更加广泛。2、Opencv中光流的实现OpenCV提供了一些算法实现来解决稀疏光流任务1)PyramidLucas-Kanade2)SparseRLOF仅使用稀疏特征集意味着我们将不会有不包含在其中的像素的运动信息。使用密集光流算法可以消除这一限制,该算法假定为图像中的每个像素计算一个运动向量。OpenCV中已经实现了一些密集光流算法:1)DensePyramidLucas-Kanade2)Farneback3)PCAFlow4)SimpleFlow5)RLOF6)DeepFlow7)DualTVL13、稀疏光流Lucas-Kanade方法它假设在一个小的窗口内,所有的像素点都有相似的运动#lucas_kanade.pyimportcv2importnumpyasnpdeflucas_kanade_method(video_path):cap=cv2.VideoCapture(video_path)#ShiTomasi角点检测的参数feature_params=dict(maxCorners=100,qualityLevel=0.3,minDistance=7,blockSize=7)#lucaskanade光流算法的参数lk_params=dict(winSize=(15,15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03),)#创建一些随机的颜色color=np.random.randint(0,255,(100,3))#取第一帧并在其中找到角点ret,old_frame=cap.read()old_gray=cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)#返回检测到的角点坐标p0=cv2.goodFeaturesToTrack(old_gray,mask=None,**feature_params)#创建用于绘图的掩模图像mask=np.zeros_like(old_frame)index=0whileTrue:index+=1ret,frame=cap.read()ifnotret:breakframe_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)#计算光流#calcOpticalFlowPyrLK(prevImg,nextImg,prevPts,nextPts[,status[,err[,\\#winSize[,maxLevel[,criteria[,flags[,minEigThreshold]]]]]]])->nextPts,status,errp1,st,err=cv2.calcOpticalFlowPyrLK(old_gray,frame_gray,p0,None,**lk_params)#返回成功跟踪的特征点的位置#哪些点成功跟踪(True)或失败(False)#每个点的错误度量(通常是跟踪的质量或置信度)#选择比较好的点good_new=p1[st==1]good_old=p0[st==1]#画出轨迹fori,(new,old)inenumerate(zip(good_new,good_old)):a,b=new.ravel()#多维数组展开c,d=old.ravel()#多维数组展开mask=cv2.line(mask,(int(a),int(b)),(int(c),int(d)),color[i].tolist(),2)frame=cv2.circle(frame,(int(a),int(b)),5,color[i].tolist(),-1)img=cv2.add(frame,mask)cv2.imshow("frame",img)cv2.imwrite(f"duck_{str(index)}.jpg",img)k=cv2.waitKey(25)&0xFFifk==27:breakifk==ord("c"):mask=np.zeros_like(old_frame)#现在更新之前的帧和之前的点old_gray=frame_gray.copy()p0=good_new.reshape(-1,1,2)if__name__=="__main__":video_path="duck.mp4"lucas_kanade_method(video_path)#pythonlucas_kanade.py123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869原理是检测前帧的角点,然后计算光流,绘制结果展示,分割成多个片段,前后片段是连续的可以观测到还是有些异常值的(比较直的线段)4、密集光流#dense_optical_flow.pyimportcv2importnumpyasnpimportargparsedefdense_optical_flow(method,video_path,params=[],to_gray=False):#读取视频cap=cv2.VideoCapture(video_path)#读取第一帧ret,old_frame=cap.read()#创建HSV并使Value为常量hsv=np.zeros_like(old_frame)hsv[...,1]=255#精确方法的预处理ifto_grayld_frame=cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)index=0whileTrue:index+=1#读取下一帧ret,new_frame=cap.read()frame_copy=new_frameifnotret:break#精确方法的预处理ifto_gray:new_frame=cv2.cvtColor(new_frame,cv2.COLOR_BGR2GRAY)#计算光流flow=method(old_frame,new_frame,None,*params)#编码:将算法的输出转换为极坐标mag,ang=cv2.cartToPolar(flow[...,0],flow[...,1])#使用色相和饱和度来编码光流hsv[...,0]=ang*180/np.pi/2hsv[...,2]=cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)#转换HSV图像为BGRbgr=cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)cv2.imshow("frame",frame_copy)cv2.imshow("opticalflow",bgr)cv2.imwrite(f"duck_{str(index)}.jpg",bgr)k=cv2.waitKey(25)&0xFFifk==27:breakold_frame=new_framedefmain():parser=argparse.ArgumentParser()parser.add_argument("--algorithm",choices=["farneback","lucaskanade_dense","rlof"],required=True,help="Opticalflowalgorithmtouse",)parser.add_argument("--video_path",default="duck.mp4",help="Pathtothevideo",)args=parser.parse_args()video_path=args.video_pathifargs.algorithm=="lucaskanade_dense":method=cv2.optflow.calcOpticalFlowSparseToDensedense_optical_flow(method,video_path,to_gray=True)elifargs.algorithm=="farneback":#OpenCVFarneback算法需要一个单通道的输入图像,因此我们将BRG图像转换为灰度。method=cv2.calcOpticalFlowFarnebackparams=[0.5,3,15,3,5,1.2,0]#Farneback的算法参数dense_optical_flow(method,video_path,params,to_gray=True)elifargs.algorithm=="rlof":#与Farneback算法相比,RLOF算法需要3通道图像,所以这里没有预处理。method=cv2.optflow.calcOpticalFlowDenseRLOFdense_optical_flow(method,video_path)if__name__=="__main__":main()#pythondense_optical_flow.py123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384854.1、farneback输出结果效果还行,显示出来的部分仅为手拿着物体在移动4.2、lucaskanade_dense输出结果这个感觉更稠密4.3、rlof输出结果这个光流不太稳定的样子,噪点比较大5、涉及到的库5.1、cv2.goodFeaturesToTrackcv2.goodFeaturesToTrack用于检测图像中的角点,这些角点通常用于后续的图像跟踪或特征匹配任务函数原型corners=cv2.goodFeaturesToTrack(image,maxCorners,qualityLevel,minDistance,[,corners[,mask[,blockSize[,useHarrisDetector[,k]]]]])1参数说明image:输入图像,应为8位或32位浮点型,单通道图像(灰度图)。maxCorners:返回角点的最大数量。如果实际检测到的角点数超过此值,则只返回最强的前maxCorners个角点。如果设置为0,则返回所有检测到的角点。qualityLevel:角点的最小可接受质量水平,是角点检测算法中用于筛选角点的阈值参数。minDistance:检测到的角点之间的最小欧氏距离。在此距离内的多个角点将被视为同一角点并只保留一个。corners(可选):输出参数,用于存储检测到的角点坐标。如果提供了此参数,则检测到的角点将存储在此数组中。mask(可选):一个与输入图像同样大小的8位单通道图像,用于指定感兴趣区域(ROI)。非零(通常是255)像素位置表示该位置是角点检测的候选区域。blockSize(可选):计算角点时使用的邻域大小,默认为3。该值越大,检测到的角点越稳定,但计算量也越大。useHarrisDetector(可选):一个布尔值,指定是否使用Harris角点检测器。如果为True,则使用Harris角点检测;如果为False(默认值),则使用Shi-Tomasi角点检测。k(可选):Harris角点检测器中的自由参数,仅当useHarrisDetector为True时有效。返回值corners:如果函数调用时未提供corners参数,则该函数将返回检测到的角点坐标,格式为numpy.ndarray,每个角点由(x,y)坐标表示。使用示例importcv2importnumpyasnp#读取图像image=cv2.imread('path_to_image.jpg',0)#0表示以灰度模式读取图像#设置角点检测参数maxCorners=100qualityLevel=0.3minDistance=7#检测角点corners=cv2.goodFeaturesToTrack(image,maxCorners,qualityLevel,minDistance)#如果检测到了角点,则绘制并显示它们ifcornersisnotNone:foriincorners:x,y=i.ravel()cv2.circle(image,(x,y),3,255,-1)cv2.imshow('Corners',image)cv2.waitKey(0)cv2.destroyAllWindows()1234567891011121314151617181920212223注意事项输入图像应为灰度图,因为角点检测通常在灰度空间中进行。qualityLevel和minDistance参数对检测到的角点数量和分布有显著影响,需要根据具体应用场景进行调整。使用mask参数可以指定只在图像的特定区域中检测角点,有助于减少计算量和提高检测精度。blockSize参数和useHarrisDetector参数提供了检测算法的灵活性,可以根据需要选择合适的设置。5.2、cv2.calcOpticalFlowPyrLKcv2.calcOpticalFlowPyrLK是OpenCV库中用于计算两幅图像之间稀疏光流(SparseOpticalFlow)的一个函数,特别是通过Lucas-Kanade方法结合金字塔(PyrLK)来跟踪图像中的特征点。函数原型p1,st,err=cv2.calcOpticalFlowPyrLK(prevImg,nextImg,prevPts,nextPts[,status[,err[,winSize[,maxLevel[,criteria[,flags[,minEigThreshold]]]]]]])1参数说明prevImg:前一帧的图像(8位单通道图像)。nextImg:当前帧的图像(与前一帧同样大小和类型)。prevPts:前一帧图像中的特征点(关键点)数组,数据类型为numpy数组,形状为(N,1,2),其中N是特征点的数量。nextPts:输出参数,表示在当前帧图像中计算出的特征点位置,与prevPts大小相同。如果传递None,则函数会创建一个新的数组来存储结果。status(可选):输出参数,表示每个特征点的跟踪状态。如果某个特征点被成功跟踪,其对应的status值为1,否则为0。如果传递None,则函数会创建一个新的数组来存储状态。err(可选):输出参数,表示每个特征点的错误向量(误差)。在某些情况下,这个参数可能被忽略。如果传递None,则函数会创建一个新的数组来存储错误向量。winSize(可选):搜索窗口的大小,默认值为(21,21)。maxLevel(可选):金字塔的最大层数,0表示不使用图像金字塔。默认值为3。criteria(可选):迭代搜索算法的终止条件。通常为cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,并设置最大迭代次数和epsilon值。flags(可选):操作标志,默认为0。可以使用的标志包括cv2.OPTFLOW_USE_INITIAL_FLOW,表示使用初始估计的点位置。minEigThreshold(可选):测量是否被视为良好特征点的最小特征值。默认值为1e-4。返回值p1:二维点数组,表示在nextImg图像中成功跟踪的特征点的位置。st:与prevPts大小相同的布尔数组,表示哪些点成功跟踪(True)或失败(False)。err:与prevPts大小相同的数组,表示每个点的错误度量(通常是跟踪的质量或置信度)。工作原理cv2.calcOpticalFlowPyrLK函数通过Lucas-Kanade方法结合金字塔(PyrLK)来跟踪特征点。它首先将输入的两帧图像构建成金字塔,然后从金字塔的顶层开始逐层向下计算光流。这种金字塔方法可以提高计算效率并提供更好的光流估计结果。使用示例importcv2importnumpyasnp#读取前一帧和当前帧图像prevImg=cv2.imread('frame1.png',cv2.IMREAD_GRAYSCALE)nextImg=cv2.imread('frame2.png',cv2.IMREAD_GRAYSCALE)#使用Shi-Tomasi角点检测器找到前一帧图像中的关键点prevPts=cv2.goodFeaturesToTrack(prevImg,maxCorners=100,qualityLevel=0.3,minDistance=7,blockSize=7)#设置LK光流算法的参数lk_params=dict(winSize=(15,15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03))#计算光流nextPts,st,err=cv2.calcOpticalFlowPyrLK(prevImg,nextImg,prevPts,None,**lk_params)#筛选出被成功跟踪的点good_new=nextPts[st==1]good_old=prevPts[st==1]#绘制跟踪结果(省略具体绘制代码)12345678910111213141516171819202122注意事项输入图像应为灰度图,因为光流计算通常在灰度空间中进行。prevPts中的特征点应使用适当的特征检测算法(如Shi-Tomasi角点检测器)获得。5.3、cv2.optflow.calcOpticalFlowSparseToDense在OpenCV中,cv2.optflow.calcOpticalFlowSparseToDense是一个用于计算从稀疏特征点到密集光流场的函数。这个函数结合了稀疏特征匹配和光流算法的优点,通过先找到一组稀疏的关键点(如使用Shi-Tomasi角点检测器),然后利用这些稀疏点来估计整个图像的光流场。函数原型flow=cv2.optflow.calcOpticalFlowSparseToDense(prevImg,nextImg,prevPts,nextPts,density=1,sigma_dist=0.05,sigma_color=0.1,patchSize=21,maxLevel=2)1参数说明prevImg:上一帧图像,类型为np.uint8或np.float32。nextImg:当前帧图像,与prevImg具有相同的类型和尺寸。prevPts:上一帧图像中检测到的稀疏点集,类型为np.float32,形状为(N,1,2),其中N是点的数量,每个点是一个(x,y)坐标。nextPts:当前帧图像中对应prevPts的稀疏点集,同样为np.float32类型,形状为(N,1,2)。density:光流场的密度,控制输出光流场的平滑程度。较高的值意味着更密集的光流场,但也可能包含更多的噪声。默认值为1.0。sigma_dist:在计算光流时,空间邻近性的高斯核标准差。较小的值意味着更关注局部邻域。sigma_color:在计算光流时,颜色相似性的高斯核标准差。较小的值意味着颜色变化对光流的影响更大。patchSize:用于计算光流时考虑的邻域大小。较大的值可以提高算法的鲁棒性,但也会增加计算量。maxLevel:金字塔的最大层数。算法会在多个尺度上计算光流,以提高对大尺度运动的处理能力。返回值flow:密集光流场,类型为np.float32,形状为(height,width,2),其中每个像素点(x,y)的光流是一个(u,v)向量,分别表示在水平和垂直方向上的运动分量。示例代码importcv2importnumpyasnp#假设prevImg,nextImg,prevPts,nextPts已经准备好#...#计算密集光流场flow=cv2.optflow.calcOpticalFlowSparseToDense(prevImg,nextImg,prevPts,nextPts,density=1.0,sigma_dist=0.05,sigma_color=0.1,patchSize=21,maxLevel=2)#可视化光流场hsv=np.zeros_like(nextImg)hsv[...,1]=255hsv[...,0]=0.5*flow[...,0]+0.5hsv[...,2]=0.5*flow[...,1]+0.5hsv=cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)img=cv2.add(nextImg,hsv)#显示结果cv2.imshow('OpticalFlow',img)cv2.waitKey(0)cv2.destroyAllWindows()1234567891011121314151617181920215.4、cv2.calcOpticalFlowFarnebackcv2.calcOpticalFlowFarneback是OpenCV库中用于计算两个连续帧之间稠密光流的函数。该函数基于GunnarFarneback的算法,该算法在计算速度和准确性之间取得了良好的平衡。函数原型cv2.calcOpticalFlowFarneback(prevImg,nextImg,pyr_scale,levels,winsize,iterations,poly_n,poly_sigma,flags[,flow])1参数说明prevImg:前一帧的图像(灰度图像)。类型为np.uint8。nextImg:下一帧的图像(灰度图像),与前一帧保持同样的格式和尺寸。类型为np.uint8。pyr_scale:指定图像金字塔上下两层之间的尺度关系(
|
|