PPF算法-OpenCV三维点匹配(Surface Matching)_opencv ppf-程序员宅基地

技术标签: OpenCV  计算机视觉  Sureface Matching  3D匹配  ICP  

文章目录

参考资料

1. 论文: 3D Object Detection and Localization using Multimodal Point Pair Features 2012
2. 论文: Model Globally, Match Locally: Efficient and Robust 3D Object Recognition 2010
3. 论文:A refined icp algorithm for robust 3-d correspondence estimation 2003
4. 论文:Efficient Variants of the ICP Algorithm 2001
5. PPF3DDetector API
6. ICP API


3D面匹配算法简介
1. 参考资料

1. 论文: 3D Object Detection and Localization using Multimodal Point Pair Features 2012
2. 论文: Model Globally, Match Locally: Efficient and Robust 3D Object Recognition 2010
3. PPF3DDetector API

2. 问题定义

输入 :

​ (1) 要检测目标的3D模型数据 M M M 。可以通过CAD或者从多张RGBD图像中重建。每个数据项应包含3D点的坐标以及其法向量。

​ (2) 要检测的场景的3D数据。通过RGBD传感器获得,即我们有目标场景的RGB(灰度)图 I C I_C IC和深度图 I R I_R IR 。分别由域 Ω C \Omega_C ΩC Ω R \Omega_R ΩR 定义。

目标 :

​ 当场景中存在目标时,准确定位到目标的位置并求得在场景中目标相对于相机的姿态 p p p

方法 :

​ 根据目标的3D模型数据 M M M ,训练一个目标的模型–>在场景中检测3D特征点–>匹配特征点,求得姿态 p 0 p_0 p0 的一个初始估计–>利用ICP(iterative closest point)算法,将 p 0 p_0 p0作为初始输入,对求得的姿态进行进一步的精炼(refine)操作以得到一个更准确的结果 p p p -->输出最后的结果 p p p

这里写图片描述

3. 多模态特征(Multimodal Feature)

参考资料:

论文: 3D Object Detection and Localization using Multimodal Point Pair Features 2012

参考论文[1]将边缘点分为两类。第一类是纹理边缘(texture edge),第二类是几何边缘(geometric edge)。 所谓的纹理边缘即场景内由于目标内部的纹理所产生的边缘,相对的,几何边缘就是场景内由目标到背景的突变所产生的边缘。作者论文中提到:对于3D匹配而言,几何边缘点是一种比较好的特征点,而纹理边缘点对于匹配并没有多少用处,相反反而会带来更大的计算量。因此论文中提到了一种高效的几何边缘点提取方法。所谓的多模态特征,便是基于几何边缘点 e e e 和参考点(reference point) r r r 进行定义和计算的。其中参考点 r r r 通过对模型进行均匀采样获得。
多模态特征对于尺度(scale)变化、沿着视点方向的旋转(rotations of the object around the viewing direction)、以及透视畸变(perspective distortions)具有不变性。因而对于不同的视点,只需要一个模板图片就够了。下面是计算多模态特征的步骤

(1) 投影矫正(Perspective Correction)

示意图如下:
这里写图片描述

将边缘点 e ∈ Ω E \boldsymbol {e \in \Omega_E} eΩE 和参考点 r ∈ Ω R \boldsymbol{ r \in \Omega_R } rΩR 及其法向量 n r \boldsymbol{n_r} nr 重投影到一个新的平面 I ′ \boldsymbol{I^{'}} I 上( e \boldsymbol e e 是从 I C \boldsymbol I_C IC 上选取的点, r \boldsymbol r r 是从 I R \boldsymbol I_R IR 上选取的点)。新的投影平面 I ′ \boldsymbol{I^{'}} I 定义为垂直于向量 v r = r ∣ r ∣ \boldsymbol{v_r = \frac{r}{|r|}} vr=rr 且焦距与原投影平面具有相同的焦距的平面。重投影之后,参考点 r \boldsymbol{r} r 位于新的投影平面的中心。通过重投影这一预处理,使得多模态特征具有上面提到的:沿着视点方向的旋转(rotations of the object around the viewing direction)不变形,以及透视畸变(perspective distortions)不变性。后文中出现的 r \boldsymbol{r} r e \boldsymbol{e} e 都是重投影之后的点。

(2) 几何边缘检测(Geometric Edge Detection)

