参考教材:

  • python计算机视觉编程
  • 视觉SLAM十四讲,从理论到实践

针孔照相机模型

针孔摄像机模型(有时称作摄影照相机模型),是计算机视觉中广泛应用的照相机模型。原因是:

这个名字来源于一种简单的照相机,他利用小孔成像原理进行成像,换句话说就是:在光线投影到图像平面前,从唯一一个点经过,这个经过的点就叫做:照相机中心,记做C,如下图所示:

(这张图来自于他人博客:https://blog.csdn.net/Dujing2019/article/details/91691202)

不同之处在于,在现实世界中,我们的图像平面在相机中心之后,而这里,我们为了方便理解,将
点放在了成像平面之前。如果图像坐标轴和三维坐标系中中x、y轴对齐、平行的话,可以得出针孔照相机的投影性质。照相机的光学坐标轴和z轴一致,该投影几何可以简化成相似三角形。在头硬质前通过旋转和平移变换,对该坐标系加入三维点,会出现完整的投影变换。

在针孔照相机中,三维点X为图像点x,它们之间的关系如下所示(都是其次坐标):
λ x = P X \lambda x = PXλx=PX
这里,3*4的矩阵P是照相机矩阵(投影矩阵),齐次坐标中,三维点X的坐标由四个元素组成,X = [ X , Y , Z , W ] X = [X,Y,Z,W]X=[X,Y,Z,W],这里的标量λ \lambdaλ为三维点的逆深度。如果我们想要在齐次坐标中将最后一个值归一化为1那么我们需要他。

照相机矩阵

也就是上面说的P,这个P可以再一步分解:
P = K [ R ∣ t ] P=K[R|t]P=K[R∣t]
在上一个章节中我们对此也有所接触,我们知道,在射影变换矩阵中,左上角四个元素负责控制图像的旋转、缩放等,而右边的两个元素负责控制图像的平移。这里也是同理。

但不同的是,这里同的是,这里多出了一个矩阵K,这个矩阵K叫做内标定矩阵,它描述的是照相机投影的性质,标定矩阵仅和照相机自身的情况有关,通常可以写作:
K = [ a f s c x 0 f c y 0 0 1 ] K = \left[

af00sf0cxcy1afscx0fcy001

\right]K=⎣⎡​af00​sf0​cx​cy​1​⎦⎤​

图像平面和照相机之间的焦距为f,当像素数组在传感器上偏斜的时候,需要用到倾斜参数s。大多数情况下,s可以设置为0,也就是说:
K = [ f x 0 c x 0 f y c y 0 0 1 ] K = \left[

fx000fy0cxcy1fx0cx0fycy001

\right]K=⎣⎡​fx​00​0fy​0​cx​cy​1​⎦⎤​
其中,s被我们定成了0,f x = a f y f_x = af_yfx​=afy​,那么话再说回来,f x , f y f_x,f_yfx​,fy​的不同是用a来度量的,它叫做:纵横比例参数。a是在像素元素非正方形的时候使用的,通常情况下,我们可以设置a = 1 a= 1a=1,经过了这些假设,矩阵变成了:
K = [ f 0 c x 0 f c y 0 0 1 ] K = \left[

f000f0cxcy1f0cx0fcy001

\right]K=⎣⎡​f00​0f0​cx​cy​1​⎦⎤​
除了焦距之外,标定矩阵中剩余的唯一参数为光心(也叫主点),他的坐标是:( c x , c y ) (c_x,c_y)(cx​,cy​),它代表着光线坐标轴和图像平面的交点。因为光心通常在图像的中心,并且图像的坐标是从左上角开始的,所以光心的坐标非常接近于图像宽度和高度的一半。

三维点的投影

from scipy import linalg
from numpy import *
from pylab import *class Camera(object):"""表示针孔照相机的类"""def __init__(self, P):"""初始化照相机模型:param P: P = K[R| t]"""self.P = Pself.K = None # 标定矩阵self.R = None # 旋转self.t = None # 平移self.c = None # 照相机中心def project(self, X):"""返回投影到图像视图的点:param X:X (4*n数组)的投影点:return:并且进行坐标归一化"""x = dot(self.P, X)for i in range(3):x[i] /= x[2]return xif __name__ == "__main__":# 载入点points = loadtxt("house.p3d")# 交换维度以读取points = points.swapaxes(0,1)# 齐次坐标points = vstack((points,ones(points.shape[1])))# 设置照相机参数P = hstack((eye(3), array([[0],[0],[-10]])))cam = Camera(P)x = cam.project(points)# 绘制投影figure()plot(x[0], x[1], 'k.')show()

我们使用牛津多视图数据集中的ModelHousing数据集来运行,原书中的下载地址已经过时,如果想要下载,可以在:http://www.robots.ox.ac.uk/~vgg/data/mview/ 进行下载。

为了研究照相机的移动会如何改变投影效果,我们围绕一个随机的三维向量进行增量旋转的投影:

from scipy import linalg
import numpy as np
import matplotlib.pyplot as pltclass Camera(object):"""表示针孔照相机的类"""def __init__(self, P):"""初始化照相机模型:param P: P = K[R| t]"""self.P = Pself.K = None # 标定矩阵self.R = None # 旋转self.t = None # 平移self.c = None # 照相机中心def project(self, X):"""返回投影到图像视图的点:param X:X (4*n数组)的投影点:return:并且进行坐标归一化"""x = np.dot(self.P, X)for i in range(3):x[i] /= x[2]return xdef rotationMatrix(self, a):"""用于描述围绕a轴旋转的三维旋转矩阵:param a: 旋转轴"""R = np.eye(4)R[:3, :3] = linalg.expm([[0, -a[2], a[1]], [a[2], 0, -a[0]], [-a[1], a[0], 0]])return Rif __name__ == "__main__":# 载入点points = np.loadtxt("house.p3d")# 交换维度以读取points = points.swapaxes(0,1)# 齐次坐标points = np.vstack((points,np.ones(points.shape[1])))# 设置照相机参数P = np.hstack((np.eye(3), np.array([[0],[0],[-10]])))cam = Camera(P)x = cam.project(points)# 绘制投影plt.figure()plt.plot(x[0], x[1], 'k.')plt.show()# 创建变换r = 0.05 * np.random.random(3)rot = cam.rotationMatrix(r)# 旋转矩阵和投影plt.figure()for t in range(100):cam.P = np.dot(cam.P, rot)x = cam.project(points)plt.plot(x[0], x[1], 'k.')plt.show()

最终模拟出的图片如下:

上面这张图代表点的运动轨迹,想画它非常简单:

plt.figure()
for t in range(20):cam.P = np.dot(cam.P, rot)x = cam.project(points)plt.plot(x[0], x[1], 'k.')
plt.show()

相当于我们每次都画,但是都不show,最后再一起show出来就可以了。

照相机矩阵的分解

没啥好讲的,贴代码

from scipy import linalg
import numpy as np
import matplotlib.pyplot as pltclass Camera(object):"""表示针孔照相机的类"""def __init__(self, P):"""初始化照相机模型:param P: P = K[R| t]"""self.P = Pself.K = None # 标定矩阵self.R = None # 旋转self.t = None # 平移self.c = None # 照相机中心def project(self, X):"""返回投影到图像视图的点:param X:X (4*n数组)的投影点:return:并且进行坐标归一化"""x = np.dot(self.P, X)for i in range(3):x[i] /= x[2]return xdef rotationMatrix(self, a):"""用于描述围绕a轴旋转的三维旋转矩阵:param a: 旋转轴"""R = np.eye(4)R[:3, :3] = linalg.expm([[0, -a[2], a[1]], [a[2], 0, -a[0]], [-a[1], a[0], 0]])return Rdef factor(self):"""将矩阵分解为kRt,P = K[R|t]:return:KRt"""# 分解前半部分K, R = linalg.rq(self.P[:,:3])# 将K的对角线元素设为正值T = np.diag(np.sign(np.diag(K)))if(linalg.det(T)<0):T[1, 1] *= -1self.K = np.dot(K, T)self.R = np.dot(T, R)self.t = np.dot(linalg.inv(self.K), self.P[:, 3])return self.K, self.R, self.tif __name__ == "__main__":# 载入点points = np.loadtxt("house.p3d")# 交换维度以读取points = points.swapaxes(0,1)# 齐次坐标points = np.vstack((points,np.ones(points.shape[1])))# 设置照相机参数P = np.hstack((np.eye(3), np.array([[0],[0],[-10]])))cam = Camera(P)x = cam.project(points)# 绘制投影plt.figure()plt.plot(x[0], x[1], 'k.')plt.show()# 创建变换r = 0.05 * np.random.random(3)rot = cam.rotationMatrix(r)# 旋转矩阵和投影plt.figure()for t in range(20):cam.P = np.dot(cam.P, rot)x = cam.project(points)plt.plot(x[0], x[1], 'k.')plt.show()# 测试新写的函数K = np.array([[1000, 0, 500], [0, 1000, 300], [0, 0, 1]])tmp = cam.rotationMatrix([0, 0, 1])[:3, :3]Rt = np.hstack((tmp, np.array([[50], [40], [30]])))cam = Camera(np.dot(K, Rt))print(K,Rt)print(cam.factor())

你可以观测到,结果与输入并不相同,这是由于为了避免符号的二义性而导致的。

计算中心

照相机的中心是一个三维点,满足约束:P C = 0 PC= 0PC=0,又因为P = K [ R ∣ t ] P = K[R|t]P=K[R∣t],所以有:

KaTeX parse error: No such environment: align at position 9: \begin{̲a̲l̲i̲g̲n̲}̲ K [R|t] C = K…

实现起来就很简单了,当然我们也可以看出来照相机的中心和照相机的标定矩阵是无关的:

def center(self):"""计算、返回摄像机的中心"""if(self.c is not None):return self.cself.factor()self.c = -np.dot(self.R.T, self.t)return self.c

相机标定(张正友标定法)

标定是非常重要的一步,它起到了减少误差的作用。

传统的标定方法虽然可以用于任意的摄像机模型,并且精度较高。但其不足之处在于:过程过于复杂,需要高精度的已知结构信息,在实际应用中的很多场景下无法使用标定块,而基于主动视觉的摄像机自标定技术不能用于摄像机运动未知和无法控制的场景。

张正友标定法是一种介于传统标定方法和自标定方法之间的一种简易标定方法。它的标定方法简单易做,只需要我们打印一张棋盘格就可以了。并且在标定过程中只需要将平面模板按任意角度在摄像机前放置即可。

研究的坐标系

我们主要研究四个坐标系:

其中图像坐标系和像素坐标系非常相似,图像坐标系的原点是相机光轴与成像平面的角点,单位是毫米,而像素坐标系的原点是图像的左上角,像素坐标系的单位是像素。

世界坐标系到相机坐标系的变换

[ X C Y C Z C 1 ] = = [ R t 0 1 ] [ X w Y w Z w 1 ] \left[

XCYCZC1XCYCZC1

\right] = = \left[

R0t1Rt01

\right] \left[

XwYwZw1XwYwZw1

\right]⎣⎢⎢⎡​XC​YC​ZC​1​⎦⎥⎥⎤​==[R0​t1​]⎣⎢⎢⎡​Xw​Yw​Zw​1​⎦⎥⎥⎤​

这个就很简单了,就是一个旋转平移

相机坐标系下的点转换到图像坐标系下

x = f Z c X c x = \frac{f}{Z_c}X_cx=Zc​f​Xc​

y = f Z c Y c y = \frac{f}{Z_c}Y_cy=Zc​f​Yc​

这个也非常的简单,我们可以用矩阵的形式来进行表达:
[ x y 1 ] = . [ f Z C 0 0 0 0 f Z C 0 0 0 0 f Z C 0 ] [ X C Y C Z C 1 ] \left[

xy1xy1

\right] =. \left[

fZC000fZC000fZC000fZC0000fZC0000fZC0

\right] \left[

XCYCZC1XCYCZC1

\right]⎣⎡​xy1​⎦⎤​=.⎣⎢⎡​ZC​f​00​0ZC​f​0​00ZC​f​​000​⎦⎥⎤​⎣⎢⎢⎡​XC​YC​ZC​1​⎦⎥⎥⎤​

图像坐标系到像素坐标系的变换

u = c x + x f x u = c_x+xf_xu=cx​+xfx​

v = c y + y f y v =c_y+yf_yv=cy​+yfy​

[ u v 1 ] = . [ f x 0 c x 0 f y c y 0 0 1 ] [ x y 1 ] \left[

uv1uv1

\right] = . \left[

fx000fy0cxcy1fx0cx0fycy001

\right] \left[

xy1xy1

\right]⎣⎡​uv1​⎦⎤​=.⎣⎡​fx​00​0fy​0​cx​cy​1​⎦⎤​⎣⎡​xy1​⎦⎤​

相机畸变模型

x d i s t o r t e d = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) y d i s t o r t e d = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + 2 p 2 x y + p 1 ( r 2 + 2 y 2 ) x_{distorted} = x(1+k_1r^2+k_2r^4+k_3r^6)+2p_1xy+p_2(r^2+2x^2)\\ y_{distorted} = y(1+k_1r^2+k_2r^4+k_3r^6)+2p_2xy+p_1(r^2+2y^2)xdistorted​=x(1+k1​r2+k2​r4+k3​r6)+2p1​xy+p2​(r2+2x2)ydistorted​=y(1+k1​r2+k2​r4+k3​r6)+2p2​xy+p1​(r2+2y2)

