0%

先提一下本文的发表理由。因为实验需要用到一个DAVIS 346的Event Camera,且项目需要通过Motion Capture(Mocap)得到人体的3D准确标定,所以就研究了一下相机的Intrinsic和绝对Extrinsic标定。令人吃惊的是,网上其实并没有太多关于如何通过实验方法标定得到相对于坐标原点(O点,Origin)的Extrinsic Matrix的讲解,尤其是缺少中文教程。于是我就把参考各种英文资料(主要是一些大学的slides和有关采集Mocap数据集的文献)总结的一些理论推导和实验方法,以及实际能用的代码整理得到了本文。

首先是几张非常重要的Slides,后面都会refer到,可以先自行熟悉下。另外,本篇不是100%从零开始的教程,篇幅限制并无法展开所有的细节,若想深度理解,请自行结合几个大学(CMU,Stanford)相应的Slides一起学习。

image-20221021152707045

image-20221021165722788

Coordinates

  • 在整个相机的投影与校准过程中,一共涉及3个坐标系。它们分别是:
    1. 世界坐标系:以空间中某点为原点建立欧拉坐标系,设定xyz方向后形成的坐标系。
    2. 相机坐标系。该坐标系的原点是相机的焦点。焦点一般在相机内部,也可能落在的相机外部,这取决于focal length。坐标系的指向:x和y就是相平面的横纵坐标方向(相机视角方向),z是与xy平面垂直的方向,亦即镜头指向的前方。
    3. 图像坐标系(也可以分成两个:图像坐标系(m)和像素坐标系(pixel))。值得注意的一点是每个像素并不是真正的一个点,pixel坐标系所代表的整数值是每个像素点的中心。
  • 考虑功能性,还有一个同质坐标系,用于实际运算。
image-20221021144051423

Intrinsic

  • 理想状况下(无Skewness和Distortion),Intrinsic 矩阵Encode的信息有:Focal Length、Image Sensor的长宽(in pixel),每像素代表的米数(pixel/m),也即相机的分辨率。

  • 非理想情况下,Skewness和Distortion也会被放到Intrinsic中。

  • 关于当提高/降低分辨率时候的Intrinsic变化:$k,l,c_x,c_y$都要乘以分辨率提高的系数。

    image-20221026155238653

  • 参见Intrinsic的计算过程,由于计算时已经考虑了目标物体深度对成像位置的影响,所以Intrinsic其实是包含了透视(perspective)信息的。

  • Intrinsic可使相机坐标系转化为图片坐标系。

Extrinsic

  • Extrinsic 可以看作是两个矩阵写在了一起:旋转矩阵R和平移矩阵T。前三列是R,最后一列是T。 其实,虽然经常写作$[R|T]$,但事实上还有一个相似变换S,这个S是个对角线矩阵,对角线上的值为$[S_x, S_y, S_z, 1]$。S直接和R乘在一起,与T无关。

    image-20221026162455644
  • Extrinsic可使世界坐标系转化为相机坐标系。

Skewness and Distortion

  • Skewness指的是相机Sensor的两个轴不垂直,即xy之间有一个小夹角。通常这不会发生,但如果有制造方面的问题,这也是可能的。

  • 相机的Skewness

  • Skewness的解决方法是把这个夹角找到,并在Intrinsic中反映出来。

    image-20221026154924465

  • Distortion包含:

    • Radial Distortion (径向畸变):
    • Tangential distortion (切向畸变):本质上是相平面和相机坐标系存在一个夹角,即“图像Sensor和镜头截面不平行”。
  • 关于Distortion的计算:

    image-20221026163826434

Homogeneous Coordinates

  • 同质坐标的主要用意是把本来在分母上的z(深度)给挪走,以便让投影这个Transformation从non-linear变成Linear。
  • 注意同质坐标虽然在视觉效果上是在原本的坐标(u,v)或(x,y,z)下面加了一个1,但是实际上这个1在欧式坐标系中并不存在。当我们后面列出方程校准时,应该回到原本的欧式坐标系解。

IMU

  • IMU输出三个方向角速度和三个轴向加速度的值,使用时也需要校准。
  • 具体校准方法参见Kalibr和DV,因为我没用上,所以不多展开。