几何边缘点检测基于两步进行:
a. 在RGB图 I C I_C IC 中利用Canny边缘算子进行边缘检测,求得所有几何边缘的候选点。
b. 对第一步得到的所有候选点,计算其在深度图 I R I_R IR 中沿着该点边缘梯度方向的一条线段上的最大和最小深度值。如果两者深度差值大于一定阈值,则认为该点为几何边缘点,否则为纹理边缘点。(论文中给出深度阈值一般1-3即可)

经过这一步操作后,我们已经得到了场景中经过重投影后的所有几何边缘点 e \boldsymbol{e} e 、参考点 r \boldsymbol{r} r r \boldsymbol{r} r的法线方向 n r \boldsymbol{n}_r nr 以及 r \boldsymbol{r} r 的单位向量 v r = r ∣ r ∣ \boldsymbol{v}_r = \boldsymbol{\frac{r}{|r|}} vr=rr ,下面开始进入多模态特征的计算。(注意,对于所有边缘梯度向外的边缘点,我们将其边缘梯度方向进行重定向,使得所有的边缘梯度方向都是指向物体内部的)

(3) 多模态特征的计算(Calculate Multimodal Feature)

多模态特征是一种4维的点对特征(point pair feature),定义如下:
F ( e , r ) = ( d ( e , r ) , α d , α n , α v ) (1) F(\boldsymbol e, \boldsymbol r) = (d(\boldsymbol e, \boldsymbol r), \alpha _d, \alpha _n, \alpha _v) \tag{1} F(e,r)=(d(e,r),αd,αn,αv)(1)
其中每个特征维度定义如下:
d ( e , r ) = Z ( r ) ∣ e − r ∣ f (2) d(\boldsymbol e, \boldsymbol r) = Z(\boldsymbol r) \frac{|\boldsymbol e - \boldsymbol r|}{f} \tag{2} d(e,r)=Z(r)fer(2)
其中 $f $ 为相机焦距, Z ( r ) Z(\boldsymbol r) Z(r) 为参考点 r \boldsymbol r r 的深度。这样的定义,使得 F ( e , r ) F(\boldsymbol e, \boldsymbol r) F(e,r) 具有缩放不变形。
α d = ∠ ( e d , e − r ) (3) \alpha _d = \angle{(\boldsymbol e_d, \boldsymbol e - \boldsymbol r)} \tag{3} αd=(ed,er)(3)
其中 e d \boldsymbol e_d ed 为边缘的梯度方向。
α n = ∠ ( n r , e − r ) (4) \alpha _n = \angle{(\boldsymbol n_r, \boldsymbol e - \boldsymbol r)} \tag{4} αn=(nr,er)(4)

α v = ∠ ( n r , v r ) (5) \alpha _v = \angle{(\boldsymbol n_r, \boldsymbol v_r)} \tag{5} αv=(nr,vr)(5)

示意图如下:

这里写图片描述

4. 模型描述(Model Description)

参考资料:

论文: Model Globally, Match Locally: Efficient and Robust 3D Object Recognition 2010

这一步,作者参考了上面参考论文中 3.2 Global Model Description 小节的内容,但是特征描述符使用作者自己提出的PPF多模态特征描述符。在离线阶段,我们需要对目标模型在不同视点下显示出来的可见部分的表面进行一个描述。这种描述通过一个hash表来完成。该hash表将每一个量化(离散化)后的多模态特征向量映射到一个具有相似特征向量的特征点对列表中。示意图如下:

这里写图片描述

其中左边为模型表面上具有相近特征向量的点对,右边为对应的hash表,这些点对被放在同一个hash slot中。

5. 投票方法(Voting Scheme)

参考资料:

论文: Model Globally, Match Locally: Efficient and Robust 3D Object Recognition 2010

论文使用一种类似于广义霍夫变换(GHT)的方法,对所有可能的结果进行投票,得到所有满足条件的结果。再对这些结果进行聚类操作得到最好的结果。最后,利用ICP算法对结果进行Refinement操作。

(1) Voting

首先说一下参考论文中提到的一个局部坐标系 ( m i , α ) (\boldsymbol m_i, \alpha) (mi,α) :

这里写图片描述

