OpenCV cvCalibrateCamera2函数

最近弄相机标定,然后发现OpenCV中自带的函数cvCalibrateCamera2,它用的是张氏标定的方法,具体过程我也没多懂,但是基本明白,其实它就是几个矩阵neng来neng去。哎……
关于cvCalibrateCamera2这个函数,官方的参数是:

1
2
3
4
5
6
7
8
9
10
11
12
public:
static void cvCalibrateCamera2(
IntPtr object_points,
IntPtr image_points,
IntPtr point_counts,
MCvSize image_size,
IntPtr intrinsic_matrix,
IntPtr distortion_coeffs,
IntPtr rotation_vectors,
IntPtr translation_vectors,
int flags
)

其中各个参数的作用:
object_points (IntPtr)
官方:The joint matrix of object points, 3xN or Nx3, where N is the total number of points in all views
我的:这个就是张氏标定里面的世界坐标,3代表世界坐标的x,y,z。一般都令z=0,因为世界坐标是图片中棋盘角点的位置,所以,世界坐标一般都是“算”出来的,从左上角的(0,0)开始,只要知道实际上棋盘格子中每个小格子的宽度,就能得到整个棋盘点的坐标。N的话要结合另一个参数point_counts,比如说有10张标定的照片,每张的角点数是20,那么N就是20*10

image_points (IntPtr)
官方:The joint matrix of corresponding image points, 2xN or Nx2, where N is the total number of points in all views
我的:这个是由计算机得出来的角点坐标集合,就是计算机识别出来的角点的位置,一般先由cvFindChessboardCorners求出大致坐标,再又cvFindCornerSubPix通过迭代来发现具有子像素精度的角点位置。

前面两个参数其实是对应的,一个世界坐标对应一个图像坐标,如果有多张图片的话,放在同一个矩阵。类似于定义一个queue,然后把第一张图片的角点位置一个一个push进去,然后再第二张图片的角点位置一个一个push进去,一直这样……

point_counts (IntPtr)
官方:Vector containing numbers of points in each particular view, 1xM or Mx1, where M is the number of a scene views
我的:就是每张照片的角点个数,仅此。

image_size (MCvSize)
官方:Size of the image, used only to initialize intrinsic camera matrix
我的:相片的实际大小,一般右键查看相片的属性就能获得

intrinsic_matrix (IntPtr)
官方:The output camera matrix (A) [fx 0 cx; 0 fy cy; 0 0 1]. If CV_CALIB_USE_INTRINSIC_GUESS and/or CV_CALIB_FIX_ASPECT_RATION are specified, some or all of fx, fy, cx, cy must be initialized
我的:获得的相机的内参,就是张氏标定的内参矩阵

distortion_coeffs (IntPtr)
官方:The output 4x1 or 1x4 vector of distortion coefficients [k1, k2, p1, p2]
我的:就是张氏标定里面的畸变参数

rotation_vectors (IntPtr)
官方:The output 3xM or Mx3 array of rotation vectors (compact representation of rotation matrices, see cvRodrigues2).
我的:旋转向量,由于一张照片就有一个旋转角度,所以这里的M表示有多少张照片,如果要第3张的参数就是这个矩阵的第三列

translation_vectors (IntPtr)
官方:The output 3xM or Mx3 array of translation vectors
我的:平移向量,由于一张照片就有一个平移位置,所以这里的M也表示有多少张照片。

flags (Int32)
官方:Different flags
百度百科:
不同的标志,可以是0,或者下面值的组合:

  • CV_CALIB_USE_INTRINSIC_GUESS - 内参数矩阵包含fx,fy,cx和cy的初始值。否则,(cx, cy)被初始化到图像中心(这儿用到图像大小),焦距用最小平方差方式计算得到。注意,如果内部参数已知,没有必要使用这个函数,使用cvFindExtrinsicCameraParams2则可。
  • CV_CALIB_FIX_PRINCIPAL_POINT - 主点在全局优化过程中不变,一直在中心位置或者在其他指定的位置(当CV_CALIB_USE_INTRINSIC_GUESS设置的时候)。
  • CV_CALIB_FIX_ASPECT_RATIO - 优化过程中认为fx和fy中只有一个独立变量,保持比例fx/fy不变,fx/fy的值跟内参数矩阵初始化时的值一样。在这种情况下, (fx, fy)的实际初始值或者从输入内存矩阵中读取(当CV_CALIB_USE_INTRINSIC_GUESS被指定时),或者采用估计值(后者情况中fx和fy可能被设置为任意值,只有比值被使用)。
  • CV_CALIB_ZERO_TANGENT_DIST – 切向形变参数(p1, p2)被设置为0,其值在优化过程中保持为0。

