资源描述
目录
1. 项目的一般过程 1
2. 在构造函数、OnCreate、Initial中初始化的区别: 1
3. 重载和重置的区别: 1
4. c++关键字 1
5. const与define的区别: 2
6. 调试 3
6.1 断点 3
6.2 单步和监控 3
6.3 调用堆栈 3
6.4 日志 3
6.5 通过模块选项卡发现外部模块错误 4
6.6 通过暂停按钮发觉死锁和死循环 4
6.7 断言(assert) 4
7. size_t与ptrdiff_t 4
8. 数据保存 4
9. Opencv中的cvWaitKey用法 5
10. CvMat与IplImage的区别,OPENCV的基本数据类型 5
10.1 矩阵和图像类型 6
10.2 CvMat矩阵结构 6
10.3 IplImage数据结构 7
11. 摄像机标定函数CvCalibrateCamera2()的优化方式 8
1. 项目的一般过程
1)接触到新项目,首先要把理论搞清。
2)看一些旧的文献,主要是加深理论的理解,了解其发展的过程,以免自己犯同样的错误。最好能找到代码
3)查新文献。主要是了解现在的发展趋势,寻找一些新的想法和新的思路。
4)编程实现。可以借助之前人写的代码
2. 在构造函数、OnCreate、Initial中初始化的区别:
时间上,先后顺序不同,构造函数生成本类的对象,但没有产生窗口,OnCreate后窗口誔生, 然后才是视图的OnInitialUpDate,一般在这里对视图的显示做初始化。
3. 重载和重置的区别:
成员函数被重载的特征有:
1) 相同的范围(在同一个类中);
2) 函数名字相同;
3) 参数不同;
4) virtual关键字可有可无。
覆盖的特征有:
1) 不同的范围(分别位于派生类与基类);
2) 函数名字相同;
3) 参数相同;
4) 基类函数必须有virtual关键字
4. c++关键字
void
int
char
float
double
bool
true
false
sizeof
long
short
singed
unsigned
const
inline
switch
case
default
auto
static
extern
register
for
while
if
else
do
break
continue
return
goto
new
delete
private
protected
public
class
struct
enum
union
typedef
template
typename
namespace
using
this
friend
virtual
mutable
explicit
operator
catch
throw
try
and_eq
bitand
bitor
compl
not
not_eq
or
or_eq
xor
and,
asm
export
typeid
volatile
根据其内容可将其细分一下:
基本的数据类型关键字:void, int, char, float, double, bool
类型修饰关键字:long, short, singed, unsigned
布尔型字面值:true, false
非常重要的变量声明修饰符:const, inline
存储类别关键字:auto, static, extern, , register
控制结构关键字:for, while, if, else, do
switch语句关键字:switch, case, default
路径跳转关键字:break, continue, return, goto
动态创建变量关键字:new, delete
长度运算符:sizeof
复合类型关键字:class, struct, enum, union, typedef
与类成员相关关键字:this, friend, virtual, mutable, explicit, operator
派生类继承方式:private, protected, public
模板:template, typename
命名空间:namespace, using
异常处理:catch, throw, try,
各种操作符的替代名:and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq
其他不常用的:asm, export, typeid, volatile
5. const与define的区别:
说明(主要是const)
使用const的一些建议
1 要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
2 要避免最一般的赋值操作错误,如将const变量赋值,具体可见思考题;
3 在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;
4 const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
5 不要轻易的将函数的返回值类型定为const;
6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;
6. 调试
6.1 断点
没有比断点更常用的了,通过点击代码左边边栏或者移动光标到指定行按F9等都可以添加断点。值得注意的是并非每一行都可以添加断点,这个就留给大家去实践中体会吧。
通过菜单》调试》窗口》断点或者直接按Alt+F9可以调出断点选项卡,通过选项卡我们可以设置条件断点、数据断点等。
6.2 单步和监控
调试中除了F5之外,另外更常用的估计是F10、F11了,前者是一次一个语句的执行,或者可以看出一行;而后者如果出现能进入的子过程,那么就会进入子过程。这个请大家找个程序,至少要有函数调用的,当断点触发的时候,请自行体验一下F10和F11的效果你就明白了。说白了,实践才是最好的老师,我负责告诉你有这么个东西。
两个选项卡,局部变量和自动变量,它们都负责显示一些当前断住状态下的变量的值,注意,这些只有在程序中断的时候才有意义。自动变量选项卡并非指auto变量,而是指VS帮我们猜想我们可能感兴趣的一些变量的值,或者函数返回值,而局部变量基本上就是本过程的一些变量的值了。
注意,这些选项卡不仅仅可以用于查看,甚至可以用于你临时修改它们的值,方法就是双击值就可以了,如下图所示:
这时候你可以把它临时改成false都行哈。
大家看到监视1了吗?这个选项卡是留给用户的,如果前面的变量太多你不想用滚动条滚来滚去的看就可以在这里输入要监视的变量了:
这里我检查了当前语言字符串的设置。注意并非什么变量都可以检视,必须是调试器可以为我们推断出来的才行,即调试器知道它的地址是什么。如上图所示有一个特殊的用法$err,hr这个是VS特别的,它的意义相当于让调试器帮你获取GetLastError的值,这在Windows编程的时候非常有用。
对于一些指针类型的变量我们还可以在监视里面对它做强制转型,比如你的函数传递一个void* p进来,但是你知道这次你传递的是一个Data结构体的指针,而调试器是无法知道这个p指向的是Data,所以你可以在监视中输入(Data*)p。这样调试器会自动帮我们把他当做Data结构体的指针来识别。
6.3 调用堆栈
调试过程中调用堆栈实在是太重要了,因为它指出了你的程序是正在处于什么状态,是谁调用了谁:
如果你没有这个选项卡可以通过Alt+7或者调试》窗口》调用堆栈把它调出来。
运行时也可以获取调用堆栈的,这个需要Windows API的帮助,这个请看我写的这个。
6.4 日志
日志有很多种,你可以写一个专门的日志系统来处理日常的日志工作,但是这里我只说把信息数出到VC的输出窗口,像这样:
使用WindowsAPI OutputDebugString来实现,当然你也可以对他做一些封装,在程序中在重要的代码部分记录下日志,这对调试很有帮助,你这样会一眼知道哪里出了问题,甚至你可以把调用对战嵌入到这个包装中去:
6.5 通过模块选项卡发现外部模块错误
假设我们依赖于一个外部库Test.dll,这个DLL在系统目录下面有一个,而在Path路径下面还有一个,而他们的版本不同,甚至只是名字相同而内容完全不同。或者其他一系列的类似的问题,都可以通过模块选项卡来察觉,另外这个模块选项卡还告诉了我们我们依赖了那些外部DLL,这在发布的时候很有用,使得我们可以漏掉需要的DLL。
6.6 通过暂停按钮发觉死锁和死循环
当我们的程序失去响应的时候我们不妨尝试点击调试窗口上面的暂停按钮:
如果中断(暂停)成功那么我们会看到死锁或者死循环的调用堆栈了。
6.7 断言(assert)
assert大家应该很熟悉了吧,这是最直接提供错误信息的方法了。特别的,当我们在调试的时候,调试器会帮助我们定位到断言触发的地方。
7. size_t与ptrdiff_t
size_t是unsigned类型,ptrdiff_t是signed类型,都用于指明数组的长度或差值
8. 数据保存
在图形绘制中,当窗口的焦点或者其他操作时,窗口需要重绘,这样我们需要保存之前的信息,例如绘制图形。建立一个新的类,该类包含了我们需要的保存信息元素,然后用集合类的方式,保存多个信息的地址,当窗口重绘时,在Ondraw函数里面读取集合类中保存的信息,进行窗口重绘。
CStringArray:字符串的集合类
CPtrArray:类似于CObjectArray,可以保存一系列的对象。
在孙鑫视频11(00.28.29)讲述了使用对象和使用指针对象的区别:对象新建以后,是在栈中开辟的空间,我们将其地址保存在集合类中,这样在重绘时,直接按照在集合类中取得的地址去找信息,但是该对象是LButtonDown中创建的,当函数结束,对象也就消失了,内存也相应的被回收,即重绘时我们读不到任何信息。相反的,如果是对象指针,我们为对象new一个内存,该内存是在堆上开辟的,当函数结束时,虽然对象指针消失了,但是堆中的信息还在,我们还可以读到。
注意:通常情况下,我们的对象指针不能在内存回收之前消失,因为如果指针对象消失了,那么它开辟的这部分空间就无法delete掉,只有等到程序关闭才会释放。这并不是我们愿意看到的,所以对象指针消失之前一定要释放内存,即注意对象指针在哪里创建,在其消失前,内存释放。
9. Opencv中的cvWaitKey用法
int cvWaitKey( int delay=0 )
返回值为int型,函数的参数为int型,当delay小于等于0的时候,如果没有键盘触发,则一直等待,此时的返回值为-1,否则返回值为键盘按下的码字;当delay大于0时,如果没有键盘的的触发,则等待delay的时间,此时的返回值是-1,否则返回值为键盘按下的码字。
10. CvMat与IplImage的区别,OPENCV的基本数据类型
OpenCV提供了多种基本数据类型。虽然这些数据类型在C语言中不是基本类型,但结构都很简单,可将它们作为原子类型。可以在“…/OpenCV/cxcore/include”目录下的cxtypes.h文件中查看其详细定义。
数据类型中最简单的就是CvPoint。CvPoint是一个包含integer类型成员x和y的简单结构体。CvPoint有两个变体类型:CvPoint2D32f和CvPoint3D32f。前者同样有两个成员x,y,但它们是浮点类型;而后者却多了一个浮点类型的成员z。
CvSize类型与CvPoint非常相似,但它的数据成员是integer类型的width和height。如果希望使用浮点类型,则选用CvSize的变体类型CvSize2D32f。
CvRect类型派生于CvPoint和CvSize,它包含4个数据成员:x,y,width和height。(正如你所想的那样,该类型是一个复合类型)。
下一个(但不是最后一个)是包含4个整型成员的CvScalar类型,当内存不是问题时,CvScalar经常用来代替1,2或者3个实数成员(在这个情况下,不需要的分量被忽略)。CvScalar有一个单独的成员val,它是一个指向4个双精度浮点数数组的指针。
所有这些数据类型具有以其名称来定义的构造函数,例如cvSize()。(构造函数通常具有与结构类型一样的名称,只是首字母不大写)。记住,这是C而不是C++,所以这些构造函数只是内联函数,它们首先提取参数列表,然后返回被赋予相关值的结构。
各数据类型的内联构造函数被列在下表中:cvPoint(),cvSize(),cvRect()和cvScalar()。这些结构都十分有用,因为它们不仅使代码更容易编写,而且也更易于阅读。假设要在(5,10)和(20,30)之间画一个白色矩形,只需简单调用:
cvRectangle(
myImg,
cvPoint(5,10),
cvPoint(20,30),
cvScalar(255,255,255)
);
表3-1:points, size, rectangles和calar三元组的结构
结构
成员
意义
CvPoint
int x, y
图像中的点
CvPoint2D32f
float x, y
二维空间中的点
CvPoint3D32f
float x, y, z
三维空间中的点
CvSize
int width, height
图像的尺寸
CvRect
int x, y, width, height
图像的部分区域
CvScalar
double val[4]
RGBA 值
cvScalar是一个特殊的例子:它有3个构造函数。第一个是cvScalar(),它需要一个、两个、三个或者四个参数并将这些参数传递给数组val[]中的相应元素。第二个构造函数是cvRealScalar(),它需要一个参数,它被传递给给val[0],而val[]数组别的值被赋为0。最后一个有所变化的是cvScalarAll(),它需要一个参数并且val[]中的4个元素都会设置为这个参数。
10.1 矩阵和图像类型
下图为我们展示了三种图像的类或结构层次结构。使用OpenCV时,会频繁遇到IplImage数据类型,IplImage是我们用来为通常所说的“图像”进行编码的基本结构。这些图像可能是灰度,彩色,4通道的(RGB+alpha),其中每个通道可以包含任意的整数或浮点数。因此,该类型比常见的、易于理解的3通道8位RGB图像更通用。
OpenCV提供了大量实用的图像操作符,包括缩放图像,单通道提取,找出特定通道最大最小值,两个图像求和,对图像进行阈值操作,等等。
图3-1:虽然OpenCV是由C语言实现的,但它使用的结构体也是遵循面向对象的思想设计的。实际上,IplImage由CvMat派生,而CvMat由CvArr派生
在开始探讨图像细节之前,我们需要先了解另一种数据类型CvMat,OpenCV的矩阵结构。虽然OpenCV完全由C语言实现,但CvMat和IplImage之间的关系就如同C++中的继承关系。实质上,IplImage可以被视为从CvMat中派生的。因此,在试图了解复杂的派生类之前,最好先了解基本的类。第三个类CvArr,可以被视为一个抽象基类,CvMat由它派生。在函数原型中,会经常看到CvArr(更准确地说,CvArr*),当它出现时,便可以将CvMat*或IplImage*传递到程序。
10.2 CvMat矩阵结构
在开始学习矩阵的相关内容之前,我们需要知道两件事情。第一,在OpenCV中没有向量(vector)结构。任何时候需要向量,都只需要一个列矩阵(如果需要一个转置或者共轭向量,则需要一个行矩阵)。第二,OpenCV矩阵的概念与我们在线性代数课上学习的概念相比,更抽象,尤其是矩阵的元素,并非只能取简单的数值类型。例如,一个用于新建一个二维矩阵的例程具有以下原型:
cvMat* cvCreateMat ( int rows, int cols, int type );
这里type可以是任何预定义类型,预定义类型的结构如下:CV_<bit_depth> (S|U|F)C<number_of_channels>。于是,矩阵的元素可以是32位浮点型数据(CV_32FC1),或者是无符号的8位三元组的整型数据(CV_8UC3),或者是无数的其他类型的元素。一个CvMat的元素不一定就是个单一的数字。在矩阵中可以通过单一(简单)的输入来表示多值,这样我们可以在一个三原色图像上描绘多重色彩通道。对于一个包含RGB通道的简单图像,大多数的图像操作将分别应用于每一个通道(除非另有说明)。
10.3 IplImage数据结构
从本质上讲,它是一个CvMat对象,但它还有其他一些成员变量将矩阵解释为图像。这个结构最初被定义为Intel图像处理库(IPL)的一部分。IplImage结构的准确定义如例3-10所示。
例3-10:IplImage结构
typedef struct _IplImage {
int nSize;
int ID;
int nChannels;
int alphaChannel;
int depth;
char colorModel[4];
char channelSeq[4];
int dataOrder;
int origin;
int align;
int width;
int height;
struct _IplROI* roi;
struct _IplImage* maskROI;
void* imageId;
struct _IplTileInfo* tileInfo;
int imageSize;
char* imageData;
int widthStep;
int BorderMode[4];
int BorderConst[4];
char* imageDataOrigin;
} IplImage;
我们试图讨论这些变量的某些功能。有些变量不是很重要,但是有些变量非常重要,有助于我们理解OpenCV解释和处理图像的方式。
width和height这两个变量很重要,其次是depth和nchannals。depth变量的值取自ipl.h中定义的一组数据,但与在矩阵中看到的对应变量不同。因为在图像中,我们往往将深度和通道数分开处理,而在矩阵中,我们往往同时表示它们。可用的深度值如表3-2所示。 表3-2:OpenCV图像类型
宏
图像像素类型
IPL_DEPTH_8U
无符号8位整数 (8u)
IPL_DEPTH_8S
有符号 8位整数(8s)
IPL_DEPTH_16S
有符号16位整数(16s)
IPL_DEPTH_32S
有符号32位整数(32s)
IPL_DEPTH_32F
32位浮点数单精度(32f)
IPL_DEPTH_64F
64位浮点数双精度(64f)
通道数nChannels可取的值是1,2,3或4。
随后两个重要成员是origin和dataOrder。origin变量可以有两种取值:IPL_ORIGIN_TL 或者 IPL_ORIGIN_BL,分别设置坐标原点的位置于图像的左上角或者左下角。在计算机视觉领域,一个重要的错误来源就是原点位置的定义不统一。具体而言,图像的来源、操作系统、编解码器和存储格式等因素都可以影响图像坐标原点的选取。举例来说,你或许认为自己正在从图像上面的脸部附近取样,但实际上却在图像下方的裙子附近取样。避免此类现象发生的最好办法是在最开始的时候检查一下系统,在所操作的图像块的地方画点东西试试。
dataOrder的取值可以是IPL_DATA_ORDER_PIXEL或IPL_DATA_ORDER_PLANE,前者指明数据是将像素点不同通道的值交错排在一起(这是常用的交错排列方式),后者是把所有像素同通道值排在一起,形成通道平面,再把平面排列起来。
参数widthStep与前面讨论过的CvMat中的step参数类似,包括相邻行的同列点之间的字节数。仅凭变量width是不能计算这个值的,因为为了处理过程更高效每行都会用固定的字节数来对齐;因此在第i行末和第i+1行开始处可能会有些冗于字节。参数imageData包含一个指向第一行图像数据的指针。如果图像中有些独立的平面(如当dataOrder = IPL_DATA_ORDER_PLANE)那么把它们作为单独的图像连续摆放,总行数为height和nChannels的乘积。但通常情况下,它们是交错的,使得行数等于高度,而且每一行都有序地包含交错的通道。
最后还有一个实用的重要参数—— 感兴趣的区域(ROI),实际上它是另一个IPL/IPP 结构IplROI的实例。IplROI包含xOffset,yOffset,height,width和coi成员变量,其中COI代表channel of interest(感兴趣的通道)。ROI的思想是:一旦设定ROI,通常作用于整幅图像的函数便会只对ROI所表示的子图像进行操作。如果IplImage变量中设置了ROI,则所有的OpenCV函数就会使用该ROI变量。如果COI被设置成非0值,则对该图像的操作就只作用于被指定的通道上了。不幸的是,许多OpenCV函数都忽略参数COI
11. 摄像机标定函数CvCalibrateCamera2()的优化方式
OPENCV中标定的源码详解和改进
9
展开阅读全文