如上图所示。其定义为:
s i = T s → g − 1 R x ( α ) T m → g m i (6) \boldsymbol s_i = T_{s \rightarrow g}^{-1} R_x(\alpha)T_{m \rightarrow g} \boldsymbol m_i \tag{6} si=Tsg1Rx(α)Tmgmi(6)
其中 T m → g T_{m \rightarrow g} Tmg m i \boldsymbol m_i mi 变换到以原点为起点且法向量 n m \boldsymbol n_m nm 被旋转到 x x x 轴上, T s → g T_{s \rightarrow g} Tsg s i \boldsymbol s_i si 执行同样的操作。这个局部坐标系的意义在于:对于场景中的每一个参考点( s r ∈ S \boldsymbol s_r \in S srS),确定一个位于模型上的与参考点 s r \boldsymbol s_r sr 满足最佳匹配的条件的点 m i \boldsymbol m_i mi 以及它们之间的一个沿 x x x 轴的旋转角度 α \alpha α ,那么场景中的模型的姿态便能由 ( m i , α ) (\boldsymbol m_i, \alpha) (mi,α) 唯一确定。因此,对每一个参考点 s r \boldsymbol s_r sr 而言,它的投票空间即是以所有可能角度 α \alpha α 为横轴,以模型上的所有点 m i \boldsymbol m_i mi 为纵轴的一个2D平面。

投票过程便是对每一个参考点 s r \boldsymbol s_r sr ,计算它与场景中每一个几何边缘点 e i \boldsymbol e_i ei 的PPF特征 F ( s r , e i ) \boldsymbol F(\boldsymbol s_r, \boldsymbol e_i) F(sr,ei) ,再通过模型描述中建立的hash表,找到所有可能的模型上的对应点对 ( m i , m j ) (\boldsymbol m_i, \boldsymbol m_j) (mi,mj) ,计算对应的参数 α i \alpha_i αi 并在投票空间中点 ( m i , α i ) (\boldsymbol m_i, \alpha_i) (mi,αi) 上进行累加。所有点对 ( s r , e i ) (\boldsymbol s_r, \boldsymbol e_i) (sr,ei) 投票完成后,找到投票空间中所有满足阈值条件的点 ( m i , α i ) (\boldsymbol m_i, \alpha_i) (mi,αi) ,作为下一步操作的输入。投票过程示意图如下:

这里写图片描述

(2) Pose Clustering

投票完成后,对于每一个参考点 s r \boldsymbol s_r sr 我们都到了一组满足条件的姿态 p i p_i pi 。对于所有的姿态 P P P ,通过聚类将之分成多个组。对于每一个,计算组内所有姿态的分数加权和作为该组的一个评分。每个姿态的分数即为该姿态在投票环节所得的票数。选取分数最高的组的所有姿态的均值作为最终的结果。

(3) Pose Refinement:

仅仅通过上述步骤得到结果,通常还具有一定的误差,一般旋转角度误差在 1 0 。 10^。 10 内,平移距离误差在模型直径的 0.005 内都算正常范围。因此,对于聚类得到的结果,还需要利用ICP算法进行Refinement操作,从而得到最终的最佳匹配结果。ICP算法细节见下一节。


ICP算法
1. 参考资料

1. 论文:A refined icp algorithm for robust 3-d correspondence estimation 2003
2. 论文:Efficient Variants of the ICP Algorithm 2001
3. ICP API

2. 问题定义

输入:

​ (1) 场景的3D点的坐标集合 A A A

​ (2) 模型的3D点的坐标集合 B B B

目标:

​ 求解一个点对集合 C = { ( i , j ) ∣ a i ∈ A   a n d   b j ∈ B } C = \{(i, j) | \boldsymbol a_i \in A and \boldsymbol b_j \in B\} C={ (i,j)aiA and bjB} ,或者使得两个点集间对应点对某种误差最小的运动模型的参数 R , t \boldsymbol R , \boldsymbol t R,t

方法:

​ Picky ICP 算法。

3. 算法流程

Picky ICP 算法不同于其他ICP算法,该算法采用分层的思想,每次只对输入点集 A A A 中的一部分点进行迭代计算,当算法收敛时,再对下一层的点进行同样的计算,并将当前计算结果作为下次计算的初始值。

(1) 选取控制点(selection of control point)

对输入场景点集,将其分为 h + 1 h+1 h+1 层。第一次选取下标为 2 h 2^h 2h 的倍数的点作为控制点。此后逐层选取下标为 2 h − 1 , 2 h − 2 , . . . , 2 0 2^{h-1}, 2^{h-2}, ..., 2^0 2h1,2h2,...,20 的点作为控制点,直到所有点都被选为控制点。