想要知道是枕型畸变的话,那么离中心点越远,偏移量越大,反之则反。

标定了什么?

标定了两个部分:

  • 内参:

    • f x , f y f_x,f_yfx​,fy​:相机焦距
    • c x , c y c_x,c_ycx​,cy​:图像中心点
    • k 1 , k 2 , k 3 , p 1 , p 2 k_1,k_2,k_3,p_1,p_2k1​,k2​,k3​,p1​,p2​:畸变参数
  • 外参:
    • R t RtRt:反映了相机和世界坐标的关系

标定任务

我们可以将我们的标定结果用一个函数来衡量:
Σ i = 1 n Σ j = 1 m ∣ ∣ m i j − m ^ ( A , R i , t i , M j ) ∣ ∣ 2 \Sigma_{i=1}^n\Sigma_{j=1}^m||m_{ij}-\hat{m}(A,R_i,t_i,M_j)||^2Σi=1n​Σj=1m​∣∣mij​−m^(A,Ri​,ti​,Mj​)∣∣2
说人话就是,我们计算出来的参数得到的像素坐标与真实像素坐标的差的平方的和。但是非常遗憾的是我们无法直接对该目标函数进行优化,因为需要优化的参数过多,非常容易陷入局部最优。于是张正友便提出了张正友标定法,该方法通过数值方法找到了一个很好的起始点。

