2018-09-26 13:22:54    330    0    0

第一步

windows安装anaconda(傻瓜式安装),可以勾选一个自动设置环境变量的东西,省事

依赖的环境

  1. Anaconda3 x64 (with Python 3.5/3.6)
  2. Windows 64位系统(Windows 7 Windows Server 2008 及以上)
  3. CUDA 8 / CUDA 9
  4. cuDNN v5以上
  5. 如果安装了CUDA编译的包,请确保你的电脑有Nvidia的显卡。
  6. 注:这里没有介绍GPU版本的安装方法,如需要的请搜索其他博文。

新建虚拟环境

开始菜单打开Anaconda Prompt(在ubuntu下,直接打开命令终端),在里面输入conda create -n pytorch python=3.6
(根据python版本号设置,版本号可以通过python指令查看)为pytorch创建一个虚拟环境

  1. 添加清华镜像(非必需,当时没添加镜像也成功了,只是网络可能会波动,多试几次,另外下载的时候,最好不要进行任何操作)
  2. conda config --addchannels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  3. conda config --setshow_channel_urls yes

科大镜像(清华已经关闭镜像):

  1. conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
  2. conda config --set show_channel_urls yes
  3. ##现在科大镜像也关闭了,不知何时能开启
  4. ##(想要删除清华源把add改成remove就行。)

腾讯的还可以试试:

  1. https://mirrors.cloud.tencent.com/anaconda/pkgs/free/

镜像源参考:
https://zhuanlan.zhihu.com/p/62899936

激活该环境

打开anaconda navigator,点击environment,可以看到base基础环境,以及刚刚新建的pytorch环境
,

2018-09-25 22:32:14    300    0    0
#1.1什么是线阵传感器.面阵传感器? 线阵CCD工业相机主要应用于工业、医疗、科研与安全领域的图象处理。在机器视觉领域中,线阵工业相机是一类特殊的视觉机器。与面阵工业相机相比,它的传感器只有一行感光元素,因此使高扫描频率和高分辨率成为可能。线阵工业相机的典型应用领域是检测连续的材料,例如金属、塑料、纸和纤维等。被检测的物体通常匀速运动,利用一台或多台工业相机对其逐行连续扫描,以达到对其整个表面均
2018-09-20 20:47:28    452    0    0

使用的是ocv3.30,vs20173

  • 1双击下载好的或者拷好的安装包,选择解压路径后点Extract即可完成解压,解压后会自动生成一个opencv的文件夹
  • 2 此电脑>右键->属性->高级系统设置->环境变量
    找到系统变量中的path变量,双击它,点击新建,将你解压的opencv文件夹中的***opencv\build\x64\vc14\bin路径添加到当中

  • 3 网上很多教程没有这一步,会报那种找不到dll文件的错误,是因为没有将opencv里面的相关文件复制到C盘中的文件夹里面
    操作方法:将bin目录下面的opencv_world330.dll和opencv_world330d.dll文件复制到C:\Windows\SysWOW64这个文件夹里面即可;将bin目录里面的opencv_ffmpeg330_64.dll复制到C:\Windows\System32这个文件夹里面(详细看图)
    如果是opencv其他的版本,把对应的dll文件移动到上述两个C盘文件夹即可!

  • 4 新建控制台项目(其他项目也行), 进入属性管理器,菜单栏->视图->其他窗口->属性管理器
    对Debug|X64进行配置(所以项目上面要选择debug和x64),右键Microsoft.Cpp.x64.user,点击属性
    这里我们对属性中的 VC++目录->包含目录和VC++目录->库目录进行添加相关路径,对 链接器->输入->附加依赖项进行添加相关路径
  • 5、包含目录中加入
  1. D:\ opencv \opencv\build\include
  2. D:\ opencv \opencv\build\include\opencv
  3. D:\ opencv \opencv\build\include\opencv2

库目录中加入

  1. D:\ opencv \opencv\build\x64\vc14\lib
  • 6、链接器->输入->附加依赖项中加入
  1. opencv_world330d.lib

注意:到上面所有工作,opencv已经配完了,注意解决方案平台那一栏要换成X64(因为我们一直都在配X64)

测试代码:

  1. #include <io
2018-09-20 18:54:53    291    0    0

对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态内存分配解决了这个问题。
new和delete运算符是用于动态分配和撤销内存的运算符。

一、new用法

1.开辟单变量地址空间

使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。
new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。

一般使用格式:

  1. 格式1:指针变量名=new 类型标识符;
  2. 格式2:指针变量名=new 类型标识符(初始值);
  3. 格式3:指针变量名=new 类型标识符 [内存单元个数];

说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。例如:

  1. 1)new int; //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a
  2. 2)int *a = new int(5) 作用同上,但是同时将整数空间赋值为5