(2) 计算对应点对(computation of point pair)

对每一个控制点,计算模型点集 B B B 中的最近点,作为其对应点。可以采用k-d树进行加速。同时,对于存在多个对应点对的情况,只保留距离最近的点对。

(3) 删除离群点(reject outliers)

对第二步得到的所有点对,通过点对距离阈值判断的方法,删去一些离群点对,增强算法的鲁棒性。

首先计算所有点对的标准差 σ \boldsymbol \sigma σ ,当 d i s t ( a i , b j ) > S C A L E ∗ σ dist(\boldsymbol a_i, \boldsymbol b_j) \gt SCALE*\boldsymbol \sigma dist(ai,bj)>SCALEσ 时,该点对被认为是离群值,在后续的计算中,该点对被忽略。

(4) 迭代计算模型参数(computation of motion)

利用误差平法和作为误差度量:
( R ′ , t ′ ) = arg ⁡ min ⁡ ( R , t ) ∑ ( i , j ) ∈ C ∣ ∣ b j − R a i − t ∣ ∣ 2 (7) (\boldsymbol R^{'}, \boldsymbol t^{'}) = \mathop{\arg \min}_{(\boldsymbol R, \boldsymbol t)} \sum_{(i, j) \in C} ||\boldsymbol b_j - \boldsymbol R \boldsymbol a_i - \boldsymbol t||^2 \tag{7} (R,t)=argmin(R,t)(i,j)CbjRait2(7)
求解使得该误差最小的 ( R , t ) (\boldsymbol R, \boldsymbol t) (R,t) 。可以利用迭代的方法进行求解。

(5) 迭代停止条件

( R , t ) (\boldsymbol R, \boldsymbol t) (R,t) 的参数变化小于某个阈值或者迭代次数达到最大迭代次数时,停止计算。


运行效果截图

测试平台配置:

​ 内存:8G

​ 处理器:Intel Core i5-7500 3.40GHz x 4

​ 操作系统:Ubuntu 64-bit

输入模型点集大小: 28291

输入场景点集大小:114373

测试结果:

Training costs: 329.72 s
Matching costs: 2.10481 s
Number of matching poses: 20
Performing ICP on 2 poses…
ICP costs: 1.57771 s

截图:

这里写图片描述

其中绿色点云为模型,红色点云为场景,蓝色点云为使用Pose result 0 将模型变换到场景中得到的结果。


重要API
1. 参考资料
(1) OpenCV PPF3DDetector API
(2) OpenCV ICP API
(3) OpenCV Compute Normals API
2. PPF3DDetector API
(1) 创建PPF特征检测器
/**
 * @brief: 创建3D PPF 特征检测器,并指定相关参数
 * @param relativeSamplingStep: 相对于模型直径的采样步长。在对模型进行模型描述建模时,用于
 在模型上采样的步长。值越小,模型越稠密,姿态估计越准确,但是内存要求以及训练时间越长。值越大,
 模型越稀疏,姿态估计精度降低,但是内存要求以及训练时间和匹配时间更短。
 * @param relativeDistanceStep: 相对于模型直径的离散步长。在对模型进行模型描述建模时,用
 于对PPF特征向量进行离散化的步长。值越小,则离散化越精细,哈希表越大,但是哈希表每个bin之间的
 关系越模糊。值越大,细化越粗糙,哈希表越小,但是两个不同PPF特征向量可能会因为过大的步长而被放
 入相同的哈希槽中。默认该值与 'relativeSamplingStep'相同。对于存在较多噪声的场景,该参数可
 以设得较大以提高对噪点的鲁棒性。
  *@param numAngles: 在PPF特征检测的'voting scheme' 步骤中,需要对角度进行离散化,从而得
  以使用GHT算法。角度离散化的区间数即为 'numAngles'。参考论文中建议值为'30',对于存在较多噪
  声的场景,可以将该参数设为 '25' 或 '20' 以提高对噪点的鲁棒性。
 */
cv::ppf_match_3d::PPF3DDetector::PPF3DDetector ( 
    const double 	relativeSamplingStep,
	const double 	relativeDistanceStep = 0.05,
	const double 	numAngles = 30 )

Pyhon: 无 Python 接口
(2) 建立对模型的模型描述
/**
 * @brief: 使用输入的 'Model' 数据,建立一个新的模型
 * @param Model: 模型的3D坐标+法向量点集(Nx6, CV_32F)
 */