标定原理

我们假设我们的平面处于世界坐标系下Z = 0 Z=0Z=0的平面内。需要注意的是:如果想用张正友标定法,那么就必须尽量保证标定板(也就是那个棋盘格的纸)必须在一个平面上,这样才能满足假设,标定板(标定平面)在世界坐标系下Z = 0 Z=0Z=0平面内。

当然,在满足这个条件的时候,我们就有如下公式:
s [ u v 1 ] = . A [ r 1 r 2 r 3 t ] [ X Y 0 1 ] s \left[

uv1uv1

\right] = . A \left[

r1r2r3tr1r2r3t

\right] \left[

XY01XY01

\right]s⎣⎡​uv1​⎦⎤​=.A[r1​​r2​​r3​​t​]⎣⎢⎢⎡​XY01​⎦⎥⎥⎤​
其中:
A = [ f x 0 c x 0 f y c y 0 0 1 ] A = \left[

fx000fy0cxcy1fx0cx0fycy001

\right]A=⎣⎡​fx​00​0fy​0​cx​cy​1​⎦⎤​
因为这个Z恒为0,所以r3是不会影响结果的,所以我们可以把式子写为:
s [ u v 1 ] = . A [ r 1 r 2 t ] [ X Y 1 ] s \left[

uv1uv1

\right] = . A \left[

r1r2tr1r2t

\right] \left[

XY1XY1

\right]s⎣⎡​uv1​⎦⎤​=.A[r1​​r2​​t​]⎣⎡​XY1​⎦⎤​
在前面的学习中我们知道,如果知道两个平面上的点的映射关系,我们是可以把两个平面之间的单应性变换关系求出来的(如果你不懂可以看看之前的博客,有专门讲单应性变换的)