2.开辟数组空间

对于数组进行动态分配的格式为:

  1. 指针变量名=new 类型名[下标表达式];
  2. delete [ ] 指向该数组的指针变量名;

两式中的方括号是非常重要的,两者必须配对使用,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。
delete []的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。

2018-09-20 18:32:52    365    0    0

众所周知,最开始我们用new来创建一个指针,那么等我们用完它之后,一定要用delete将该指针删掉。但是,值得注意的是,难道就仅仅是删除这个指针这么简单的么?下面,我们用一个程序来说明这个问题:

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int *p=new int;
  6. *p=3;
  7. cout<<"将3赋给p的地址后,指针p读取的值:"<<*p<<endl;
  8. delete p;
  9. cout<<"删除空间后,指针p读取的值:"<<*p<<endl;
  10. long *p1=new long;
  11. *p1=100;
  12. cout<<"创建新空间后,指针p中保存的地址:"<<p<<endl;
  13. cout<<"指向新空间的指针p1保存的地址:"<<p1<<endl;
  14. *p=23;
  15. cout<<"将23赋给p的地址后,指针p读取的值:"<<*p<<endl;
  16. cout<<"将23赋给p的地址后,指针p1读取的值:"<<*p1<<endl;
  17. delete p1;
  18. return 0;
  19. }

在上面这个程序中,我们在第8行就将指针p利用delete删掉了。但是,我们来看看程序的输出结果:
title
对照着上面的程序,我们来分析一下这个输出。首先,我们在程序的第5行初始化了一个指针p。之后输出指针p读取的值。由于第6行的原因,程序肯定会输出3了。之后,我们在程序的第8行删除了这个指针p。但是我们惊奇的发现,在程序的第9行竟然可以输出指针p读取的值。我们不是已经把它删了么?其实不然,debug,上图:
title
从监视窗口中,我们可以看见虽然程序的第8行已经将指针p删除了,但是在监视窗口中p仍然存在,只是*p所指向的值不再是原来的3了,而是一个随机数。这里就说明了一个非常重要的概念:我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身
然后我们接着往下分析。在程序的第10行我们又创建了一个long型的指针p1。在12行与13行的输出中我们惊奇地发现,指针p保存的地址居然和指针p

2018-09-20 18:25:13    308    0    0

为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned


  1. size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度
  2. string::size_type 制类型一般就是unsigned int, 但是不同机器环境长度可能不同 win32 和win64上长度差别;size_type一般也是unsigned int

  3. 使用的时候可以参考:

  1. string::size_type a =123;
  2. vector<int>::size_type b=234;
  3. size_t b=456;
  1. size_t 使用的时候头文件需要 (vs2017中貌似不需要, std::size_t sz;即可声明变量size_t类型变量sz) ;size_type 使用的时候需要或者

  1. sizeof(string::size_type)
  2. sizeof(vector<bool>::size_type)
  3. sizeof(vector<char>::size_type)
  4. sizeof(size_t)

上述长度均相等,长度为win32:4 win64:8
6. 二者联系:在用下标访问元素时,vector使用vector::size_type作为下标类型,而数组下标的正确类型则是size_t
By lb 2018/8/24

2018-09-20 12:23:48    328    0    0

  1. //自定义拷贝构造函数
  2. classname(const classname &ob)
  3. {
  4. //自定义拷贝构造函数的函数体
  5. }
  6. // 其中ob是用来初始另一个对象的对象的引用

  1. class StringData{
  2. private: char *str;
  3. public:
  4. StringData(char *s){
  5. str=new char[strlen(s)+1];
  6. strcpy(str,s);
  7. }
  8. StringData(const StringData &p){
  9. str=new char[strlen(p.str)+1];
  10. strcpy(str,p.str);
  11. }
  12. ~StringData() { delete str; }
  13. //…
  14. };
  15. int main()
  16. {
  17. StringData x(“abc”);
  18. StringData y(x);
  19. getchar();
  20. return 0;
  21. }

说明:
拷贝构造函数是一种特殊的构造函数。它用于依据已存在的对象建立一个新对象。
如果一个对象里面有指针成员,并且这个指针已经动态分配空间了,同时,对象有析构函数对这个指针进行释放。如上面那个例子,如果我们通过这个对象创建一个新对象:

  1. A a("123");
  2. A b = a; // 调用拷贝构造函数

如果我们没有自定义拷贝构造函数,导致对象 a 和 b 的指针成员指向同一个地址空间,当对象生命周期结束时,a 和 b 都会调用析构函数,最后导致,这个指针会被释放 2 次,导致内存出问题。

引用


在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
  深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

所以,对象有指针成员,尽量自定义拷贝构造函数。