Intel 8086 内部寄存器

因为老是忘记8086 CPU的内部寄存器,在此备忘一下。

1. 通用寄存器

有AX、BX、CX、DC四个通用寄存器,每一个都是16位,但是可以拆成2个8位寄存器使用,分别用AH、AL……来表示。

当然,4个通用寄存器除了作为通用寄存器使用外,还有各自的习惯用法。

  • AX(AH AL)又称累加器,常用于存放算术逻辑运算中的操作数,林外所有的I/O指令都使用累加器与外设接口传送数据,有些指令指定只能使用AX(AL)寄存器,如乘法、除法、I/O等指令。
  • BX(BH BL)又称基址寄存器,常用来存放访问内存时的基地址或用做间接寻址时的地址寄存器。
  • CX(CH CL)又称计数寄存器,在循环和串操作指令中用做计数器,指令执行后CX寄存器中的内容会自动改变。
  • DX(DH DL)又称数据寄存器,在I/O指令中用来存放端口的地址,在乘法指令中作为辅助寄存器

2. 4个专用寄存器

  • SP堆栈指针寄存器:它用在堆栈中存放栈顶偏移指针,永远指向堆栈的栈顶。
  • BP基址指针寄存器:一般也用来存放访问内存时的基地址。
  • SI源变址寄存器、DI目的变址寄存器:它们常常用在变址寻址方式中。

每一个专用寄存器也都是16位

3. 4个段寄存器

  • CS代码段寄存器:存放当前程序所在段的段基址(段首址)。
  • DS数据段寄存器:存放当前程序所在数据段的段基址。
  • SS堆栈段寄存器:存放当前程序所在堆栈段的段基址。
  • ES附加段寄存器:存放当前程序所用辅助数据段的段基址。

同样,都是16位的

4. 指令指针寄存器IP

IP寄存器用于存放下一条执行指令的偏移地址。CPU取指令总是以CS为段基址,以IP为段内偏移地址。当CPU从CS段且偏移地址为(IP)的内存但愿中取出指令代码的一字节后,IP会自动加1,从而指向代码的下一字节,用户不能直接访问IP寄存器。

5. 标志寄存器FR

FR也是16位寄存器,但是只能使用其中的9位,包括6个状态标志位和3个控制标志位。状态标志位记录前面算术逻辑运算结果的特征,控制标志是用户自己通过指令设置的。对其后的操作产生控制作用。

OF DF IF TF SF ZF AF PF CF
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
  • 6个状态标志
    • SF符号标志,和运算结果最高位相同,若SF=1,表示结果是负数;SF=0,表示结果为正数,对于无符号数,SF无意义
    • ZF零标志,算术运算的结果为0时,ZF=1,否则ZF=0
    • PF奇偶标志,若运算结果的低8位中1的个数为偶数时,PF=1
    • CF借进位标志,进行加减法操作时,若最高位有进位,CF=1,循环指令和部分位操作指令也会影响此标志
    • AF辅助进借位标志,加减法运算时,如果低半字节向高半字节(第三位向第四位)借进位,则AF=1,一般在BCD码运算中作为是否进行十进制调整的判断依据
    • OF溢出标志,当带符号运算过程中结果超出最大范围,OF=1
  • 3个控制标志
    • DF方向标志,串操作指令中,若DF=1则串操作过程中地址值不短减小的方向进行
    • IF终端允许标志,若IF=1,则CPU可以响应可屏蔽中断请求
    • TF跟踪标志,若TF=1,则CPU处于单步执行指令的工作放式,每执行一条指令,自动产生一次单步终端。