现在我们可以把式子写为:
s m ~ = A [ r 1 r 2 t ] M ~ s\widetilde{m}= A \left[

r1r2tr1r2t

\right] \widetilde{M}sm=A[r1​​r2​​t​]M
可以将右边的式子合并成:
s m ~ = H M ~ s\widetilde{m}= H \widetilde{M}sm=HM
其中,H = A [ r 1 r 2 t ] H = A \left[

r1r2tr1r2t

\right]H=A[r1​​r2​​t​]这里的H代表内参和外参的乘积构成的矩阵。我们可以通过反推单应性变换将H求出,那么现在的问题在于,如何通过已知的H反求出内参和外参。

我们知道,内参、外参、H之间是知二推一的关系,所以我们就想,如果能将内参求出来,那么就可以很自然的知道外参了。
[ h 1 h 2 h 3 ] = λ A [ r 1 r 2 r t ] \left[

h1h2h3h1h2h3

\right] = \lambda A \left[

r1r2rtr1r2rt

\right][h1​​h2​​h3​​]=λA[r1​​r2​​rt​​]
有以下约束条件:

根据这两个约束条件,经过数学变换,可以得到:
h 1 T A − T A − 1 h 2 = 0 h_1^TA^{-T}A^{-1}h_2 = 0h1T​A−TA−1h2​=0