DVS

  • DVS的校准主要分为两种方法:
    • 一种是直接用paired的RGB进行校准,毕竟这里的RGB和DVS share同一组透镜。
    • 如果没有这个RGB,就直接用accumulate的frame做校准。

Methods

解方程直接校准P矩阵

  • 在Paper DHP19: Dynamic Vision Sensor 3D Human Pose Dataset里, 他们采用的方法是:直接在经过Mocap校准的空间中放置一系列Markers,然后在DVS的RGB(APS)输出frame中直接进行手动标注,得到其在image plane中的$(u, v)$坐标, 然后解方程。

    image-20221021155115039
  • 上图中提到了一个点:从投影矩阵计算相机坐标系的原点,即相机的焦点位置的方法:$C=Q^{-1}c_4$。具体的推理其实很简单,主要就靠一个条件公式:$PC=0$,即原点的投影是0。

  • 细节上,他们用了38个Marker,并8次改变它们的位置,通过最小平方法解得最接近的11个P中参数值。这里的最小平方法的意义在于通过增加数据点取平均P值来减小误差。其实11组式子就够了,但这里还是用了$8\times38\times2$个公式,就在于此。

  • 具体的最小平方法介绍及代码:Link

  • 这个全矩阵P其实包含了Camera Intrinsic K, Camera Extrinsic RT, 以及Camera Skewness。

  • 理论:

    image-20221026175034098

    image-20221026175051047
  • 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    # Least Square Calibration for Camera Projection Matrix using Numpy
    def svd_calibration(points_3d, points_2d):
    # points_3d: 3D points in world coordinate
    # points_2d: 2D points in image coordinate
    # return: projection matrix
    assert points_3d.shape[0] == points_2d.shape[0]
    assert points_3d.shape[1] == 3
    assert points_2d.shape[1] == 2
    num_points = points_3d.shape[0]
    A = np.zeros((2 * num_points, 12))
    for i in range(num_points):
    A[2 * i, 0:4] = *(points_3d[i, :]), 1
    A[2 * i, 8:12] = *(-points_2d[i, 0] * points_3d[i, :]), -points_2d[i, 0]
    A[2 * i + 1, 4:8] = *(points_3d[i, :]), 1
    A[2 * i + 1, 8:12] = *(-points_2d[i, 1] * points_3d[i, :]), -points_2d[i, 0]
    U, S, V = np.linalg.svd(A)
    P = V[:,-1].reshape((3, 4))
    return P

    # OR

    # Least Square Calibration for Camera Projection Matrix
    def least_square_calibrate_camera_projection_matrix_np(x,y,z,u,v):
    # x,y,z: 3D points in world coordinate
    # u,v: 2D points in image coordinate
    # return: projection matrix
    assert len(x) == len(y) == len(z) == len(u) == len(v)
    num_points = len(x)
    A = np.zeros((2 * num_points, 12))
    for i in range(num_points):
    A[2 * i, 0:4] = x[i], y[i], z[i], 1
    A[2 * i, 8:12] = -u[i] * x[i], -u[i] * y[i], -u[i] * z[i], -u[i]
    A[2 * i + 1, 4:8] = x[i], y[i], z[i], 1
    A[2 * i + 1, 8:12] = -v[i] * x[i], -v[i] * y[i], -v[i] * z[i], -v[i]
    U, S, V = np.linalg.svd(A)
    P = V[:,-1].reshape((3, 4))
    return P
  • 注意:SVD这里是用于解决Least Squares Problem的,如果直接用np.linalg.lstsq函数的话(b取全0),会解得一个全0矩阵(因为0永远是一个解)。

  • SVD的解法细节:

    image-20221027230723755

  • 解SVD的时候可以选择把P矩阵右下角$P_{(3,4)}$设为1。不设是homogeneous解法,设了之后是inhomogeneous。