void cv::ppf_match_3d::PPF3DDetector::trainModel (const Mat &Model)
    
Python: 无 Python 接口
(3) 匹配
/**
 * @brief: 在提供的场景 'scene' 中,使用以训练的模型进行匹配,并返回匹配得到的所有可能姿态
 * @param scene: 目标场景的3D坐标+法向量点集(Nx6, CV_32F)
 * @param results: 最终求得的姿态列表。
 * @param relativeSceneSampleStep: 相对于场景点集数量的采样步长。如果设为 1.0/5.0,则场
 景点集中的 5-th 的点被用于计算。该参数提供了一种调整算法速度和精度的方法。较大的值可以提高速
 度,但是降低精度。反之,较小的值会提高精度,但降低速度。
 * @param relativeSceneDistance: 相对于模型直径的距离阈值。参数作用类似于训练过程中的 
 'relativeSamplingStep' 参数的作用。
 */
void cv::ppf_match_3d::PPF3DDetector::match	(	
    const Mat & 	scene,
	std::vector< Pose3DPtr > & 	results,
	const double 	relativeSceneSampleStep = 1.0/5.0,
	const double 	relativeSceneDistance = 0.03 )	

Python: 无 Python 接口
3. ICP API
(1) 创建ICP对象
/**
 * @brief: 创建ICP对象
 * @param iterations: 最大迭代次数
 * @param tolerence: 控制ICP算法每次迭代的精度
 * @param rejectionScale: 在ICP算法的 '删除离群点(reject outliers)' 步骤中的scale系数
 * @param numLevels: 金字塔的层数。太深的金字塔层数可以提高计算速度,但最终的精度会降低。过
 于粗略的金字塔,虽然会提高精度,但是在第一次计算时,会带来计算量的问题。一般设在[4, 10]之间内
 较好。
 * @param sampleType: 目前该参数被忽略。
 * @param numMaxCorr: 目前该参数被忽略。
 */
cv::ppf_match_3d::ICP::ICP	(	
    const int 	iterations,
	const float tolerence = 0.05f,
	const float rejectionScale = 2.5f,
	const int 	numLevels = 6,
	const int 	sampleType = ICP::ICP_SAMPLING_TYPE_UNIFORM,
	const int 	numMaxCorr = 1 )		

Python:
<ppf_match_3d_ICP object> =	cv.ppf_match_3d_ICP( iterations[, tolerence[, rejectionScale[, numLevels[, sampleType[, numMaxCorr]]]]]	)
(2) 使用ICP算法对齐场景和模型
/**
 * @brief: 使用 'Picky ICP' 算法对齐场景和模型点,同时返回残差和姿态 
 * @param srcPc/dstPc: 模型/场景3D坐标+法向量集合。大小为(Nx6),且目前只支持 CV_32F 类型。
 场景和模型点数量不用相同。
 * @param residual: 最终的残差
 * @param pose: 'srcPc' 到 'dstPc' 点集 的变换矩阵
 */
int cv::ppf_match_3d::ICP::registerModelToScene	(	
    const Mat & 	srcPC,
	const Mat & 	dstPC,
	double & 	residual,
	Matx44d & 	pose )

Python:
retval, residual, pose = cv.ppf_match_3d_ICP.registerModelToScene(srcPC, dstPC)
4. ComputeNormals API
(1) 计算3D坐标的法向量
/**
 * @brief: 使用平面拟合的方法,计算一个3D点云中任意点的法向量。
 * @param PC: 输入的3D点云。必须为 (Nx3) 或 (Nx6)
 * @param PCNormals: 输出点云。(Nx6)
 * @param NumNeighbors: 平面拟合时考虑的点的数量
 * @param FlipViewpoint: 如果为 'true',则计算得到的法向量会被翻转到指向 'viewpoint' 的
 方向。为 'fasle' 则不进行任何操作
 * @param viewpoint: 视点位置
 */
int cv::ppf_match_3d::computeNormalsPC3d ( 
    const Mat & 	PC,
	Mat & 	PCNormals,
	const int 	NumNeighbors,
	const bool 	FlipViewpoint,
	const Vec3f & 	viewpoint )
    
Python:
retval, PCNormals = cv.ppf_match_3d.computeNormalsPC3d(	PC, NumNeighbors, FlipViewpoint, viewpoint[, PCNormals]	)


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Anderson_Y/article/details/81907119

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签