h 1 T A − T A − 1 h 1 = h 2 T A − T A − 1 h 2 h_1^TA^{-T}A^{-1}h_1 = h_2^TA^{-T}A^{-1}h_2h1T​A−TA−1h1​=h2T​A−TA−1h2​

实际上这两个式子在表达:
r 1 r 2 = 0 r 1 r 1 = r 2 r 2 r_1r_2 = 0\\ r_1r_1 = r_2 r_2r1​r2​=0r1​r1​=r2​r2​
我们用B来表示A − T A − 1 A^{-T}A^{-1}A−TA−1,可以写作:

可以看出来B是一个对称矩阵,所以B的有效元素只有六个:
b = [ B 11 B 12 B 22 B 13 B 23 B 33 ] T b = \left[

B11B12B22B13B23B33B11B12B22B13B23B33

\right]^Tb=[B11​​B12​​B22​​B13​​B23​​B33​​]T
我们可以将b理解为有关于相机内参的未知量,进一步化简可以得到:
h i T B h j = v i j T b h_i^TBh_j = v_{ij}^TbhiT​Bhj​=vijT​b
通过计算可以得到:
v i j = [ h i 1 h j 1 h i 1 h j 2 + h i 2 h j 1 h i 2 h j 2 h i 3 h j 2 + h i 2 h j 3 h i 3 h j 3 ] T v_{ij} = \left[

hi1hj1hi1hj2+hi2hj1hi2hj2hi3hj2+hi2hj3hi3hj3hi1hj1hi1hj2+hi2hj1hi2hj2hi3hj2+hi2hj3hi3hj3

\right]^Tvij​=[hi1​hj1​hi1​hj2​+hi2​hj1​​hi2​hj2​​hi3​hj2​+hi2​hj3​​hi3​hj3​​]T
那么综合上面的信息,我们现在的任务就变成解方程:
[ v 12 T ( V 11 − v 22 ) T ] b = 0 \left[

vT12(V11−v22)Tv12T(V11−v22)T

\right]b =0[v12T​(V11​−v22​)T​]b=0
那么我们至少需要拍三张照片才能将内参矩阵确定出来,当然我们拍的照片越多,我们的抗噪声的能力也就越强。最后,我们根据结果,将内参的值提取出来就可。

然后也可以获得外部参数:

标定工具

有两种:

理想状态下圆形的检测精度较高,因为它能避免特征检测时候的错误。也就是说:中心拟合精度高。但是圆形却存在着偏心差,如下图:

但是相比之下,棋盘格却不存在偏心差。

标定的摄像机位置和数量

可以从这张图中看出来:

等我写个程序再写一个标定的实操博客。