kalibr

  • Link

  • Used for:

    1. Multi-Camera Calibration: Intrinsic and extrinsic calibration of a camera-systems with non-globally shared overlapping fields of view
    2. Visual-Inertial Calibration (CAM-IMU): Spatial and temporal calibration of an IMU w.r.t a camera-system along with IMU intrinsic parameters
    3. Multi-Inertial Calibration (IMU-IMU): Spatial and temporal calibration of an IMU w.r.t a base inertial sensor along with IMU intrinsic parameters (requires 1-aiding camera sensor).
    4. Rolling Shutter Camera Calibration: Full intrinsic calibration (projection, distortion and shutter parameters) of rolling shutter cameras.
  • 简单说就是主攻多相机/IMU系统。多个相机多个IMU相机+IMU等。

  • 校准出来的Extrinsic结果并不是相对原点绝对的,而是多个设备间相对的。比如IMU+Cam校准出来的Extrinsic就是IMU相对于Cam坐标的变换。

    引用一段原话

    • T_cn_cnm1
      camera extrinsic transformation, always with respect to the last camera in the chain
      (e.g. cam1: T_cn_cnm1 = T_c1_c0, takes cam0 to cam1 coordinates)
    • T_cam_imu
      IMU extrinsics: transformation from IMU to camera coordinates (T_c_i)
    • timeshift_cam_imu
      timeshift between camera and IMU timestamps in seconds (t_imu = t_cam + shift)
  • 综上所述,Kalibr并不是满足我们需求的校准方案。

DV Calibration

  • Tutorial Link, Code Link
  • 单个多个DVS都可以。
  • 基于Kalibr的方案。
  • 对于单个DVS,校准主要进行的是undistortion,且可以在校准后直接应用于相机后续的图像,让后面的record都不再有失真。
  • 这里的校准可以有效应对之前Upal教授提出的扭曲问题,应在后续操作中应用。

OpenCV Camera Calibration

  • 这个校准会使用chessboard,而关于3d坐标,他们用了棋盘上两个相邻的点的实际距离是已知的这个特性(因为打印的标准棋盘,间距是固定的,如30mm),来提供相应的3D坐标信息。

  • 这个校准会分别输出Intrinsic matrix (mtx), rotation matrix (R, rvecs), translation matrix (T, tvecs), Distortion coefficients (dist)。这些输出可以直接被用来纠偏。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # generate camera matrixes
    ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

    img = cv.imread('left12.jpg')
    h, w = img.shape[:2]
    newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

    # undistort
    dst = cv.undistort(img, mtx, dist, None, newcameramtx)
    # crop the image
    x, y, w, h = roi
    dst = dst[y:y+h, x:x+w]
    cv.imwrite('calibresult.png', dst)
  • 关于OpenCV校准出来的Extrinsic Matrix,由于世界坐标系必定有一个原点,所以它们也是毫无疑问有一个原点的。但这个世界坐标系原点实际上只有参考意义(第一张校准图的左上角棋盘点),并无法直接使用。同时,相机坐标系的原点是相机的焦点,而这个焦点也是几乎不可预知和测量位置的(它可能在相机内部或外部,但校准并不会告诉你这个点位置,所以你也无法通过直接测量相机O点和实际世界O点之间的相对位置来纠正Extrinsic。)

    Link: I believe it used to be the position of the top-left corner of the checkerboard in the first calibration image, but it may have changed. You can visualized it by writing a few lines of code that project point (0,0,0) (in calibrated scene coordinates) in all the calibration images, then plotting its projected image coordinates on top of the image themselves.

    You should really not depend on it being anything meaningful, and instead locate a separate feature in 3D and roto-translate the reference frame to it after calibration.

  • 实际上,不要想通过OpenCV的校准来直接得到有实际意义的Extrinsic,若想得到,请自行用前面提到的Method 1来实际label一些已知3D坐标的Markers对应的2D点,用Least Squares解得。

  • 但是,OpenCV的校准可以提供有效的Distortion Coefficient和Intrinsic,并可直接被用于畸变补偿。

Reference

Blogs/Websites

Slides