2018-09-20 12:23:48    356    0    0

C++在构造函数和析构函数中调用虚函数时都不会使用动态联编

先看一个例子:

  1. #include <iostream>
  2. using namespace std;
  3. class A{
  4. public:
  5. A() {
  6. show();
  7. }
  8. virtual void show(){
  9. cout<<"in A"<<endl;
  10. }
  11. virtual ~A(){
  12. show();
  13. }
  14. };
  15. class B:public A{
  16. public:
  17. B() {
  18. show();
  19. }
  20. void show(){
  21. cout<<"in B"<<endl;
  22. }
  23. };
  24. int main(){
  25. A *a = new A;//输出"in A"
  26. delete a;//输出"in A"
  27. cout << "*******我是分割线**********" << endl;
  28. A *b = new B;//输出"in A"后,输出"in B"
  29. delete b;//输出"in B"后,再输出"in A"
  30. getchar();
  31. return 0;
  32. }

输出结果,可以看到没有预想的多态效果:

  1. in A
  2. in A
  3. *******我是分割线**********
  4. in A
  5. in B
  6. in B
  7. in A

结论:构造函数和析构函数调用虚函数时都不使用动态联编,如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。
原因分析:
(1)不要在构造函数中调用虚函数的原因:因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化, 因此调用子类的虚函数是不安全的,故而C++不会进行动态联编。
(2)不要在析构函数中调用虚函数的原因:析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经“销毁”,这个时再调用子类的虚函数已经没有意义了。

2018-09-20 12:23:48    313    0    0
  • 1.2.3
  1. int *a,*b//声明两个指针变量
  2. a=b; //指针复制:此语句使a指向的地址与b指向的地址相同,它们都指向同一内存区域。
  3. *a=*b ; //指针赋值:此语句使a指向的地址(内存位置)的内容与b指向地址(内存位置)的内容相同,但a与b指向的地址不一定相同

a,b,都是指针变量,*a表示指针变量所指向的值,同样*b也是如此。*a=*b就是表示把指针b所指向的值赋值给指针变量a所指向的值。

但是要是指针a要是没有初始化或是开辟空间的话,这样赋值就有危险!!!
例如:

  1. int *a;
  2. int *b=5;
  3. *a=*b;

首先声明一个指针变量a,但是系统并没有为其分配空间,也就是说此时指针变量的值即地址不确定的,那么*a也就表示一个不确定的值,有可能这个值是系统的重要数据,那么*a=*b;不就修改了系统的数据了吗???也就变量的危险了。
所以声明指针变量的时候一定注意初始化,初始化为0也可以,代表空指针。

  • 4

    &a=&b ; //这个一般比较少用,使指针a本身的内容与指针b本身的内容相同

  • 5
    总结指针的用处:用于指向与其类型相同的地址,重点在于指向地址,而不在于指针本身的内容(故&a=&b一般仅作理解,比较少用)
    一句话:指针a是一个地址, *a是它指向的内容,&a是它本身的内容

2018-09-20 12:23:48    322    0    0

关于利用virtual函数实现动态绑定/多态,需要尤其注意的一点是:


必须保证基类和派生类中对应的虚函数除了函数名称一致之外,还必须保证形参和返回值也完全一致!
若基类和派生类中仅仅是虚函数的函数名称一致,但是形参不一致,则无法实现动态绑定/多态!实际上造成的结果是:派生类中对基类中对应的函数进行了隐藏,在派生类中只能访问派生类中的该函数,在基类中只能访问基类中对应的同名函数。

针对基类和派生类中的虚函数有没有默认实参问题进行讨论。

1.基类虚函数不带参数,派生类带参数

  1. #include<iostream>
  2. using namespace std;
  3. class super
  4. {
  5. public:
  6. virtual void somemethod()const { cout<<"base" << endl; }
  7. };
  8. class sub : public super
  9. {
  10. public:
  11. virtual void somemethod(int i = 12)const { cout<<"sub" << "i = " << i <<endl; }
  12. };
  13. int main(void)
  14. {
  15. sub mysub;
  16. super &ref = mysub;
  17. ref.somemethod(); // 调用哪个?
  18. return 0;
  19. }

输出:

  1. base

调用的是基类的,派生类是基类这个函数的重新定义的版本,无法实现动态绑定/多态。(这个务必引起重视,因为形参已经不一样了)

2.基类虚函数不带参数,派生类带参数

  1. #include<iostream>
  2. using namespace std;
  3. class super
  4. {
  5. public:
  6. virtual void somemethod(int i = 21)const {cout<<"base"<<"i = "<< i <<endl;}
  7. };
  8. class sub : public super
  9. {
  10. public:
  11. virtual void somemethod()const { cout<<"sub" << "i = " << i <<e