python图像处理笔记-八-针孔照相机模型与照相机标定相关推荐

  1. python图像处理笔记-十二-图像聚类

    python图像处理笔记-十二-图像聚类 学习内容 这一章主要在学习的是聚类算法以及其在图像算法中的应用,主要学习的聚类方法有: KMeans 层次聚类 谱聚类 并将使用他们对字母数据及进行聚类处理, ...

  2. Python图像处理笔记——卷积

    Python图像处理--卷积 一.什么是卷积? 1. 数学定义 2. 引入库 3. python实现对图像的卷积 二.相关与卷积 1. 相关的定义 2. Python实现 扩展阅读 一.什么是卷积? ...

  3. Python学习笔记(八)

    Python基础总结 Python基础总结 Python简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.目前分为Python2.X和Python3.X两个版本,且Python ...

  4. python学习笔记(IO模型)

    1.IO模型介绍: io模型一般有五种: * blocking IO          * nonblocking IO          * IO multiplexing          * s ...

  5. [Python图像处理] 十八.图像锐化与边缘检测之Scharr算子、Canny算子和LOG算子

    该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...

  6. 【懒懒的Python学习笔记八】

    面向对象编程是最有效的编程方法之一,在面向对象编程中,你编写表示现实世界中事物和情景的类,并基于这些类来创建对象.使用类来创建对象被称为实例化. 创建和使用类 使用类可以模拟任何东西.下面的实例编写一 ...

  7. Python学习笔记(八)爬虫基础(正则和编解码)

    知识点 正则 正则匹配url,引用re库,将需要匹配的字段用(.*?)来匹配,可以匹配任何字符串.如果有换行,可以用如下方式解决: 1. ([\s\S]*?) 2. re.findall(reg,ht ...

  8. python学习笔记一——鸭子模型

    1.鸭子模型 在程序设计中,鸭子类型(duck typing)是动态类型的一种风格.在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合&qu ...

  9. python图像处理笔记(六):手动获取坐标标注图像

    引言 之前的两篇文章有提到图片标注,但一个是用yolo算法给识别到的图像加框,一个是根据霍夫变换做直线与圆的检测,本篇想总结一下根据鼠标点击来对图像做一些填充处理的方式. opencv鼠标事件 介绍的 ...

  10. Python图像处理笔记——傅里叶变换

    文章目录 一.前言 二.傅里叶变换在图像中的应用 0. 本文用到的库 1. 图像的傅里叶变换和逆变换 2. 高斯模糊 3. 傅里叶变换频域滤波 (1)低通滤波 (2)高通滤波 (3)带通滤波 一.前言 ...

最新文章

  1. 大数据开发实战:Hive优化实战2-大表join小表优化
  2. AjaxControlTookit中的AutoCompleteExtender位置错位问题 ListSearchExtender不支持中文的问题...
  3. 5.2.3 OS之I/O设备的分配与回收(DCT-COCT-CHCT-SDT)
  4. element table 表格设置max-height 没有出现滚动条,多渲染了一列。
  5. matlab深度学习_matlab使用贝叶斯优化的深度学习
  6. Confluence 6 自定义 Decorator 模板的宏和针对高级用户
  7. Pv6报头结构以及与IPv4的比较
  8. zookeeper的集群配置
  9. unix network programming volume 2 interprocess communications second edition环境搭建出错的处理...
  10. 剑指offer面试题[31]-连续数组的最大和
  11. 2020 CTF暑假夏令营培训Day2 密码学Crypto 部分笔记
  12. 【代码优化】坚持使用Override注解
  13. DBeaver执行SQL脚本
  14. 专业wifi测试软件,专业的WiFi检测工具有哪些?
  15. excel公式编辑器_巧用Excel制作炫酷聚光灯效果,数据查看太方便了
  16. vue拍照功能PC+手机需要的可以看一下
  17. AI萃取的5G咖啡,只有华为能调出这个味道
  18. Nginx源码完全注释(1)ngx_alloc.h / ngx_alloc.c
  19. 已通过认证的微信公众号名字可以改吗?
  20. Android开发之最新Android Studio推送代码到最新GitHub教程 | Android Studio绑定GitHub | AS令牌登录GitHub | 创建GitHub令牌

热门文章

  1. php用什么打开_php文件怎么打开,用什么软件打开php文件
  2. Xray配合awvs漏洞扫描
  3. 133道Java面试题及答案(面试必看)
  4. python爬取喜马拉雅vip音频安卓_Python爬虫:爬取喜马拉雅音频数据详解
  5. 深职院计算机专业宿舍,深圳职业技术学院宿舍怎么样 住宿条件好不好
  6. Teigha 4.0 Net 开发记录
  7. MySQL存数学符号,如何将数学符号保存到mysql或mssql数据库?
  8. C# winform开发的考试系统
  9. 初入门-游戏设计思路拆解
  10. 经典而常用的配乐和背景音乐合集(上)