Papers

  • [A Flexible New Technique for Camera Calibration](

写在前面

Pytorch-Lightning这个库我“发现”过两次。第一次发现时,感觉它很重很难学,而且似乎自己也用不上。但是后面随着做的项目开始出现了一些稍微高阶的要求,我发现我总是不断地在相似工程代码上花费大量时间,Debug也是这些代码花的时间最多,而且渐渐产生了一个矛盾之处:如果想要更多更好的功能,如TensorBoard支持,Early Stop,LR Scheduler,分布式训练,快速测试等,代码就无可避免地变得越来越长,看起来也越来越乱,同时核心的训练逻辑也渐渐被这些工程代码盖过。那么有没有更好的解决方案,甚至能一键解决所有这些问题呢?

Read more »

数据来源

卫星类型

  1. Sentinel-2:提供混合分辨率的13 Bands MSI。分辨率有$10m \times 10m$,$20m \times 20m$,$60m \times 60m$,bands中心波长从442.3nm到2185.7nm。

    image-20201213154533413
    Read more »

前言:你喜欢的番剧更新了。是你喜欢的字幕组的高清资源。里面没有放肆的圣光和暗牧。尽管它也许没在国内放送。你可以在任何设备上观看它,并且可以无缝切换。电视,手机,iPad,电脑都没有问题。它很快。

这将是本篇介绍的全自动全平台订阅追番系统。

出于各种原因,有许多番剧在B站并找不到。即使开了大会员,从国外驾梯回国,还是无法看到一些喜欢的内容。但同时,各大动漫种子站却提供了几乎无所不包的资源,其中包括新番。但依靠种子追番最常见的问题就是难以track,经常会忘记更新,而且每次都需要经过一个搜索-下载种子-下载-整理-观看的过程,确实还是很劝退的。如何搭建一个易于配置、足够简单、全自动化抓取、下载、串流的追番系统,便成为了一个追番人的核心需求。

下面本文将会带领你认识整套流程,如果你足够经常折腾,大致一个小时之内就可以搭建完成。祝你好运:)

PS: 本文涉及的所有脚本都可以在该GitHub库上找到。

Read more »

TL;DR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Add submodule
git submodule add

# Clone a project with submodules
git clone --recursive

# Update when submodeule remote repo changed
git submodule update --remote

# When cloned without recursive
git submodule init
git submodule update

# Push submodule change to its remote origin master
cd <submodule_name>
git add -A .
git commit -m "xxx"
git checkout <detached branch name/number>
git merge master
git push -u origin master
Read more »

构图法+出镜比例+人物动态=好的构图

常见单人构图

对称构图

主要包含:左右对称,对角线对称

相对比较中规中矩,但是同样的,限制会比较大,也需要更多细节上的不对称来中和构图上的对称。这些不对称元素往往来自于:人物动态、道具、背景元素。

image-20200912154217731
Read more »

日系画风与现实的区别

  • 真实的“三庭五眼”:
    • 三庭:d(发际线, 眉骨) = d(眉骨, 鼻底) = d(鼻底, 下巴)
    • 五眼:眼宽=眼间距=眼睛到面部轮廓的距离
  • 眼睛的长度相对真实比例更长的,并不符合三庭五眼中的五眼比例。而是眼睛宽度和眼间距大体相同且较长,而眼睛两边到脸部轮廓的距离更短。
Screenshot - 2020-06-12 22.46.34
Read more »

Cascade

相当于Progressive Optimization,每个阶段都会输出一个和最终结果形状相同的Matrix,如目标分布的图像、BBox等,然后下一个block的作用则是输入这个Matrix和前面提取的Features,输出Refined后的Matrix,该步骤不断重复。核心是逐步优化。

Read more »

IoU, AP, mAP, mAP@0.5, mAP@[0.5: 0.95], Average mAP

TL;DR

  1. IoU:两个框框重叠部分面积/两个框框合并后的总面积​
  2. AP:绘制Recall-Precision图,经过平滑后曲线的下面全面积。这个图的绘制方法是:按照每个预测结果的Confidence从上往下排列,先只取一个画出图上左上角第一个点,然后是只取前两个,直到取完。
  3. mAP:AP是针对某一个类的,而mAP是把各个类的AP做一个平均。
  4. mAP@0.5:当IoU阈值为0.5时的mAP。
  5. mAP@[0.5:0.95]:COCO要求IoU阈值在[0.5, 0.95]区间内每隔0.05取一次,这样就可以计算出10个类似于PASCAL的mAP,然后这10个还要再做平均。
Read more »