标志位在DEBUG中的显示形式:

标志名 标志为1时 标志为0时
OF[Overflow Flag] ov[OVerflow] nv[Not oVerflow]
DF[Direction Flag] dn[DowN] up[UP]
IF[Interrupt Flag] ei[Enable I~t] di[Disable I~t]
SF[Sign Flag] ng[NeGative sign] pl[PLus sign]
ZF[Zero Flag] zr[ZeRo] nz[Not Zero]
AF[Auxiliary carry Flag] ac[Auxiliary Carry] na[Not Aux~]
PF[Parity Flag] pe[Parity Even] po[Parity Odd]
CF[Carry Flag] cy[CarrY] nc[Not Carry]

OK,完毕~

Go项目的目录结构

一直搞不懂golang是怎么组织项目目录结构的,后来看了Golang项目目录结构组织总算弄明白一些了。整体来说,golang使用目录+包来管理的,比起python来说确实麻烦一些。具体往后看,本文基于上面的连接进行总结。

1. 准备工作

首先,需要设置一个环境变量GOPATH,简单地说,import的包都在这个变量里面找,包括go get得到的包也都是安装在这里面。

一个GOPATH一般有3个子目录:

src (存放源码,最重要的)
pkg (存放编译后的代码)
bin (存放编译后的可执行文件)

一般来说pkg和bin目录不用我们创建,go build 或者 go install的时候就会自己创建了,golang的安装目录就是一个GOPATH,这里我们添加一个路径到GOPATH,这里引用原文的例子
首先,创建一个GOPATH变量,如果已有就加在后边,用;分开

2. 新建项目

<proj>
  |--<src>
       |--<a>
           |--<a1>
                |--al.go
           |--<a2>
                |--a2.go
       |--<b>
           |--b1.go
           |--b2.go
       |--<c>
           |--c.go
  |--<pkg>
  |--<bin>

各文件代码:
a1.go:

1
2
3
4
5
6
7
package a1

import "fmt"

func PrintA1() {
fmt.Println("a/a1")
}

a2.go:

1
2
3
4
5
6
7
package a2

import "fmt"

func PrintA2() {
fmt.Println("a/a2")
}

b1.go:

1
2
3
4
5
6
7
package b

import "fmt"

func printB1() {
fmt.Println("b.b1")
}

b2.go:

1
2
3
4
5
6
7
8
package b

import "fmt"

func PrintB() {
printB1()
fmt.Println("b.b2")
}

c.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"a/a1"
"a/a2"
"b"
)

func main() {
a1.PrintA1()
a2.PrintA2()
b.PrintB()
}

最后在c.go的目录下go run c.go
结果:

a/a1
a/a2
b.b1
b.b2

若执行go install c则将会同时生成pkg目录和bin目录,且可执行文件在bin目录里面。

3. 总结

golang的项目目录组织结构说起来其实就两个细节:
import 的是目录,从GOPATH里面src目录开始的目录
package 是给函数调用的。
比如c.go 里面,import “a/a1” 指的是GOPATH/scr/a/a1 目录,而c.go里面的a1.PrintA1() 中的a1是a1.go 里面的packae a1,比如说:
如果 a1.go 的路径变成 a/a11a1.go 里面改成 package aaa ,那么 c.go 就变成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"a/a11"
"a/a2"
"b"
)


func main() {
aaa.PrintA1()
a2.PrintA2()
b.PrintB()
}

4. 补充(据说不推荐)

golang的import是可以不依赖GOPATH的,但是据说是不推荐的作法。

1
2
3
|--<a>
|--a.go
|--main.go

a.go:

1
2
3
4
5
6
7
8
9
package a

import (
"fmt"
)


func PrintA() {
fmt.Println("aaa")
}

main.go:

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"./a"
"fmt"
)


func main() {
a.PrintA()
fmt.Println("main")
}

结果:

1
2
aaa
main

可见,golang也是支持相对引入的