diff --git a/README.md b/README.md
index 185db2a..9830f25 100644
--- a/README.md
+++ b/README.md
@@ -14,4 +14,5 @@
- [tutorial](./tutorial/README.md);
- [虚幻引擎UE4](./UE4/README.md);
- [数据库](./数据库/README.md);
+- [其它](./其它/README.md);
diff --git "a/tutorial/AndroidStudio\350\256\276\347\275\256.md" "b/tutorial/AndroidStudio\350\256\276\347\275\256.md"
new file mode 100644
index 0000000..dde28fc
--- /dev/null
+++ "b/tutorial/AndroidStudio\350\256\276\347\275\256.md"
@@ -0,0 +1,130 @@
+pycharm激活看到的一个方法:
+1.先选择:License server
+2.输入:http://idea.imsxm.com
+3.点击:Activate完成了;
+
+使用前JAVA环境配置:[这](https://www.jdkdownload.com/)。
+
+## 一、安装
+
+ 以下前3步操作尽量就在安装软件前就先修改好,安装完后做了第4步再去打开软件,避免后续麻烦:
+
+1、修改配置模拟器(就是AVD Manger,模拟安卓手机的)生成的,也是最占空间的一个:
+
+添加一个系统变量
+
+>变量名(N): ANDROID_SDK_HOME # 名字是固定写法
+>
+>变量值(V): D:\Cache\Android\AVD # 建议这么写,可以自定义
+
+***
+
+2、.gradle缓存文件夹的修改(后期加载android项目时,这会占用挺大空间)
+
+添加一个系统变量
+
+>变量名(N): GRADLE_USER_HOME # 固定写法
+>
+>变量值(V): D:\Cache\Android\\.gradle # 注意这里.前面有个转义符
+
+Ps:这是[官方文档](https://developer.android.com/studio/intro/studio-config?utm_source=android-studio#antivirus-impact)建议的做法,还可以打开软件去修改。(就像上面这么做)
+
+***
+
+3、这应该是修改sdk存放的位置:
+
+添加一个系统变量(好像没啥用,但去弄一个嘛,==建议安装的时候选择自定义安装==,这样可以选择Sdk位置,占用内存这些)
+
+> 变量名(N): ANDROID_HOME
+>
+> 变量值(V): D:\Cache\Android\Sdk
+
+ 不搞第3步的sdk的话,可能会说找不到sdk.dir,然后也可以在项目的顶级目录下,编写一个名为`local.properties`的文件,然后里面的内容就一行`sdk.dir=D\:\\Cache\\Android\\Sdk`
+
+4、然后去安装路径的bin目录下修改`idea.properties`文件(jetbrains系列):
+
+ 这就是把一些工程文件的缓存路径改一下,==JrtBrains的所有软件也都改一下==。类似于:
+
+- ```
+ #---------------------------------------------------------------------
+ # Uncomment this option if you want to customize a path to the settings directory.
+ #---------------------------------------------------------------------
+ # idea.config.path=${user.home}/.PyCharm/config
+ idea.config.path=E:/Cache/JeaBrains/Pycharm_profession/.PyCharm/config
+
+ #---------------------------------------------------------------------
+ # Uncomment this option if you want to customize a path to the caches directory.
+ #---------------------------------------------------------------------
+ # idea.system.path=${user.home}/.PyCharm/system
+ idea.system.path=E:/Cache/JeaBrains/Pycharm_profession/.PyCharm/system
+
+ #---------------------------------------------------------------------
+ # Uncomment this option if you want to customize a path to the user-installed plugins directory.
+ #---------------------------------------------------------------------
+ # idea.plugins.path=${idea.config.path}/plugins
+ idea.plugins.path=E:/Cache/JeaBrains/Pycharm_profession/plugins
+
+ #---------------------------------------------------------------------
+ # Uncomment this option if you want to customize a path to the logs directory.
+ #---------------------------------------------------------------------
+ # idea.log.path=${idea.system.path}/log
+ idea.log.path=E:/Cache/JeaBrains/Pycharm_profession/log
+ ```
+
+-----------------------------------------
+
+-------------------------------------------------------------------------
+
+ 以上操作完成后,打开软件,当有一个提示时选择cancle,如果是默认安装,它就会自己把andriod sdk下到类似于这样的路径:`C:\Users\Administrator\AppData\Local\Android\Sdk`,等它下好后,把这个Sdk文件夹整个复制到你想要放的地方(避免后续二次下载),然后进入软件设置,搜索一下`SDK`,然后把那个路径改成你想要放的地方。再去把C盘里的删除就好了。
+
+ 所以为了避免麻烦,一点要选择==custom安装==,这样就能选择andriod sdk的下载路径。
+
+## 二、Hello-World
+
+1. 新建项目时,选择中间的==Empty Activity==,如果是选择No Activity,里面的res是没有layout层的;接着语言是选择JAVA(选择kotlin也能使用)
+2. 然后点开lauout里的activity_main.xml==可能是看不到代码的,要点右上角的Code==;
+
+Tips:
+
+- AVD的机子,尽量先选用==Nexus 6==这个机子,(用Pixel 2这机子那个半天安装install不上)
+- 让虚拟手机==使用电脑的摄像头==:AVD Manger找到要设置的手机,点击倒三角中的Edit-->Show Advanced Setting,然后就能修改Camera
+
+## 三、问题解决
+
+问题:
+
+ CMake '3.10.2' was not found in PATH or by cmake.dir property.
+
+解决:
+
+ 进到SDKmanger 然后SDK tools,选择Cmake安装,然后重启一下,不行,就再把==Show Package Details==打开,把那些都勾上,再重启。
+
+---
+
+问题:
+
+ No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi
+
+解决:
+
+ 重启软件,弹出提示gradle升级时,记得去升级,升级了就好了
+
+---
+
+问题:
+
+ 运行时,一直卡在waiting for target device to come online
+
+解决:
+
+ 先把所有的模拟手机关掉,然后在AVD Manger中找到自己要运行的机子,点击最右边的倒三角,选择==cold boot now==就可以了。
+
+---
+
+问题:
+
+ Device supports x86, but APK only supports armeabi-v7a, arm64-v8a ondevice New Device API 29
+
+解决(可能会有其他问题):
+
+ 在app文件夹里找到build.grade文件,找到==defaultConfig==,里面ndk下有abiFilters的后面添加上==x86==,再重新build && run。(因为用的虚拟设备,可以看到其ABI就是x86)
\ No newline at end of file
diff --git "a/tutorial/HTML\346\226\207\346\241\243.md" "b/tutorial/HTML\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..e2b8e90
--- /dev/null
+++ "b/tutorial/HTML\346\226\207\346\241\243.md"
@@ -0,0 +1,2667 @@
+注释一行是:ctrl + /
+
+在当前位置注释:ctrl + shift + /
+
+Emmet 语法 [速查表](https://www.jianshu.com/p/9352a0411fcb)
+
+有一个盒子模型和margin和overflow属性,还挺有用的,如果以后用得上,在这里(“.\就业班\07 HTML和CSS\08-margin和overflow属性”)(好像overflow可以把ul列表搞成一行)
+
+通过css来做动画效果,也挺有意义的(“.\就业班\首页布局案例和CSS3动画及移动端布局\02-CSS3动画”)
+
+这个网址是[免费图标](https://phosphoricons.com/)的各种类型下载。
+
+这个网址是前端各种在线环境,vue等各种三方库,[在线环境](https://pcljs.org/zh-cn/docs/tutorials/getting-started/playground)。(这个网址还能调用PCL的库在浏览器中运行)
+
+## 一、HTML
+
+### 1.1. HTML文档结构
+
+W3C中规定:所有属性值用双引号括起来(虽然单引号和不要引号都行,但是更加统一规范)
+
+- 这里包括了html基本组成,引用外部css样式,把网页前面加一个图标
+
+```html
+
+
+
+
+
+
+ 我的第一个网页
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 这是一个段落
+
+
+
+
+```
+
+### 1.2. 常用标签
+
+webstorm中快速打出固定的东西(在一个空白的html文件中):
+
+1. 先输入`!`再按Tab就行;
+ - 标签*10再按tab就可以快速打出10个标签,比如 p\*10,再tab就能获得10个p标签。
+1. 或者输入`html:5`再按Tab就行。
+
+#### 1.2.1 几个基本标签
+
+一般在body中使用各种标签,head中的内容都是安装上面的方法生成的:
+
+- div:用来布局的,没有具体含义,可以看做层;
+- h1:用于控制标题级别的,数字只能是1~6,1级标题最大,6最小,会自动加粗,有默认字号;
+- p:表示一个段落,相当于一个回车,里面一般放一段文字;
+- br:换行,一段文字想在某些地方换行就加这,==单标签==;
+- hr:生成一条水平线,主要起装饰作用,==单标签==;
+- a:实现超链接跳转,包括网址和文件;
+- img:加载外部图片,==单标签==;这个尽量要给alt属性,以免在加载不出图片时,更友好的显示;加上title属性,鼠标放上面会有title内容的文字提示
+- span:作用与div一样,都是用来布局,不同的是div会单独占一行,而span不会,span用于行内布局;
+- ul/ol:前者是无序列表,后者是有序列表,他们的内容都用的是li标签。
+
+```html
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+ abc_hello_world
+
这里面是可以嵌套的
+
+
+
+ 一、前端的学习
+ 1.1、html入门
+ 最后一级标题
+
+
+ 这里面放一大段文字,然后最后结尾p标签就相当于来了一个回车,进行分段
+
+
+ 这里面放一大段文字,
然后最后结尾p标签就相当于来了一个回车
+
+
+ 这是标签属性的测试
+
+
+
+
+
+
+ 请点击百度这里
+
+
+
+ 01文档001
+ 01文档002
+
+
+
+
+
+
+
+ div1
+ div2
+ span1
+ span2
+
+
+
+ - 这是内容1
+ - 这是内容2
+ - 这是内容3
+
+
+
+ - 这是有序列表1
+ - 这是有序列表2
+
+
+
+ 这会自动加引号
+
+
+ 电话:123456789
+ 住址:XXXXXXXXXX
+
+
+
+
+
+```
+
+补充一个单标签(都还是加上`/`闭合):
+
+- 换行符:
+- 水平线:
+- 图片标签:
+- 文本标签:
+- link标签:
+- 元信息标签:
+
+#### 1.2.2 加强文本样式标签
+
+文本格式化标签:
+
+- b 和 strong:都是对文本进行==加粗==,且都是==行级标签==;
+ - 但strong除了加粗还有强调作用,注:强调主要用于SEO时,便于提取对应的关键字。
+
+- i 和 em:使文字倾斜,且都是==行内标签==,如果是简单的倾斜效果,使用==i==标签就行;
+ - em同样具有强调效果。
+
+- pre:可定义预格式化的文本,这里面的文本通常会保留空格和换行符,而文本也会呈现为等宽字体,文字的字号也会小一号;
+ - pre是==块级标签==;
+ - 反观p标签中的空格都是不会被认可的,多少空格都没用
+- small 和 big:均为==行内标签==,让字体缩小或放大一号;
+ - 浏览器支持的最小字号为12px,需要更小的就要做处理了;
+ - big在HTML5中被淘汰了,但并没有删除(在ide中,big显示时也会被加一条删除线),能用,但不建议使用。
+- sub 和 sup:将其内的文本设为下标和上标。
+
+```html
+
+
+
+
+ 文本格式化标签
+
+
+ 第一种加粗方式
+ 第二种加粗方式,并强调
+
+ 文字倾斜
+ 文字倾斜,并强调
+
+ 这里的文本 会保留空格
+ 以及换行, 保持现状的样子
+
+ 这是正常字体
+ 这是小一号的字体
+ 这是大一号的字体
+
+ X1 + X1 = Y
+ 下标:X1+Y1 = Y
+ 上标: X2 + Y2 = Y2
+
+ 上标: X1 + Y1 = Y
+
+
+```
+
+#### 1.2.3 \ 等实体转义
+
+ 现象:在HTML中,内容编辑时,如果通过空格键编辑的多个空格,==网页会显示仅且只显示一个空格==,而小于(<)和大于号(>),网站则会认为是标签而无法直接显示在页面中,这都可以通过实体字符来解决。
+
+| 实体字符 | 代表的字符 |
+| ---------- | ---------- |
+| `<` | < |
+| `>` | > |
+| `&` | & |
+| ` ` | 一个空格 |
+| `©` | 版权(©) |
+| `×` | 乘号(×) |
+| `÷` | 除号(÷) |
+| `'` | 英文单引号 |
+| `"` | 英文双引号 |
+
+Tips:
+
+- 一定是以&开头,以`;`结束;
+
+- 一个` `只代表一个空格,想要多个空格就多次输入
+
+ ```html
+ aa bb
+ aa bb
+ aa ÷ bb
+ ```
+
+然后一般点击左右那个箭头,就是通过大于小于符号做的,然后给它设置样式:
+
+> ```
+> .sym {
+> color: hotpink;
+> font-weight: bolder;
+> font-size: larger;
+> }
+> <
+> ```
+
+#### 1.2.4 块级/行内元素
+
+W3C中的嵌套规范:
+
+- 块级元素介意包含行内元素或块级元素,但行内元素只能包含行内元素;
+- p,h1~H6,dt标签中只能包含行内标签,不能再包含块级标签;
+- 块级元素与块级元素并列,行内元素与行内元素并列,不要混合并列。
+
+ ==块级元素==(相当执行了 display:block; 的操作):块级元素会独占一行,其宽度自动填满其父级元素宽度,一般情况下,块级元素可以设置 width,height属性,一般用来搭建网站架构、布局、承载内容...,它包括以下这些标签:(若设置 display:none 样式就会不可见)
+
+> address、dir、div、dl、dt、dd、fieldset、from、h1~h6、hr、menu、noframes、ol、p、pre、table、ul等。
+
+- 独占一行;
+
+- 宽度和高度是可控的,如果没有设置其宽度,将默认铺满整行;
+
+ ```html
+ this is div
+ ```
+
+ this is div
+
+- 其内可以包含块级和行内元素。
+
+---
+
+ ==行内元素==(相当于执行了 display:inline; 的操作):行内元素不会独占一行,相邻的行内元素会排列在同一行内,直到一行排不下才会换行,其宽度随元素的内容而变化,行内元素设置width和height无效,一般用在网站内容之中的某些细节或部位,用以“强调、区分样式、上标、下标、锚点”等等。下面这些标签都属于行内元素:
+
+> a、b、bdo、big、small、br、cite、em、font、i、img、input、kbd、label、select、span、strong、sub、sup、textarea等。
+
+- 不会独占一行,与相邻的行级元素占同一行,直到行占满,才会自动掉到下一行;
+
+- 宽度和高度是不可控的;
+
+ ```html
+ 这里放点文字
+ this is span
+ ```
+
+ 这里放点文字
+ this is span
+
+- 其内只有包含行内元素。
+
+注:以上两个例子都设置了宽高,也看到效果,只有块级元素可以,行内元素设置宽高是不起作用的。
+
+---
+
+块级元素和行内元素之间的转换:
+
+块转行:在属性中加入==display:inline;==
+
+行转块:在属性中加入==display:block;==
+
+```html
+块级元素转行内元素
+这里再放点文字
+行内元素转块级元素
+```
+
+可以把这一小节的这6行代码放一起,就能在浏览器看出很明显的效果了。
+
+### 1.3. 标签通用属性
+
+1. 标签名是由标签名、标签属性和文本内容三部分组成(注意:单标签没有文本内容);
+
+2. 标签属性分为通用属性、自有属性和自定义属性:
+
+ 1. 通用属性有:
+
+ - id:给标签起一个唯一标识符,id在一个网页内==必须是唯一的==;
+ - class:用来给标签取一个类名(同一类的就方便统一使用 .box 这样的样式);
+ - title:当鼠标移到该标签时,所显示的提示内容;
+ - style:用来设置行内样式。
+
+ ```html
+
+ 这是id1
+ 这是id2
+
+
+ a_div
+ a_PP
+
+
+ 鼠标放上来有提示
+
+
+ 这是一个行内样式
+
+ ```
+
+ 2. 自定义标签属性:通常用来传值或是图片懒加载(滑到某区域时,某区域才加载图片)等方面
+
+ 格式:data-* // `data-`是固定的,后面的*是自己起
+
+ 如:\
// 多个属性之间空格隔开就是,不要加逗号
+
+ \....
+ 上面的src以及goods_name都是自己起的,尽量跟后面的值相关,然后后面代码会把具体值传过来
+
+
+### 1.4. form表单
+
+form表单是用来实现前后端交互的一个重要标签,常用属性:
+
+- name:表单名称
+- action:表单数据提交的地方(通常是一个后滩文件名(.jsp、.php、.py等,或网址))
+- method:前端提交数据到后端的方法,主要有:get和post(注:默认是get,但尽量用post,因为get会把数据明文显示出来,而post不会)
+
+```html
+
+
+
+
+
+```
+
+表单元素分为四类:(==以下的每一个大类或是大类里又细分的,其实就是一个标签,可单独使用==)
+
+1. ==**input类**==:主要用来输入,根据其不同的type属性,可以变化为多种状态输入方式
+
+ - ==== // 定义提供文本输入的单行输入字段(这是默认值,什么都不给就是这)
+
+ - placeholder //文本框内提示
+
+ - name // 命名
+
+ - minlength // 最少输入的字符个数
+
+ - maxlength // 最多输入的字符个数,超过了就输入不进去了
+
+ - disabled // 失效(disabled或disabled=""或disanled="disabled"都会让这个框变灰而无方法选中)
+
+ - readonly // 只读(这也不能修改,与disabled写法一样,区别是它跟正常框一样,不会变灰)
+
+ - value // 可以给一个默认值(有这个就不会再显示placeholder的提示了)
+
+ - pattern // 正则匹配,(比如注册邮箱时,不合法,直接前端就验证了)
+
+ ```html
+
+ ```
+
+ - ==== // 定义密码字段
+
+ 它的属性跟text是一样的,主要就是不是明文显示
+
+ - ==== // 定义==单选==按钮
+
+ - name // 这个很重要,做抉择的选项的name值必须一样,这样保证只有一个能被选中,不给的话,两个都能被选中,所以这name跟上面的name不一样
+
+ - checked // 给这个值,代表默认选择(一般单选都会给一个默认值,多给也是最后一个起作用)
+
+ - 这个还有value、disabled、readonly属性,但是不常用
+
+ ```html
+
+ ```
+
+ - ==== // 定义复选框(也叫检查框),可选择0项、1项或多项
+
+ - name // 这个属性必须要有
+
+ - 其它属性基本同上面的radio
+
+ 这个选择的==默认值可以不给==,也可以给多个(注意html直接渲染的结果)
+
+ ```html
+
+ ```
+
+
+
+ - ==== // 主要是文件的上传,点击就会在本地选择文件
+
+ - name # 常用的是这个属性,具体可看Django笔记中的上传文件的使用
+
+ - ==== // 定义普通按钮
+
+ - value // 这个属性它的值主要是在button上显示
+
+ - disabled // 跟上面用法一样,设置了这个按钮就变灰了,就不能点了
+
+ ```html
+
+ ```
+
+ - ==== // 定义图片提交按钮(就是把一个按钮图片化了,弹幕说就是带皮肤的button),用法同button一样
+
+ - src // 属性 用来放图片的路径
+
+ - title // 属性 用来鼠标在图片上悬停时的友好提示
+
+ - 注意:这个点击提交会跳转到demo.app处,跟"submit一样",而button却不会提交
+
+ ```html
+
+ ```
+
+ - ==== // 定义提交表单数据至表单处理程序的按钮
+
+ - value // 属性,提交按钮上显示的字,默认是submit
+
+ 注意:这个点击提交会跳转到demo.app处
+
+ ```html
+
+ ```
+
+ - ==== // 前面改了一些值,这个将其全部还原为初始状态
+
+ - value // 属性 按钮上显示的字 默认是Reset
+
+ ```html
+
+ ```
+
+ - ==== // 用于输入邮箱时吧,好像用text也一样啊
+
+2. ==**textarea类**==: // 这个主要用于输入大批量的内容
+
+ - 常用属性:name/id/cols/rows/placeholder/minlength/maxlength/required(有这个值表示必须输入)/value
+
+ - 列数固定死了,行数给定了,显示区域就那么大,要是文字超过给的行数,右侧就会出现滑动拉条
+
+ ```html
+
+ ```
+
+ 注:这里还可以\,这里只是为了简单暂时,可以把提示文字放这里,这样框里初始状态就会有“备注:”这俩字,哪怕有placeholder,placeholderdr的值也会被这个覆盖,而且它是永远不会消失的。
+
+3. ==**select类**==:
+
+ - 下拉列表框,默认用于单项选择:
+
+ ```html
+
+ ```
+
+ - 提交表单后,里面会有一个键值对,key就是select的name(这里即a_sel),若是选的option中的男,那键值对的值就是所选的option中的value(这里即male)
+
+ - 多项选择,加上关键字 multiple
+
+ 如果选择很多,全部显示出来很长,那就加一个 size(代表最多显示的行数,超过的都会被收起来)
+ option的默认选中就加个==selected==。
+
+ ```html
+
+ ```
+
+4. ==**button类**==:
+
+ - 普通按钮,具有提交功能,一般是执行js代码,可以单独使用;
+
+ - 如果写在form中,具有提交功能:
+
+ ```html
+
+
+ ```
+
+---
+
+ 补充:最后还有一个iframe框架标签,就是把几个html文件组合到一起,但是十几种几乎不用,它会破坏网页的前进后退功能,且不利于SEO看弹幕说一般是用div,放[这里](https://www.bilibili.com/video/BV1i7411Z7d8?p=36&spm_id_from=pageDriver)做个了解吧。
+
+### 1.5. 表格
+
+ 在html中,table表示表格,tr表示行,td表示列,生成一个表格的快速写法:`table>tr*2>td{这里放内容}*3`,然后再按tab键,就会生成两行三列的表格
+
+```html
+
+
+
+
+ 这里放内容 |
+ 这里放内容 |
+ 这里放内容 |
+
+
+ 这里放内容 |
+ 这里放内容 |
+ 这里放内容 |
+
+
+
+```
+
+注意:一般把第一行的tr改成th,这样它就会是表头,与后面的不一样。
+
+表格属性:(但是WebStorm提示这些将被Deprecated,建议用css来修改样式)
+
+- border:设置表格边框,默认单位是像素;
+- width:设置表格宽度,默认但是像素;
+- align:表格相对出现在页面中的位置,(left(默认)/center/right);
+- cellspacing:表格最外面的边框跟里面的距离,当不设置为0时,可以明显看到差别;
+- cellpadding:单元格内文本与边框的距离,换种理解,可理解为表格的高度。
+
+更细更多的东西,就看[这里](https://www.bilibili.com/video/BV1i7411Z7d8?p=27&t=3.6)。
+
+## 二、CSS
+
+一个很详细的示例,如果要看css效果的一些实现,一定要来[这里](https://www.w3school.com.cn/css/css_image_transparency.asp)看看。
+
+css语法由三个部分组成:选择器、属性、属性值。
+
+> selector { property: value}
+
+- value前面的空格可以不要
+- 属性完了都是以`;`隔开
+- 一个属性可能有多个值,多个值之间用空格隔开
+
+### 2.1. css引入方式
+
+一共有四种引用方法:
+
+第一种:==行间样式==(又叫嵌入式样式)
+
+ 直接在html文件的body的里面写:
+
+```html
+行间样式演示
+行间样式演示
+```
+
+行间样式演示
+行间样式演示
+
+---
+
+第二种:==内部样式表==,一般都是定义放在html文件的head中,然后在body中使用:
+ 下面想要修改p标签的样式,那就用 style 把 p 标签包裹起来,再设置p标签的样式
+
+```html
+
+
+
+
+
+ 这是内部样式
+ 这是内部样式
+
+```
+
+---
+
+第三种:==外部样式==,
+
+(1)先在外面写一个css文件,(2)ling:css把这个文件引入
+
+ 假设写的css文件名“01css.css”,放在html同路径下的“my_style”文件夹下:
+
+```css
+span {
+ font-size: 15px;
+ color: rgba(108, 141, 23, 0.66);
+ display: block; /*这样就把行元素转成块元素了,就会自动换行了,原来是不会的*/
+}
+```
+
+然后html文件:
+
+```html
+
+
+
+
+ CSS学习
+
+
+
+
+
+ 这是引入外部样式文件
+ 这是引入外部样式文件
+
+
+```
+
+---
+
+第四种:==导入外部样式==,与第三种其实差不多,就是用的@impoet
+
+ 这种先写的一个02css.css文件,跟第三种方法有点不一样:
+
+```css
+.my_box { /* 前面的点是固定写法,my_box可以是任意的*/
+ font-weight: bold; /*加粗*/
+ font-size: 20px;
+ color: darkorange;
+}
+```
+
+然后在html中:
+
+```html
+
+
+
+
+ CSS学习
+
+
+
+
+
+ 这是导入外部样式
+ 这是导入外部样式
+ 这是导入外部样式
+ em就倾斜的意思
+ em就倾斜的意思
+
+
+```
+
+Tips:
+
+- 这种导入的css文件也能是第三种方法里的那种css文件;
+- 主要看第三种方法和第四种方法的css文件的写法不同,第四种方法更具有自定义性;
+- .my_box{这是导入外部样式}*3 然后再tab 这种快速写法,点`.`默认就是用div布局;
+- @import导入时:
+ - 文件一定要加引号,且结尾一定要是分号;
+ - ==这个@import语句一定要放到 style 里包裹起来==。
+
+区别:
+
+- link除了加载CSS外,还可以定义RSS等其它事务;而@import属于css范畴,只能加载css;
+- link引用css时,在页面加载时同时加载;@import需要网页页面完全载入后加载;
+- link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持;
+- link支持使用Javascript控制DOM去改变样式;而@import不支持。
+
+实际开发中,可能更多的还是用的link。
+
+### 2.2. 样式写法
+
+#### 2.2.1 css选择器
+
+CSS选择器有几种分类:`所有写在html中的css选择器,都一定要用 style 标签包裹起来!`
+
+1、==*==:匹配html中所有标签,(所以性能比较差,实际开发中,不建议使用)
+
+注意点:
+
+- 一般是把这写到html文件的head中,它会对body中的所有标签都产生作用;
+
+- 在head中一定要用==style==标签包裹起来。
+
+ ```html
+
+
+
+
+ Title
+
+
+
+ 这是一个测试
+ 这效果会是一样的
+
+
+ ```
+
+2、==标签选择器==:==设置的那种标签,那就只有对应的标签才生效==
+
+- 注意:一定是已有的标签,如span、div、p这种
+
+ ```html
+
+
+
+
+ 这就没效果
+ 这才会有效果
+
+ ```
+
+3、==类选择器:自定义一个类名,设置样式,凡是使用 class=此类名 的标签均为这个样式==(示例很重要)
+ (一般都是用这个)
+
+- 注意:名字可以自己起,前面一定要加一个点`.`;
+
+- Emmet语法快速写法(假设div标签,class想要写my_sty):先输入 div#my_sty 再tab 就会自动生成(可能会生成错误,成了id=my_sty,那就建议直接 .my_sty,然后tab,默认就是div布局, 也可以 .my_sty>p 再table,就是div中嵌套着p标签了 )。
+
+ ```html
+
+
+
+
+ 这就没效果
+
+ 这才会有效果
+
+ 这跟上面就是一样的效果
+
+ ```
+
+4、==ID选择器:自定义一个id名,然后 id=此id 的标签就会使用这样式==
+
+- 注意:名字也是自己起,前面一定要加一个`#`;
+
+- 一般id是唯一的,在IDE中两个相同的id会报错提醒,但是在网页上像个相同id的标签还是会显示一样的效果;
+
+- Emmet语法快速写法(假设p标签,id想要写my_id):先输入 p#my_id 再tab 就会自动生成。
+
+ ```html
+
+
+
+
+ 这就没效果
+
+ 这是id选择器的效果
+
+ ```
+
+5、派生选择器:一般就是父类选择了一个样式(就是一般的类选择器),后续的不选都是这个样式,常见于有/无序列表。
+
+- Emmet语法快速写法:先输入 ul>li{这是内容$$}*3 再tab就得到了三条无序列表
+
+ ```html
+
+
+
+
+
+
+ - 这是内容的说001
+ - 这是内容的说002
+
+ - 注意这子内容的位置
+ - 跟内容002是在一个li里面
+
+
+ - 这是内容的说003
+ nihaio
+
+
+ ```
+
+- 这样的话,所有的内容的样式都是上面==my_der==的样式,但是注意它的写,上面这种写法测试没问题,它会对里面所有标签都生效,包括哪不合法的p标签;
+
+- 还有一种写法:.my_der li {color: coral;} 或者 .my_der>li {color: coral;}
+ 这样就只会有li标签生效,而p标签是不会生效的。
+
+- 注意:20行,p标签放这里可有效果,但是IDE会报“Element p is not allowed here”,不让放进ul中,放其它标签也是,暂时没找到除了li以外的其它可以放的标签。
+
+6、伪类选择器(说是后面学)
+
+#### 2.2.2 选择器分组
+
+让多个选择器(元素)具有相同的样式,一般用于设置公共样式,避免重复写相同样式的代码。
+
+```html
+
+
+
+
+ 样式~~
+ 一级标题
+ ~~样式
+
+```
+
+解读:
+
+- p、h1标签以及类选择器box(所以前面有个点),写一起后,三个的样式都是相同的,就不用写三次;
+- p标签又在下面自主定义了,就会增加样式,里面新加的color,就会把组里的颜色覆盖掉。
+
+#### 2.2.3 样式继承
+
+被别的标签包裹起来的就是人家的子类,子类是会继承父类的,也可以重写,有点选择器分组的味道
+
+```html
+
+
+
+
+ 这是一段测试内容
+
+```
+
+Tips:要在标签里加新的标签的快捷写法:比如第14行
+
+ 先写好了 \这是一段测试内容
,要在“内容”用span包裹起来,那就在"内容"后打一个空格,然后输入span,再按tab,然后再把"内容"两字剪切进来
+
+其实下面是更常见的继承写法:
+
+```html
+
+
+
+
+
+
+
+ 这是一段文字
+ 这是一段文字
+
+
+
+ 这是第一级别的文字
+
这是里面的新的文字
+
+
+
+```
+
+Tips:
+
+- 一般看见这种 .content .border .player {
+
+ box-shadow:3px 3px 8px 3px rgba(200,200,200,0.5)
+
+ } # 全都带点,这名字都是自己起的,继承的,然后一般也用于一层层嵌套的样式
+
+- 一般看见这种 .content .border img {
+
+ box-shadow:3px 3px 8px 3px rgba(200,200,200,0.5)
+
+ } # 前面两个名字是自己起的,后面的 img 是自带的标签名
+
+#### 2.2.4 样式优先级
+
+外部样式<内部样式<内联样式 // 总之就是一句话, 就近原则
+
+优先级权值:
+ 把特殊性分为4个等级,每个等级代表一类选择器,每个等级的值为其代表的选择器的个数乘以这一等级的权值,最后把所有等级的值相加得出选择器的特殊值。
+
+- !important:加在样式属性值后,权重值为10000,一般加了这个的,样式都以这为准了;
+- 内联样式(就是`这里面的
`),如:style="",权重值为1000;
+- ID选择器,如:#my_content,权重值为100;
+- 类、伪类选择器,如:.content、:hover,权重值为10;
+- 标签选择器:如:h2、div、p 等 权重值为1。
+
+```html
+
+
+
+
+
+
+ 这不会有颜色的
+
+```
+
+讲解:
+
+- 上面style中,它不是选择器分组,因为他们之间没有逗号分隔,是样式继承那种;
+- 就计算权重值,#content(ID选择器)和h2(标签选择器)都是一样的,都是100×1+1×1,然后div.main_content是“标签指定式选择器”,权重比 .main_content(这就是一个类选择器,名字是随便起的)高一点,所以会是红色的;
+- 18行的h2标签是不会有颜色的,因为并没有单独定义h2标签的样式;
+- 14行的h2标签,有颜色,也一定要搭配两个父级,因为都是这么定义的。
+- 重要:如果想要14行显示蓝色,那就加个!impoertant,把第7行改成==color: blue!important;==.
+
+### 2.3. 字体属性
+
+注意:==要是一个属性有多个值,直接写多个属性,用空格隔开就好了==。如下面的上划线和下划线
+
+- font-size:设置文本大小(如下两种方式)
+ - p { font-size: 20px; } // 固定值尺寸像素
+ - p { font-size: 100%} // 其百分比取值是基于父对象中字体的尺寸大小
+
+---
+
+- font-family:设置字体
+ - p {font-family: Courier, "Courier new", monospace; } // 按此顺序来,如前面的字体被支持就直接使用,后续字体就失效,若是不支持,就以此往后推(若都不支持,就会使用默认字体);若是字体名包含空格,则用引号括起来,
+
+---
+
+- font-style:设置文本字体的样式(三种属性值)
+ - p { font-style: normal; } // 默认值,正常中的字体
+ - p { font-style: italic; } // 斜体,对于没有斜体变量的特殊字体,将应用 oblique
+ - p { font-style: oblique; } // 倾斜的字体
+
+---
+
+- font-weight:设置文本==字体的粗细==(属性值有 normal、bold、bolder、lighter(比nornal细)、100-900的整数)
+ - p { font-weight: normal; } // 默认值,正常的字体
+ - p { font-weight: bold; } // 粗体
+ - p { font-weight: bolder; } // 比bold粗
+ - p { font-weight: 600; } // 定义由细到粗的字符;400等同于normal,700等同于bold
+
+---
+
+- color:设置文本字体的颜色(三种方式)
+ - p { color: red; } // 方式一:直接使用颜色名字
+ - p { color: rgb(100, 13, 200); } // 方式二:使用rgb函数
+ - p { color: #345678; } // 方式三:指定颜色为16进制(推荐)
+ - 一般红色:#ff0000,这种可以简写为#f00,(就是#ff1122,可以简写成#f12,即#AABBCC才可以写成#ABC,像#ff1233就不可以简写,尽量还是不简写)
+
+---
+
+- line-height:设置文本字体的==行高==
+ - p { line-height: normal; } // 默认值,默认行高
+ - p { line-height: 24px; } // 指定行高为长度像素
+ - p { line-height: 1.5; } // 指定==行高为字体大小的倍数==,这里就是行高是字体的1.5倍
+ - p { line-height: 2em; } // 这里也可以是两个字体的高
+
+---
+
+- text-decoration:设置文本字体的修饰(有无==各种划线==)
+ - p { text-decoration: none; } // 默认值,无修饰
+ - p { text-decoration: underline ; } // 下划线
+ - p { text-decoration: line-through; } // 贯穿线
+ - p { text-decoration: overline underline; } // 上划线 `多个属性直接用空格隔开就好了`
+
+---
+
+- text-align:设置文本字体的对齐方式
+ - p { text-align: left; } // 默认值,左对齐
+ - p { text-align: center; } // 居中对齐
+ - p { text-align: right; } // 右对齐
+
+---
+
+- text-transform:设置文本字体的==字母大小写==(默认值是none,表示无转换发生)
+ - p { text-transform: capitalize; } // 单词首字母转为大写
+ - p { text-transform: uppercase; } // 全部转换为大写
+ - p { text-transform: lowercase; } // 字母全部转换为小写
+
+---
+
+- text-indent:设置文本字体的==首行缩进==
+ - p { text-indent: 24px; } // 首行缩进 number 个像素
+ - p { text-indent: 2em; } // 首行缩进 number 个字符(em这里代表字符,前面2就是2个字符)(推荐这种方式,因为字体大小不同,这样就有一个自适应的效果)
+
+Tips:以上都是针对字体的属性,每个属性都要写一行,为了简单些,有一个font的==复合属性==:
+
+用法:`font: font-style font-variant font-weight font-size/line-height font-family;`
+
+- 一定要注意属性值的位置顺序
+- 除了font-size和font-family之外,其它任何一个属性值都可以省略
+- font-variant:它的属性值有 normal、small-caps(让小写字母都变大写,且非单词首字母的字母会小写)
+
+示例:(以下,除了18px、微软雅黑这两个值必须要有,其它的值可以省略不要的)
+
+```html
+
+
+
+ Hello World 这是忙着开着快乐阿达
+
+```
+
+### 2.4. 背景background属性
+
+- background-color:背景色
+
+ - 直接给color属性值,同上面字体的color属性值
+ - transparent // 透明的,有点去掉背景的意思
+- background-image:背景图(说是有加多个背景的方式,疑惑先留在这里吧)
+
+ - none // 这是默认值,代表没有背景图
+ - url(图片路径)
+- background-repeat:图片的铺排方式
+
+ - repeat // 这是默认值,图片小了,一张铺不满,它就会在纵向以及横向复制铺完
+ - no-repeat // 就用原图大小,铺不满也无所谓
+ - repeat-x // 只在x方向复制,不管y方向
+ - repeat-y // 只在y方向复制,不管x方向
+- background-position:设置背景图像所在的位置
+
+ - { x-number | top | center | bottom } { y-number | left | center } // x、y两个方向的值
+ - background-position: 100px center;
+ - background-position: 100px 10%; //多种书写方式
+ - background-position: right top;
+ - background-position: left; /* 如果只有一个参数,默认y方向为50% */
+ - Tips:与这个同等级的一个属性 height: 2000px; 这个给一下吧,不给可能效果不是那么直观
+- background-attachment:设置背景==图像滚动与否==,滚动位置(scroll/fixed)
+
+ - scroll // 背景图会随着滚动条滑动
+ - fixed // 背景图会固定,不会随着滚动条滑动(无论滚动条滑到哪里,它始终在那个位置)
+
+
+Tips:以上都是针对background的属性,可以在一个声明中设置所有的背景属性
+
+语法:`background: color image repeat attachment position;`
+
+示例:
+
+```html
+
+
+ Title
+
+
+
+```
+
+### 2.5. CSS选择器补充
+
+#### 2.5.1 伪类选择器
+
+1. 超链接伪类:在支持CSS的浏览器中,链接的不同状态都可以以不同的方式显示(主要就是针对a标签)
+ - a:link 未访问的状态(这个是可以省略掉后面的==:link==,因为这是默认的)
+ - a:hover 鼠标悬停状态(即鼠标放上去,不点,它的状态也会改变)
+ - a:visited 已被访问状态(即已经点过)
+ - a:active 用户激活(即鼠标左键已经点下去但并未松开时)
+2. 表单: :focus (:focus表单获得焦点时触发样式,这么写,就是只要能获得焦点的都会有这个效果)
+ - input:focus { backgroud-color: yellow; } # 这里前面加了input就是说只有input框才会有这个效果,相当于特别指定了,没特别指定的话,那就是大家都可以
+3. :first-child 伪类来选择元素的第一个子元素
+
+示例:
+
+```html
+
+
+
+
+ 伪类选择器
+
+
+
+ 这是一个超链接
+
+
+
+
+ - aaaa
+ - aaaa
+ - aaaa
+ - aaaa
+
+
+
+```
+
+#### 2.5.2 属性选择器
+
+可以理解为,好比当有很多p标签,但是只是想对其中一部分的样式做改变,那么就可以通过这个方式:
+
+- [属性名]:包含有指定属性名的元素 # 这里可以理解属性名为字典的key
+
+ ```html
+
+
+
+
+ 属性选择器
+
+
+
+ 这是第一条内容
+ this is a name!
+
+
+ ```
+
+- [属性名=值]:属性名的值为指定值的元素 # 这里理解为要字典的key和value都要对应
+ 可以理解为当所有的key都相同时,就由不同的value值做出判定
+
+ ```html
+
+
+
+
+
+
+ 这是第一条内容
+ this is a name!
+
+
+ ```
+
+ ```html
+
+
+
+ 属性选择器
+
+
+
+
+
+ ```
+
+- [属性名~=值]:属性名的值包含指定值的元素 # 这个没试验出来,总有点问题
+
+- [属性名^=值]:属性名的值以指定值为开头的元素 # 注意:属性可以好几个,以空格隔开
+
+- [属性名$=值]:属性名的值以指定值为结尾的元素 # 有点类似于正则
+
+ ```html
+
+
+
+
+ 属性选择器
+
+
+
+
+
+
+ ```
+
+#### 2.5.3 关系选择器
+
+ 简单来说,就是针对同一个标签,若是其所在的层级不一样(被1或2或更多次嵌套),那么可以选择设定不一样的显示:
+
+- 以空格 所有的后代,无论被多少次嵌套,都会被选择
+- \> 这就只会选择儿子元素(第一级的),多次被嵌套的就不会被选中
+
++ \+ 兄弟元素选中,不好说明,看例子吧
+
+```html
+
+
+
+
+ 关系选择器
+
+
+
+
+ 这是儿子中的strong
+
+ 这几个字就赢应该无strong的效果这是span中的strong,算孙子
+
+
+
+ 这是儿子中的strong(只有这会有效果)
+
+ 这几个字就赢应该无strong的效果这是span中的strong,算孙子
+
+
+
+
+ - this is a line
+ - this is a line
+ - this is a line
+ - this is a line
+ - this is a line
+ - this is a line
+
+
+
+```
+
+### 2.6. CSS伪元素
+
+ 就理论而言,有些复杂了,我就不多写了。主要用的一个是after和before,他们是能在标签之外添加内容东西,主要的伪元素有:
+
+- ==:before== 在元素之前添加内容(这还不用去改标签内的内容,有点装饰器的味道)
+
+- ==:after== 在元素之后添加内容
+
+- ==:first-letter== 向文本的首字(母)添加特殊的样式
+
+- ==:first-line== 向文本的首行添加特殊的样式
+
+ \# 以上这几个(常用)前面可以是一个冒号,也可以是两个
+
+==::selection== ==::placeholder== ==::backdrop== # 这几个(不常用)前面只能是两个冒号
+
+示例:
+
+```html
+
+
+
+
+ 伪元素
+
+
+
+ 伪元素好难顶哦,多来一点内容不他铺满伪元素好难顶哦!!
+
+
+```
+
+## 三、JavaScript
+
+javasceript组成:
+
+1. ECMAscript javascript的语法:变量、函数、循环语句等语法都是按照这个规则编写的;
+2. ==DOM==文档对象模型:操作html和css的方法,如常用的document;
+3. ==BOM浏==览器对象模型:操作浏览器的一些方法,如 alert() 的弹窗,以及后面的浏览器定时器等。
+
+### 3.0. js库的使用
+
+ 以[panolens.js](https://github.com/pchen66/panolens.js)这个==360°全景视图==的js项目来说,在其[example](https://pchen66.github.io/Panolens/#Example)中随便选择一个,然后F12把对应的js文件、图片、html内容这些下载下来(这样是为了保证版本可用,下载最新版本的three.js可能会报错),然后再本地启动服务就可以本地看了。
+
+ 下面代码的核心是 body 中的代码,就是使用这相关的js库。
+
+```html
+
+
+
+
+
+ Panolens.js panorama image panorama
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+注意:
+
+- ==会发现在github上开源大部分的js库,都有一个名为build的文件夹,只需要直接用里面的.js文件就好了==,
+ - .js 是原始文件,格式方便看;
+ - min.js 是压缩后的对应的js文件,效果一样的
+- 这个项目还要依靠three.js,也是只要这个项目build的.js文件就行;
+- 都准备好后,本地打开这个html文件,还是无法看到图片,F12会看到报错,搜索后说是==跨域==的问题,但具体我也没有深究,解决方式说是要以服务的方式,于是 python -m http.server 8080 起一个服务,然后就能正常访问了。
+
+### 3.1. js引入方式
+
+```html
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
+```
+
+1. 第一种:==行间事件==:(主要用于事件)
+ - onclick是一个点击事件,==alert==代表弹窗,弹窗里面的内容是‘登录成功!’
+ - 一定注意:如果是这种方式的话,且有==多个标签嵌套的话==,js的代码(onclick="alert('登录成功!')")一定要放在最外面那个标签,不管那标签是个还是
+2. 第二种:==页面script标签嵌入==:(跟style一样,写到head中的)
+ - alert事件是用 包裹起来的
+3. 第三种:==外部导入==,
+ - 对应的js文件就只写了 alert("hello") 这么一句话
+
+注意:这是顺序执行的,这个页面所有出现弹窗的顺序是第三种-->第二种-->第一种,且第一种要点击才会弹窗
+
+### 3.2. js语法基础
+
+#### 3.2.1 变量、属性设置
+
+变量的声明调用:js是弱类型,就一个关键字`var`
+
+命名:第一个字符必须是字母、下划线或者美元符号($)
+
+var iNum = 12;
+
+var name = "hello";
+
+js有5种基本数据类型(如下): && 1种复合类型:object
+
+1. 数字类型
+2. 字符串类型
+3. 布尔类型(true或false)
+4. undefined类型(变量声明未初始化,它的值就是undefined)
+5. null类型(表示空对象,如果定义的变量将来准备保存对象,可以将变量初始化为null,在页面上获取不到对象,返回的值就是null)
+
+赋值属性、获取属性:
+
+```html
+
+
+
+
+ Title
+
+
+
+ 这是一个div
+
+
+```
+
+解读:
+
+- window.onload = function() {} 这里面的代码说这里面的内容最后来加载,因为是从上而下加载的原因,直接写里面的内容,或获取不到"div1",因为那时还没有;
+- document.getElementById 是固定写法,别写错了,byId的内容就是同过div标签中的 id 属性来获取到这个对象;
+- .sty.color = "red" 就是等于 这是一个div
+- style.fontSize 它在style中的写法应该是 style.font-size, js中的写法要求凡是带`-`的属性,就不要这个-,然后把后面的字母,也是s大写
+
+#### 3.2.2 js属性写法
+
+1. js里面属性写法和html属性写法一样;
+2. “class”属性要写成“className”;
+3. html中style属性里面的属性,有横杠的改成驼峰式,比如"style:font-size",改成"style.fontSize"
+
+下面这就是通过js代码直接改div2的样式为div1的css样式,解读:
+
+- (1)js代码最后把div2的class属性变为box1了,并没有直接去改div2中的代码;
+- (1)第10行:html中的class属性一定要写成className;css样式赋值时千万别带前面的点;
+- (2)注意第18行,[]这样去获取变量值和直接去.变量值 是一样的;
+- (3)==可通过`innerHTML`来获取标签中的内容或是修改==。
+
+```html
+
+
+
+
+ Title
+
+
+
+
+
+
+
+ 这是div1
+ 这是div2
+
+
+
+```
+
+#### 3.3.3 条件语句 ===
+
+运算符:
+
+1. 算术运算符:+ - * / %(模或者求余)
+
+2. 赋值运算符:= += -= *= /\* %=
+
+3. 条件运算符:== === > >= < <= != && || !
+
+ - == 类型不一样,数据一样也会相等,
+
+ - === 先比类型,类型都不一样,就绝不可能再相等了。
+
+ ```javascript
+
+ ```
+
+ - if else 还有 switch 以及 for 循环条件语句,跟c++的用法都一样
+
+4. 这里面,return还可阻止默认行为,写到这里吧。
+
+#### 3.3.4 数组|数组去重
+
+数组的各种常用方式:
+
+```javascript
+
+```
+
+Tips:
+
+- 多维数组:var li = [[1, 2, 3], ['a', 'b', 'c']] 用法一模一样,li[0]\[1]的结果就是2
+
+- 数组第(8)点用来给==数组去重==(Python可以参考哈)
+
+ ```javascript
+
+ ```
+
+- js通过for循环,来给网页添加数据,一般很少这么写吧,就是通过给innerHTML属性赋值,值是一些标签,这就跟前面web后端给前端传递网页数据类似了(视频:".\就业班\09 JavaScriptv\04-数组和循环语句\03for循环-实例.flv" 大概第10分钟左右)
+
+#### 3.4.5 字符串的方法
+
+```javascript
+
+```
+
+以下是网页上让输入两个整数,然后相加:(注意下面的==+==和=====可以不用标签包裹起来):
+
+```html
+
+ Document
+
+
+
+
+ +
+
+ =
+
+
+```
+
+### 3.3. 隐藏/显示样式
+
+ style中默认是开启了样式的,在style中display属性默认是空,也是代表开启了的:
+
+- 当设置style="display: block;"也是代表开启样式,
+- 如要关闭样式,style="display: none;" 所以要用过js来控制一个按钮开启或是关闭样式也很很常用:
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+#### document.getElementsByTagName
+
+除了上面的==document.getElementById==,常用的还有:
+
+ document.getElementsByTagName("li") 获取元素对象
+
+ 可以使用内置对象document中的==getElementsByTagName==方法来获取页面上的某一种标签,获取的是一个==选择集,不是数组==,但是==可以用下标的方式操作==选择集里面的标签元素:
+
+```html
+
+
+ Title
+
+
+
+
+
+
+
+```
+
+### 3.4. js函数的使用
+
+定义:function 函数名() {} // 可以定义参数及return返回值
+
+ 首先js会预解析,预解析会把变量的声明提前,即变量使用之前没有声明或定义,但在使用之后的代码里是有声明或是定义的,那么得到的变量的值就会是==undefined==,
+
+ 但如果是函数,只要有函数的实现,调用函数的代码在函数定义之前也是可以的,因为js的预解析会把声明提前。
+
+```javascript
+
+```
+
+且这个函数也可以传参,有返回值,是弱类型,各种都可以相加,形参前也不用加var
+
+```javascript
+
+```
+
+---
+
+js函数的定义。简单使用:(以下是在html中调用js代码中函数,不提倡使用)
+
+```html
+
+
+
+
+ function
+
+
+
+
+ 这是函数的学习
+
+
+
+
+```
+
+解读:
+
+- 先是定义了一个div,里面有onclick事件,点击文字,就会调用函数,实现谈弹窗;
+- div下紧跟了一个button,它也调用了一个函数,是用来点击修改div中的内容和样式;
+- 注意:这里函数的定义没有用==window.onload = function() {}==包裹起来,是因为函数的执行就在html代码里,可以不用最后再加载。
+
+但是以上,还是把js的代码写到html中,不要这种写法,js还是要单独写,接着上面的代码,做一点改造:(==这是把js代码写出来,去修改html中的属性==)
+
+```html
+
+ function
+
+
+
+ 这是函数的学习
+
+
+```
+
+Tips:
+
+- 上面的核心是7、8行,获取到button的变量,然后将其onclick属性赋值为定义的用来改变div标签属性的函数;
+
+- 这是用 window.onload = function() {} 包裹起来了的,不然直接顺序执行到第7行是不行的,都还没那个标签;
+
+- ==匿名函数==:
+
+ - 注意第8行,意义不大,直接就赋值的函数名,那不如就用匿名函数,那么第8行就可以写成
+
+ - 相当于定义里没要函数名了,直接 function () {}
+
+ ```javascript
+ // 改成匿名函数
+ obtn1.onclick = function () { //
+ var oDiv1 = document.getElementById("div1");
+ oDiv1.innerHTML = "把显示的数据改了";
+ oDiv1.style.color = "red";
+ oDiv1.style.fontSize = "30px";
+ }
+ ```
+
+
+#### 3.4.1. 案例,网页换肤
+
+案例,网页换肤:(==就是通过botton的onclick的点击事件,来改变css的href引入属性达到换肤==)
+
+(1)css文件:(.box1这种前面带点.的,html中属性引入时都是用的class=".box1")
+
+skin_orange.css
+
+```css
+body {
+ background-color: orange;
+}
+/*
+box, .box1, .box2 {
+ font-size: 30px;
+ border-radius: 15px;
+}
+*/
+/* 不应该上面那么写,都是 input 这个标签,直接input设置总的属性,再用里面的box1自定义增加不一样的 */
+input {
+ font-size: 30px;
+ border-radius: 15px;
+}
+.box1 {
+ color: orange;
+}
+.box2 {
+ color: hotpink;
+}
+```
+
+skin_pink.css
+
+```css
+body {
+ background-color: hotpink;
+}
+input {
+ font-size: 30px;
+ border-radius: 15px;
+ border: 0; /* 这就是不要边框那圈黑的 */
+}
+.box1 {
+ color: orange;
+}
+.box2 {
+ color: hotpink;
+}
+```
+
+skin.js
+
+```javascript
+window.onload = function () {
+ var btn1 = document.getElementById("btn1");
+ var btn2 = document.getElementById("btn2");
+ var skin_link = document.getElementById("my_link");
+
+ // 用函数来改变成导入的外部的css的href链接属性就好了
+ btn1.onclick = function () {
+ skin_link.href = "my_css/skin_orange.css";
+ }
+ btn2.onclick = function () {
+ skin_link.href = "my_css/skin_pink.css";
+ }
+}
+```
+
+skin.html
+
+```html
+
+
+
+
+ skin
+
+
+
+
+
+
+
+
+
+
+```
+
+#### 3.4.2. 封闭函数
+
+使用场景:
+ 在从别的地方导入进了js文件,里面有函数变量,自己如果不清楚,恰巧写了同名的变量、函数,就会将原来的功能覆盖掉,特别是新加功能时,很可能遇到覆盖旧功能的情况,那此时就要用封闭函数:
+
+格式简单的来说,就是 (function (){自己的函数、变量写这里})()
+
+```
+;(function () {
+ var num = 24;
+ function myalert() {
+ alert("hello");
+ }
+ alert(num);
+ myalert();
+})()
+```
+
+解读:
+
+- 最后一对括号()就相当于是调用这个封装函数了,那么第6、7行就会直接执行;
+- 如果这时这个文件导入的js文件中,也有一个变量叫做num,值是12,也有一个名为myalert的函数,这时在封装函数外面调用alert(num);得到的是12,myalert();也是执行它原本的内容,就不会被覆盖掉。
+- ==封装函数还有别的写法==:
+ - 把那对括号换成 ! ,即 !function () {自己的函数、变量写这里}()
+ - 把那对括号换成 ~ ,即 ~function () {自己的函数、变量写这里}()
+
+- 第一行那里加分号,是因为js代码可能会压缩程一行,避免跟别的代码混合在一起了,保险起见加个分号。
+
+### 3.5. 定时器
+
+定时器在javascript中的作用:
+
+1. 制作动画
+2. 异步操作
+3. 函数缓冲与节流
+
+定时器:
+
+- setTimeout(函数名,int) 只执行一次的定时器
+
+ - 函数名这里可以给一个匿名函数;
+ - int这里是给一个整数值,代表多少ms时间后,定时器执行
+
+- clearTimeout 关闭只执行一次的定时器
+
+ ```javascript
+
+ ```
+
+ ---
+
+- setInterval(函数名,int) 反复执行的定时器
+
+- clearInterval 关 闭反复执行的定时器
+
+ ```javascript
+
+ ```
+
+#### 3.5.1 定时器做动画
+
+第一步:简单的让一个div盒子,从==左边的地方往右边移动==:
+
+```html
+
+
+
+
+ 定时器动画
+
+
+
+
+
+
+
+
+```
+
+第二步:让这==来回反复的运动==:(只保留js的代码,其它都一样)
+
+```html
+
+```
+
+---
+
+ 如果是要做连续==滚动效果==,就看(".\就业班\09 JavaScriptv\06-定时器和变量作用域\02无缝滚动01.flv"),里面的思想还是不错:
+
+ 其实滚动就是把一张平面图通过 innerHTML = innerHTML + innerHTML 把标签里内容复制了一份,这样滑动时就可以了,当第二份即将滑动完时,做个判断,立马又把图定位回最开始的位置。
+
+#### 3.5.2 放鼠标暂停
+
+ 接着上面,一般自己动滑动时,把鼠标放上去就会暂停(就是把速度设为0),移开又动起来(把速度设回去),下面的代码只是示意:
+
+```
+var oDiv = document.getElementById("div1");
+var iSpeed = 2; // 原来在动
+var iNowSpeed = 0; // 一个变量
+// 匿名函数
+// 鼠标移入时,把此时速度存起来,再把速度设为0,就停止了
+oDiv.onmouseover = function () {
+ iNowSpeed = iSpeed;
+ iSpeed = 0;
+}
+// 鼠标移出时,就把速度设回来
+oDiv.onmouseout = function () {
+ iSpeed = iNowSpeed;
+}
+```
+
+解读:
+
+- oDiv是通过getElementById获取到的一个对象;
+- 鼠标事件跟 oDiv.onclick 这个是一个意思:
+ - 鼠标移入:oDiv.==onmouseove==
+ - 鼠标移出:oDiv.==onmouseout==
+
+#### 3.5.3 实时时钟
+
+实时显示的时钟:
+
+```html
+
+
+
+
+ 时钟
+
+
+
+
+
+
+
+
+```
+
+Tips:
+
+- 注意月份,还有星期几,星期几返回的也是数子,可以写一个switch语句将其转变成大写;
+- 注意要先让函数先单独执行一次,不然一进来页面是会空白一秒钟;
+- 分、秒这些,也会出现 5、6这钟,可以通过一个函数来将其变为05、06这种。
+
+#### 3.5.4 倒计时
+
+距离未来某一个时间的倒计时:
+
+```html
+
+
+
+
+ 倒计时
+
+
+
+
+
+
+
+```
+
+Tips:
+
+- 一定要注意==写未来的日期时,那个月份是从0开始,所以想要的月份要给小一个1==;
+- parseInt(3.99); 得到的结果就是 3
+
+### 3.6. 常用内置对象
+
+(注意加不加括号的使用)
+
+document:
+
+- document.getElementById(); // 通过id属性获取元素
+- document.getElementsByTagNameNS(); // 通过标签名获取元素
+- document.referrer // 获取上一个跳转页面的地址(需要服务器环境)
+ - 在浏览器的console中输出 document.referrer 就可以看到上一个页面的地址(比如没登录浏览商品,看到一个想买,登录后肯定是跳转回该商品界面的体验最好,那就要用到这个来记录上次的网页地址)
+
+location:
+
+- ==window.location.href== // 获取或者重定url地址
+
+ 示例:(不再是给button标签上加 标签来跳转,而是通过js的事件控制)
+
+ ```html
+
+
+
+
+ 内置对象
+
+
+
+
+
+
+ ```
+
+- ==window.location.search== // 获取html页面跟的参数
+
+ - index.html?aa456%789 //那么window.location.search获取的就是 ?aa456%789
+
+ - 注意,.html后一定是要紧跟==?==才行,跟其它符号或是内容,都会是404
+ 示例:
+
+ ```html
+
+
+
+
+ Title
+
+
+
+ hello
+
+
+ ```
+
+- window.location.hash // 获取页面锚点或者叫哈希值
+
+### 3.7. 调试 | Math对象
+
+调试的方法,
+
+- ==alert(一个变量名);== // 通过弹窗的方式将其打印出来=
+
+ - 这会阻止程序后续的运行
+
+- ==console.log(比如一个数组的名字);== // 就可以在浏览器的console里看到对应的数据=
+
+ - 这不会阻止程序运行,可直接在console控制台看数据
+ - console里面还有很多其它的方法,看后面用到再说吧
+ - Math是内置对象,有点像python自带的库,且还不需要导入
+ - 几个常用的Math对象中的函数:
+ - Math.random() // 获取0-1的随机数,不包括1
+ - Math.floor(5.6) // 向下取整,5
+ - Math.ceil(5.3) // 向上取整,6
+ - Math.round(n) // 四舍五入
+
+ 注意:==var num = (max - min) * Math.random() + min;==这种生成随机数的写法
+
+ ```html
+
+ ```
+
+- ==document.title== // 改变标题title的方式 (这就会看到页面标题一直在变)
+
+ ```html
+
+ ```
+
+
+
+
+
diff --git a/tutorial/README.md b/tutorial/README.md
index 3dd3544..f42913b 100644
--- a/tutorial/README.md
+++ b/tutorial/README.md
@@ -1,9 +1,17 @@
+- [cuda](./cuda/CUDA.md);
+
+- [工业相机、镜头参数介绍.md](./工业相机/工业相机、镜头参数介绍.md);
+- [三维重建](./三维重建/三维重建.md);
+
- [Anaconda和Pip.md](./Anaconda和Pip.md);
+- [AndroidStudio设置.md](./AndroidStudio设置.md);
- [CAD操作快捷键.md](./CAD操作快捷键.md);
- [Cmd命令大全.md](./Cmd命令大全.md);
- [Colab.txt](./Colab.txt);
+- [HTML文档.md](./HTML文档.md);
- [JetBrains相关软件的设置.md](./JetBrains相关软件的设置.md);
- [SRS推流.md](./SRS推流.md);
- [TensorRT.md](./TensorRT.md);
- [yolov5记录.md](./yolov5记录.md);
+- [只用FFmpeg+Win32实现一个播放器.md](./只用FFmpeg+Win32实现一个播放器.md);
diff --git a/tutorial/cuda/CUDA.md b/tutorial/cuda/CUDA.md
new file mode 100644
index 0000000..b2c44d2
--- /dev/null
+++ b/tutorial/cuda/CUDA.md
@@ -0,0 +1,350 @@
+## 一、介绍
+
+1. 试试这篇[文章](https://zhuanlan.zhihu.com/p/507678214)的代码,一个简单加法,再一起知乎这篇[理论](https://zhuanlan.zhihu.com/p/34587739)就先简答了解。
+2. 接着可看看这个[博主](https://www.zhihu.com/column/c_1188568938097819648)的这6篇文章,通过一个矩阵加法的例子,一步步讲了CUDA优化的若干种方法(我只感受了第一种,后面的优化就没写了)。
+3. 主要的系统学习就是谭升的[博客](https://face2ai.com/program-blog/)。
+4. SM:Streaming Multiprocessor
+
+进阶时用吧:
+
+- [cuda-sample](https://github.com/NVIDIA/cuda-samples):官方项目,用来介绍每一个特性的,比较多,一一看不现实,自己到时候酌情吧
+
+ 然后cuda 安装后,是自带了很多演示demo,它可能涉及得更广,每个都很强,可能都还会带有shader的编写。
+
+- [thrust](https://github.com/NVIDIA/thrust):英伟达开源的c++并行算法库,我看cuda的sample里有大量用到。
+- 看要要结合 《Professional CUDA C Programming》这本书 来做,[这](https://zhuanlan.zhihu.com/p/53773183)有一个算是中文导读吧。
+
+再再进阶:(开源项目代码)
+ 有很多的CUDA源码可以供我们慢慢学习,我这就简单给几个典型的Transformer系列的加速代码了。
+
+- [LightSeq](https://github.com/bytedance/lightseq):这是字节跳动开源的生成模型推理加速引擎,BERT、GPT、VAE等等全都支持,速度也是目前业界最快的之一。
+
+- [FasterTransformer](https://github.com/NVIDIA/DeepLearningExamples/tree/master/FasterTransformer):这是英伟达开源的Transformer推理加速引擎。
+
+- [TurboTransformers](https://github.com/Tencent/TurboTransformers):这是腾讯开源的Transformer推理加速引擎。
+
+- [DeepSpeed](https://github.com/microsoft/DeepSpeed):这是微软开源的深度学习分布式训练加速引擎。
+
+> ❝ 我mentor说,不需要看这些,然后甩给了我20行代码,说看懂了就行了。结果我看懂了,门还是没开QAQ,所以建议还是看看底层一些的原理,加深自己的理解,特别是我强推的那篇博客。
+> ❞
+
+英伟达官方教程,全是英文的,且太冗长了,我只是入门了解的话就算了吧(以后吃这个饭,进阶再说)
+
+- 英伟达官方的[CUDA编程教程](https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html)。
+- [这](https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html)也是英伟达官方的CUDA编程教程,不过侧重点在实践方面,比如如何编程才能最大化利用GPU特性提升性能,建议基础打好之后再来看这个。
+
+---
+
+最后看看这篇关于自定义算子op的[教程](https://zhuanlan.zhihu.com/p/360441891)。
+
+## 二、理论知识
+
+### 2.1. 简单demo
+
+ host指代CPU及其内存,而用device指代GPU及其内存。CUDA程序中既包含host程序,又包含device程序,它们分别在CPU和GPU上运行。同时,host与device之间可以进行通信,这样它们之间可以进行数据拷贝。典型的CUDA程序的执行流程如下:
+
+1. 分配host内存,并进行数据初始化;
+2. 分配device内存,并从host将数据拷贝到device上;
+3. 调用CUDA的核函数在device上完成指定的运算;
+4. 将device上的运算结果拷贝到host上;
+5. 释放device和host上分配的内存。
+
+- `__global__`:在device上执行,从host中调用(一些特定的GPU也可以从device上调用),返回类型必须是`void`,不支持可变参数参数,不能成为类成员函数。注意用`__global__`定义的kernel是异步的,这意味着host不会等待kernel执行完就执行下一步。
+- `__device__`:在device上执行,单仅可以从device中调用,不可以和`__global__`同时用。
+- `__host__`:在host上执行,仅可以从host上调用,一般省略不写,不可以和`__global__`同时用,但可和`__device__`,此时函数会在device和host都编译。
+
+
+
+一个很简单的.cu代码:main.cu # 来源是这篇[文章](https://zhuanlan.zhihu.com/p/507678214)。
+
+- 编译:==nvcc main.cu -o add_cuda==
+- 查看详细使用时间分析:==nvprof ./add_cuda==
+
+```c++
+#include
+#include
+
+// Kernel function to add the elements of two arrays
+// __global__ 变量声明符,作用是将add函数变成可以在GPU上运行的函数
+// __global__ 函数被称为kernel,
+// 在 GPU 上运行的代码通常称为设备代码(device code),而在 CPU 上运行的代码是主机代码(host code)。
+
+__global__
+void add(int n, float *x, float *y) {
+ int index = blockIdx.x * blockDim.x + threadIdx.x;
+ int stride = blockDim.x * gridDim.x;
+ for (int i = index; i < n; i+=stride) // 以上代码可以说固定了,出现次数多的话可以把它写成宏的形式,TensorRT.md中就是这么做的
+ {
+ y[i] = x[i] + y[i];
+ }
+
+}
+
+int main(void) {
+ cudaSetDevice(1); // 设置用哪张GPU
+ int N = 1<<20;
+ float *x, *y;
+
+ // 内存分配,在GPU或者CPU上统一分配内存
+ cudaMallocManaged(&x, N*sizeof(float));
+ cudaMallocManaged(&y, N*sizeof(float));
+
+ // initialize x and y arrays on the host
+ for (int i = 0; i < N; i++) {
+ x[i] = 1.0f;
+ y[i] = 2.0f;
+ }
+
+ // Run kernel on 1M elements on the GPU
+ int m_ThreadCount = 256; // 这也是 单个blockSize的大小,CUDA GPU 使用大小为 32 的倍数的线程块运行内核,因此 `blockSize` 的大小应该设置为32的倍数,例如128、256、512等
+ int numBlock = (N + m_ThreadCount - 1) / m_ThreadCount; // 确定 `blockSize` 之后(也就是m_ThreadCount),可以根据for循环的总个数`N`确定 `numBlock` 的大小;这样计算比直接N/m_ThreadCount更加保险,万一不能整除,c++的“/”会去掉小数。
+ add<<>>(N, x, y);
+
+ // CPU需要等待cuda上的代码运行完毕,才能对数据进行读取
+ cudaDeviceSynchronize();
+
+ cudaFree(x); // Free memory
+ cudaFree(y);
+ return 0;
+}
+```
+
+注:使用cudaMallocManaged 来分配内存,这种内存在表面上看在设备和主机端都能访问,但是内部过程和我们前面手动copy过来copy过去是一样的(tensorrt中就是host复制到device,运算完后再device复制到host),也就是memcopy是本质,而这个只是封装了一下。
+
+---
+
+开始系统,写一个hello_world.cu
+
+```c++
+#include // #include也可以,设置不需要头文件
+__global__ void hello_world()
+{
+ printf("GPU: Hello world!\n");
+}
+int main(int argc,char **argv) {
+ hello_world<<<1,10>>>(); // 1,10是随意的,这里
+ cudaDeviceReset();//if no this line ,it can not output hello world from gpu
+ return 0;
+}
+```
+
+ 注:cudaDeviceReset()没有则不能正常的运行,因为这句话包含了隐式同步,GPU和CPU执行程序是异步的,核函数调用后成立刻会到主机线程继续,而不管GPU端核函数是否执行完毕,所以上面的程序就是GPU刚开始执行,CPU已经退出程序了,所以我们要等GPU执行完了,再退出主机线程。
+
+### 2.2. 内存申请|拷贝类型
+
+内存管理:
+
+| 标准C函数 | CUDA C 函数 | 说明 |
+| :-------: | :---------: | :------: |
+| malloc | cudaMalloc | 内存分配 |
+| memcpy | cudaMemcpy | 内存复制 |
+| memset | cudaMemset | 内存设置 |
+| free | cudaFree | 释放内存 |
+
+最关键的一步,这一步要走总线:cudaError_t cudaMemcpy(void *dst, const void *src, size_t count, enum cudaMemcpyKind kind),内存拷贝过程,完成以下集中过程(cudaMemcpyKind kind):
+
+- cudaMemcpyHostToHost
+- cudaMemcpyHostToDevice
+- cudaMemcpyDeviceToHost
+- cudaMemcpyDeviceToDevice
+
+函数执行成功则会返回 cudaSuccess 这个枚举值,否则会返回 cudaErrorMemoryAllocation,使用这个指令可以把错误信息翻译成详细信息:char* cudaGetErrorString(cudaError_t error);
+
+### 2.3. grid、block
+
+一个核函数只能有一个grid,一个grid可以有很多个块block,每个块可以有很多的线程,如图:
+
+每个线程都执行同样的一段串行代码,怎么让这段相同的代码对应不同的数据?那就是让这些线程彼此区分开,依靠下面两个内置结构体确定线程标号:
+
+- blockIdx(线程块在线程网格grid内的位置索引)
+- threadIdx(线程在线程块block内的位置索引)
+
+两个内置结构体基于 uint3 定义,包含三个无符号整数的结构,通过三个字段来指定:
+
+- blockIdx.x
+- blockIdx.y
+- blockIdx.z
+- threadIdx.x
+- threadIdx.y
+- threadIdx.z
+
+上面这两个是坐标,要有同样对应的两个结构体来保存其范围,也就是blockIdx中三个字段的范围threadIdx中三个字段的范围:
+
+- blockDim
+- gridDim
+
+他们是dim3类型(基于uint3定义的数据结构)的变量,也包含三个属性x,y,z.
+
+网格和块的维度一般是二维和三维的,也就是说一个网格通常被分成二维的块,而每个块常被分成三维的线程。
+
+可以使用dim3类型的grid维度和block维度配置内核,也可以使用int类型的变量,或者常量直接初始化:
+
+```
+kernel_name<<<4,8>>>(argument list);
+```
+
+上面这条指令的线程布局是:![image-20230228162145290](illustration/image-20230228162145290.png)
+
+当主机启动了核函数,控制权马上回到主机,而不是主机等待设备完成核函数的运行,前面的cudaDeviceReset();就是为了等待,同样想要主机等待设备端执行可以用下面这个指令:
+
+```
+cudaError_t cudaDeviceSynchronize();
+```
+
+### 2.4. 错误检查宏
+
+错误的处理:基本函数执行后都会返回一个cudaError_t的枚举值,在开发阶段要检查错误,可以定义一个宏,在release时可以不要:
+
+```
+#define CHECK(call)\
+{\
+ const cudaError_t error=call;\
+ if(error!=cudaSuccess)\
+ {\
+ printf("ERROR: %s:%d,",__FILE__,__LINE__);\
+ printf("code:%d,reason:%s\n",error,cudaGetErrorString(error));\
+ exit(1);\
+ }\
+}
+```
+
+### 2.5. 打印硬件信息
+
+```c++
+#include
+#include
+
+void printGPUInfo(int deviceNum) {
+
+ cudaSetDevice(deviceNum); // 多gpu这里设置。
+
+ cudaDeviceProp devProp;
+ cudaError_t error = cudaGetDeviceProperties(&devProp, deviceNum);
+ if (cudaSuccess != error) return;
+
+ std::cout << "使用GPU device " << deviceNum << ": " << devProp.name << std::endl;
+ std::cout << "SM的数量:" << devProp.multiProcessorCount << std::endl;
+ std::cout << "每个线程块的共享内存大小:" << devProp.sharedMemPerBlock / 1024.0 << " KB" << std::endl;
+ std::cout << "每个线程块的最大线程数:" << devProp.maxThreadsPerBlock << std::endl;
+ std::cout << "每个SM的最大线程数:" << devProp.maxThreadsPerMultiProcessor << std::endl;
+ std::cout << "每个SM的最大线程束数:" << devProp.maxThreadsPerMultiProcessor / 32 << std::endl;
+}
+
+int main() {
+ printGPUInfo(1);
+ return 0;
+}
+```
+
+## 三、内存模型,流,事件
+
+cuda内存模型、层次结构,文章[地址](https://face2ai.com/CUDA-F-4-1-%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E6%A6%82%E8%BF%B0/)。
+
+CUDA内存模型相对于CPU来说那是相当丰富了,GPU上的内存设备有:
+
+- 寄存器
+- 共享内存
+- 本地内存
+- 常量内存
+- 纹理内存
+- 全局内存
+
+![image-20230306132029261](illustration/image-20230306132029261.png)
+
+
+
+为了避免寄存器溢出,可以在核函数的代码中配置额外的信息来辅助编译器优化,比如:
+
+```
+__global__ void
+__lauch_bounds__(maxThreadaPerBlock,minBlocksPerMultiprocessor)
+kernel_name(...) {
+ /* kernel code */
+}
+```
+
+在核函数定义前加了一个 关键字 **lauch_bounds**,然后他后面对应了两个变量:
+
+1. maxThreadaPerBlock:线程块内包含的最大线程数,线程块由核函数来启动
+2. minBlocksPerMultiprocessor:可选参数,每个SM中预期的最小的常驻内存块参数。
+ 注意,对于一定的核函数,优化的启动边界会因为不同的结构而不同
+ 也可以在编译选项中加入 ==-maxrregcount=32== 来控制一个编译单元里所有核函数使用的最大数量。
+
+因为共享内存是块内线程可见的,所以就有竞争问题的存在,也可以通过共享内存进行通信,当然,为了避免内存竞争,可以使用同步语句:
+
+```
+void __syncthreads();
+```
+
+---
+
+CUDA变量声明总结:
+
+| 修饰符 | 变量名称 | 存储器 | 作用域 | 生命周期 |
+| :---------: | :------------: | :----: | :----: | :------: |
+| | float var | 寄存器 | 线程 | 线程 |
+| | float var[100] | 本地 | 线程 | 线程 |
+| \__share__ | float var* | 共享 | 块 | 块 |
+| \__device__ | float var* | 全局 | 全局 | 应用程序 |
+| \__constant | float var* | 常量 | 全局 | 应用程序 |
+
+---
+
+cuda流以及同步方式,还有事件,[地址](https://face2ai.com/CUDA-F-6-1-%E6%B5%81%E5%92%8C%E4%BA%8B%E4%BB%B6%E6%A6%82%E8%BF%B0/)。cudaError_t cudaEventCreate(cudaEvent_t* event);
+
+ CUDA编程和普通的C++不同的就是,我们有两个“可运算的设备”也就是CPU和GPU这两个东西,这种情况下,他们之间的同步并不是每一步指令都互相通信执行进度的,设备不知道主机在干啥,主机也不是完全知道设备在干啥。但是数据传输是同步的,也就是主机要等设备接收完数据才干别的,也就是说你爸给你寄了一袋大米,然后老人家啥也不做,拨通电话跟你保持通话不停的问你收到了么?直到你回答收到了,这就是同步的。内核启动就是异步的,你爸爸又要给你钱花,去银行给你汇了五百块钱,银行说第二天到账,他就可以回家该干嘛干嘛了,而不需要在银行等一晚,第二天你收到了,打个电话说一声就行了,这就是异步的。异步操作,可以重叠主机计算和设备计算。
+前面用的cudaMemcpy就是个同步操作,我们还提到过隐式同步——从设备复制结果数据回主机,要等设备执行完。当然数据传输有异步版本:
+
+```
+cudaError_t cudaMemcpyAsync(void* dst, const void* src, size_t count,cudaMemcpyKind kind, cudaStream_t stream = 0);
+```
+
+ 值得注意的就是最后一个参数,stream表示流,一般情况设置为默认流,这个函数和主机是异步的,执行后控制权立刻归还主机,当然我们需要声明一个非空流:
+
+声明:cudaStream_t a;
+
+资源还是要用cudaStreamCreate分配的:cudaError_t cudaStreamCreate(cudaStream_t* pStream);
+
+当然后面就要回收资源,回收方式:
+
+```
+cudaError_t cudaStreamDestroy(cudaStream_t stream);
+```
+
+ 这个回收函数很有意思,由于流和主机端是异步的,你在使用上面指令回收流的资源的时候,很有可能流还在执行,这时候,这条指令会正常执行,但是不会立刻停止流,而是等待流执行完成后,立刻回收该流中的资源。这样做是合理的也是安全的。
+ 当然,我们可以查询流执行的怎么样了,下面两个函数就是帮我们查查我们的流到哪了:
+
+```
+cudaError_t cudaStreamSynchronize(cudaStream_t stream);
+cudaError_t cudaStreamQuery(cudaStream_t stream);
+```
+
+ 这两条执行的行为非常不同,cudaStreamSynchronize会阻塞主机,直到流完成(tensorrt中的代码就是用的和这个)。cudaStreamQuery则是立即返回,如果查询的流执行完了,那么返回cudaSuccess否则返回cudaErrorNotReady。
+
+---
+
+记录事件和计算运行时间:一段简单的记录事件时间间隔的代码
+
+```
+// create two events
+cudaEvent_t start, stop;
+cudaEventCreate(&start);
+cudaEventCreate(&stop);
+// record start event on the default stream
+cudaEventRecord(start);
+// execute kernel
+kernel<<>>(arguments);
+// record stop event on the default stream
+cudaEventRecord(stop);
+// wait until the stop event completes
+cudaEventSynchronize(stop);
+// calculate the elapsed time between two events
+float time;
+cudaEventElapsedTime(&time, start, stop);
+// clean up the two events
+cudaEventDestroy(start);
+cudaEventDestroy(stop);
+```
+
+这段代码显示,我们的事件被插入到空流中,设置两个事件作为标记,然后记录他们之间的时间间隔。
+cudaEventRecord是异步的,所以间隔不准,这是特别要注意的。
\ No newline at end of file
diff --git a/tutorial/cuda/illustration/image-20230228160937292.png b/tutorial/cuda/illustration/image-20230228160937292.png
new file mode 100644
index 0000000..7a57a4e
Binary files /dev/null and b/tutorial/cuda/illustration/image-20230228160937292.png differ
diff --git a/tutorial/cuda/illustration/image-20230228162145290.png b/tutorial/cuda/illustration/image-20230228162145290.png
new file mode 100644
index 0000000..aa99b19
Binary files /dev/null and b/tutorial/cuda/illustration/image-20230228162145290.png differ
diff --git a/tutorial/cuda/illustration/image-20230306132029261.png b/tutorial/cuda/illustration/image-20230306132029261.png
new file mode 100644
index 0000000..eefbbec
Binary files /dev/null and b/tutorial/cuda/illustration/image-20230306132029261.png differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525102037704.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525102037704.png"
new file mode 100644
index 0000000..9e6c4be
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525102037704.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525104418154.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525104418154.png"
new file mode 100644
index 0000000..86ba0b8
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525104418154.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525104819991.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525104819991.png"
new file mode 100644
index 0000000..9b1b1c2
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525104819991.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525111133365.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525111133365.png"
new file mode 100644
index 0000000..d9df9c9
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525111133365.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145441209.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145441209.png"
new file mode 100644
index 0000000..77aef4c
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145441209.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145806443.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145806443.png"
new file mode 100644
index 0000000..a0c92ec
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145806443.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145857867.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145857867.png"
new file mode 100644
index 0000000..a6d8dc0
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525145857867.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525151233328.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525151233328.png"
new file mode 100644
index 0000000..824bb22
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525151233328.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525151832862.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525151832862.png"
new file mode 100644
index 0000000..a4f071d
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230525151832862.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605092737527.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605092737527.png"
new file mode 100644
index 0000000..13a4726
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605092737527.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100436904.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100436904.png"
new file mode 100644
index 0000000..8021ac6
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100436904.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100512718.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100512718.png"
new file mode 100644
index 0000000..c486e70
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100512718.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100540368.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100540368.png"
new file mode 100644
index 0000000..8c6aab4
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605100540368.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605101024892.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605101024892.png"
new file mode 100644
index 0000000..b066b3d
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605101024892.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102429734.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102429734.png"
new file mode 100644
index 0000000..3f775e4
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102429734.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102523419.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102523419.png"
new file mode 100644
index 0000000..ba236d2
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102523419.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102556188.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102556188.png"
new file mode 100644
index 0000000..fa041c7
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102556188.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102721171.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102721171.png"
new file mode 100644
index 0000000..aa49854
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102721171.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102745815.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102745815.png"
new file mode 100644
index 0000000..0978861
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102745815.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102803202.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102803202.png"
new file mode 100644
index 0000000..18a99b2
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605102803202.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605104257991.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605104257991.png"
new file mode 100644
index 0000000..3f98c9d
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605104257991.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605105212251.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605105212251.png"
new file mode 100644
index 0000000..e385de7
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230605105212251.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230612110529958.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230612110529958.png"
new file mode 100644
index 0000000..522b86d
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230612110529958.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230612174211201.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230612174211201.png"
new file mode 100644
index 0000000..5202786
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230612174211201.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230613175330552.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230613175330552.png"
new file mode 100644
index 0000000..30fc723
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230613175330552.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230613175343611.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230613175343611.png"
new file mode 100644
index 0000000..ce76583
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230613175343611.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230614131941271.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230614131941271.png"
new file mode 100644
index 0000000..5a627f1
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230614131941271.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230614132042610.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230614132042610.png"
new file mode 100644
index 0000000..5457197
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230614132042610.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230619133312711.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230619133312711.png"
new file mode 100644
index 0000000..e217d3f
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230619133312711.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230711172912698.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230711172912698.png"
new file mode 100644
index 0000000..cdc789b
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230711172912698.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230713091915350.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230713091915350.png"
new file mode 100644
index 0000000..5f48cd9
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230713091915350.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230714140029956.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230714140029956.png"
new file mode 100644
index 0000000..aaff8b9
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230714140029956.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230714140105720.png" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230714140105720.png"
new file mode 100644
index 0000000..2af430f
Binary files /dev/null and "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/illustration/image-20230714140105720.png" differ
diff --git "a/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/\344\270\211\347\273\264\351\207\215\345\273\272.md" "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/\344\270\211\347\273\264\351\207\215\345\273\272.md"
new file mode 100644
index 0000000..a976e07
--- /dev/null
+++ "b/tutorial/\344\270\211\347\273\264\351\207\215\345\273\272/\344\270\211\347\273\264\351\207\215\345\273\272.md"
@@ -0,0 +1,656 @@
+- colmap相关:
+ - 数据格式读取的代码:这个[项目](https://github.com/Fyusion/LLFF)的“Using your own poses without running COLMAP”,去看这个标题里,然后点进去它的超链接,有关于colmap的bin格式数据的读取,后续要深入的话,可以参考。
+ - colmap配置与使用-CSDN博客:[地址](https://blog.csdn.net/zhingzt/article/details/127960418)。
+ - Colmap 实用教程:[地址](https://blog.csdn.net/a40850273/article/details/127982257)。
+- 后面看到的三维重建相关的:还没试过
+ - [OpenSfM](https://github.com/mapillary/OpenSfM)。
+ - [COLMAP_SLAM](https://github.com/3DOM-FBK/COLMAP_SLAM)。
+- meshlab有时候编辑obj会闪退,可以先将其另存为ply格式的,再打开这ply格式的进行编辑,编辑好后再另存外obj格式的。
+- gltf格式相关的项目:[地址1](https://github.com/microsoft/glTF-SDK)、[地址2](https://github.com/KhronosGroup/glTF)。
+
+## 一、概述
+
+重建基本流程:特征提取与匹配->稀疏重建->稠密重建->表面重建。
+
+以下很多使用来自于B站[视频](https://www.bilibili.com/video/BV1F54y1T7m5?t=1.6)。(图有点问题,openMVS可以进行Dense cloud)
+
+![image-20230605092737527](illustration/image-20230605092737527.png)
+
+了解一下VisualSFM:
+
+> 官网[地址](http://ccwu.me/vsfm/index.html)。github[地址](https://github.com/mrquincle/visualsfm)。可以直接下载它的bin运行,是带GUI的,
+>
+> “VisualSFM is free for personal, non-profit or academic use.”
+>
+> 它得到的数据格式就是 “.nvm” 的
+
+---
+
+英伟达开源的 instant-ngp 是基于nerf,它的一个数据预处理,也是用colmap实现的,主要就是用colmap的特征提取、特征匹配、去畸变,stereo(后面两个不确定有用到没),
+
+## 二、colmap+meshlab
+
+ 主要还是参考B站这个[视频](https://www.bilibili.com/video/BV1F54y1T7m5?t=310.1);纹理贴图(meshlab实现的)知乎这篇[文章](https://zhuanlan.zhihu.com/p/605108237),里面还带有命令行,这文章对应的也有B站[视频](https://www.bilibili.com/video/BV1x64y1x7Kw?t=71.0);这两篇meshlab的贴图放这里做个参考吧,后续看能不能用到,[1](https://blog.csdn.net/zhouii/article/details/129374990)、[2](https://zhuanlan.zhihu.com/p/84832004)。
+
+ 即:稀疏重建、稠密重建是用的colmap;mesh时可用colmap或是meshlab(这次用的clomap);最终的纹理贴图是用的meshlab。示例教程如下:
+
+### 2.1 colmap安装
+
+ 首先要搞定的是colmap的linux环境编译,一定要带cuda、gui的编译,不带gui会使OpenGL被禁用,一些使用了OpenGL关键字的代码就会编译error,官方[地址](https://colmap.github.io/install.html)。
+
+下面是可能遇到的错误及解决办法:
+
+- cmake时可能遇到的错误:
+
+ > CMake Error at CMakeLists.txt:255 (message):
+ > You must set CMAKE_CUDA_ARCHITECTURES to e.g. 'native', 'all-major', '70',
+ > etc. More information at
+ > https://cmake.org/cmake/help/latest/prop_tgt/CUDA_ARCHITECTURES.html
+
+ 解决办法,指定CMAKE_CUDA_ARCHITECTURES参数的值(可能需要cmake3.24及以上版本):
+ cmake -DCMAKE_INSTALL_PREFIX=/opt/colmap/my_install -DCMAKE_CUDA_ARCHITECTURES='native' ..
+
+ 若不行的话,再指定自己的CUDA版本试试,比如 cuda_11.4,那就是-DCMAKE_CUDA_ARCHITECTURES='114'
+
+- 运行时还可能遇到这样的错误:
+
+ > “./colmap: error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory” # 也可能是其它动态库
+
+ 解决办法:全局搜索一下libQt5Core.so.5,前面安装环境都搞完的话,在docker容器中的/usr/lib/x86_64-linux-gnu里是有的,那就执行如下命令:
+ strip --remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
+
+ 要注意 libQt5Core.so.5 所在的实际路径
+
+### 2.2 GUI操作
+
+这次使用的数据集是一组大象elephant,共有52张图。
+
+#### 2.2.1 colmap重建
+
+使用colmap做第一阶段:
+
+ 数据准备:cd ~/project/elephant # elephant文件夹中只有一个名为“images”的文件夹,这个文件夹里有那52张大象图
+
+ 启动:colmap gui # 打开gui
+
+下面就是gui中的操作:
+
+- File-->new project,然后new一个database的名字,图片就选刚刚的“images”文件夹
+ ![image-20230525102037704](illustration/image-20230525102037704.png)
+
+- Processing-->Feature extraction # 直接Extract执行,使用默认参数进行特征提取(几秒)
+
+- Processing-->Feature matching # 直接Run执行,使用默认参数(num_threads给-1代表根据硬件最大线程数),参数问题后续还要去熟悉,(这是会用cuda的,就很快,10来秒,不然cpu的话时间就很长很长)
+
+- 稀疏重建:Reconstruction-->Start reconstruction # 2080Ti的cpu占用在一半左右跳动,(时间大概在2分23秒)
+
+ - **导出稀疏云:File->Export model as # 一定要选择"Bundler(*.out)"这个数据格式 **
+ ![image-20230525104418154](illustration/image-20230525104418154.png)
+
+ - 上面导出后会得到“my_sparse.out”、“my_sparse.out.list.txt”这两个文件,这很重要,后续做贴图的时候是需要的 # 这一步非常重要,一定是要有的
+
+- 稠密重建:Reconstruction-->Dense reconstruction # 会弹出来一个界面,
+
+ - 然后select新建一个名为"dense"的文件夹 # options里面可以改一些参数,暂时都是用的默认的
+ ![image-20230525104819991](illustration/image-20230525104819991.png)
+ - 去畸变:点击 Undistortion # 几秒钟
+ - Stereo:(这个阶段时间很长,两个GPU利用率基本都是拉满的,cpu没啥占用,用时 `17分钟`),这个过程也能用meshlab去实现,那就是cpu跑,可能就要几天了(还是要看重建量的大小)
+ - Fusion:很快,几十秒吧,然后会弹回一个提示,点yes(推荐)就是得到稠密点云(得到fused.ply、fused.ply.vis这两个文件),应该也是可以点击No,然后去手动Export
+ ![image-20230525111133365](illustration/image-20230525111133365.png)
+ - 最终进行mesh,这里选择的是“Poisson” # GPU没用,CPU利用是拉满了的),用时 `4分钟`左右吧
+ - 以上步骤结束后,弹出的提示不重要,在“dense”目录下就有了“meshed-poisson.ply”模型数据了
+
+注:是不是考虑在稠密点云结果的基础上直接做目标检测,然后删除掉一些东西,然后在目标点云上做Poisson Mesh,这样的结果会小很多,然后再在这个上面做贴图,不然噪点太多,mesh建面出来的结果是出不来的。
+
+ 此路径下的`project.ini`就是项目跑完后存下来的默认配置文件,里面很多参数可作参考。
+
+#### 2.2.2 meshlab贴图
+
+注:==meshlab按住ctrl就可以拖拽模型的左右上下位置==。
+
+ 使用meshlab制作贴图和减面 # 参考的的主要是还是这个[视频](https://www.bilibili.com/video/BV1F54y1T7m5?t=270.1)。以下的步骤是在colmap完成了泊松建面后,再在meshlab中做的贴图。可以尝试先点云配准,把目标物的稠密点云搞出来,然后再poisson-Mesh。
+
+- 第一步用meshlab打开前面保存的 sparse.out 稀疏云,然后它会让打开 sparse.out.list.txt #记得要把用于重建的52张图放到这俩文件夹的同一个文件夹
+
+- 用meshlab导入colmap中生成的 meshed-poisson.ply mesh,然后右上角隐藏掉稀疏云
+
+- 选中大象的部分,然后反选,把多余的mesh部分面删掉,
+
+ - 面还是太多了,简化一下,(选中了大象的目标区域,也许给50万面就够了)
+ ![image-20230525145441209](illustration/image-20230525145441209.png)
+
+- 制作纹理:
+
+ - 制作之前,要删除一些面: # 不然下一步会报错
+ ![image-20230525151832862](illustration/image-20230525151832862.png)
+ ![image-20230525145806443](illustration/image-20230525145806443.png)
+
+ - 分辨率一般给到4096: # 这个功能在texture中
+ ![image-20230525145857867](illustration/image-20230525145857867.png)
+
+- 导出成obj,同时把纹理这些导出
+- 如果要再做简化,可以用代码去删掉vertexColor,甚至法线Vn和f对应的第三个值。
+
+---
+
+ 很多时候可能用下面这种方式好一些,先稠密点云把不要的删了,再来poissonMesh(这个mesh用colmap、meshlab都行),不然有的数据干扰太多,一个通用化的参数就不好设置,得到的mesh是有问题的,就成了一团。上面也讲到过,colmap做泊松建面时出不来结果,就可能是先用meshlab将稠密点云选中目标区再反选,删除所有噪点,再mesh重建。
+
+在meshlab中对稠密点云进行建面时,深度至少选择10,参考colmap我给的13
+![image-20230525151233328](illustration/image-20230525151233328.png)
+
+一顿计算之后,就再做贴图就好了,跟上面一样,经过验证发现这样的方式也是可以。
+
+### 2.3 ==命令行实现==
+
+colmap是支持命令的行,但编译的时候还是要记得编译带gui的,colmap命令行[地址](https://colmap.github.io/cli.html?highlight=colmap%20gui)。
+
+以下脚本路径中只有一个名为"images"的文件夹,里面是全部的图像。
+
+- ==方式一==:总的一次生成
+
+ ```sh
+ # cd到一个只有"images"文件夹的文件夹里执行如下:
+ colmap automatic_reconstructor --workspace_path . --image_path ./images/ # 这里会自动做泊松建面
+
+ colmap model_converter --input_path sparse/0 --output_path ./my_sparse --output_type Bundler # ./my_spase是自己起的文件名
+ ```
+
+ 注:
+
+ - 只有8张图那个例子,用这计算(gui也得不行),是得不到结果的,大概的提示是:
+ “=> Could not register, trying another image.”
+ “=> No good initial image pair found.”
+ 最后一行还有“ERROR: failed to create sparse model”
+
+ 所以后续工程化要去做判定,这里直接就返回提示数据问题。
+ - 第二个命令是为了导出格式好用于后面贴图,它执行之后就会得到 spase.bundle.out、spase.list.txt
+
+- ==方式二==:各阶段命令分开,便于修改参数
+
+ ```sh
+ # 1.特征提取
+ colmap feature_extractor \
+ --database_path ./colmap.db \
+ --image_path ./images
+
+ # 2.特征匹配
+ colmap exhaustive_matcher --database_path ./colmap.db
+
+ # 3.稀疏重建
+ mkdir sparse
+ colmap mapper \
+ --database_path ./colmap.db \
+ --image_path ./images --output_path ./sparse
+
+ # 4.深度重建(dense/0/ 是为了跟方式一路径保持一致)
+ mkdir -p dense/0
+ # 深度重建-去畸变
+ colmap image_undistorter --image_path ./images \
+ --input_path ./sparse/0 \
+ --output_path ./dense/0 \
+ --output_type COLMAP \
+ --max_image_size 2000
+ # 深度重建-下面这会把所有gpu利用率拉满,可设置指定哪张gpu
+ colmap patch_match_stereo \
+ --workspace_path ./dense/0 \
+ --workspace_format COLMAP \
+ --PatchMatchStereo.geom_consistency true
+ # 深度重建-融合(fused.ply就是稠密点云)
+ colmap stereo_fusion \
+ --workspace_path ./dense/0 \
+ --workspace_format COLMAP \
+ --input_type geometric \
+ --output_path ./dense/0/fused.ply
+
+ # 5.最后把稀疏重建的内容转换一个格式,方便后续做贴图
+ # my_sparse是自己起的文件名
+ colmap model_converter \
+ --input_path ./sparse/0 \
+ --output_path ./my_sparse \
+ --output_type Bundler
+
+ # 以下的mesh建面可以选做,或许对fused.ply做了去噪后再来mesh
+ colmap poisson_mesher \
+ --input_path ./dense/0/fused.ply \
+ --output_path ./dense/0/meshed-poisson.ply
+ ```
+
+ colmap poisson_mesher时,有一个参数是 --PoissonMeshing.depth,就用它默认是13就好了,建议是用大于10的值,我用过15,大概13用时2m57s,15用时3m49s,15的结果更大,面数更多,但效果没看到明显的更好。
+
+ 还有 delaunay 算法mesh建面:colmap delaunay_mesher --input_path ./dense --output_path ./dense/meshed-delaunay.ply
+
+ ```sh
+ # 稠密重建那里还有一种别的方式,只简单试了,但试的量不够,效果不好说
+ colmap patch_match_stereo \
+ --workspace_path ./dense \
+ --workspace_format COLMAP \
+ --PatchMatchStereo.geom_consistency false # 1.这里改了
+
+ colmap stereo_fusion \
+ --workspace_path ./dense \
+ --workspace_format COLMAP \
+ --input_type photometric \ # 2.主要就是这个参数
+ --output_path ./dense/fused.ply
+ ```
+
+ 注意:
+ WARNING: Bundler only supports `SIMPLE_RADIAL`, `RADIAL`, and pinhole camera models.
+
+ 默认是使用的 SIMPLE_RADIAL 模式,然后instant-ngp的colmap数据预处理用的是 OPENCV ,这就不能转Bundler格式,就无法用meshlab做图片贴图。
+
+---
+
+meshlab暂时还不支持命令行,代码是QT写的,比较难剥离,去试试pymeshlab
+
+### 2.4 注意事项
+
+1. 用colmap计算式,如果图太少或有问题,无论gui还是命令行,都不会报错,但是是得不到结果的,自动重建会有这样的提示:(只有8张图那个数据)
+ “=> Could not register, trying another image.”
+ “=> No good initial image pair found.”
+2. colmap feature_extractor 特征提取时,还有很多参数,instant-ngp是用的--ImageReader.camera_model OPENCV 这里会报错,还是就用默认值。
+ “对于大型图像,在CPU上提取SIFT特征可能会消耗每个线程大量的RAM。考虑减小最大图像大小和/或第一个八度,或者手动限制提取线程的数量。如果计算机有足够的内存支持当前设置,请忽略此”
+3. 在meshlab中对稠密点云进行了简化,然后再mesh,得到的结果,贴图在win10中打开不对,meshlab中没问题,贴图小的细节就没了,这种方式还待考虑。
+4. ma
+
+### 2.5 效果必看重点
+
+数据的影响,周围噪点问题,数据好,colmap能直接建面出来
+
+ 用colmap、meshlab建面的不同:colmap是没有就没有,那一块就是有缺失的,就很容易出现破面缺面,周围一些小的噪点数据就会不管;meshlab是会自己去补充,物体缺的,他补充出来就没那么好,各种凹凸不平,就是不怎么会缺面破面,同时它会把周围的一些小噪点都连接起来,形成大片没有的面,手动好去掉这些面,但是自动的难度就很大。
+
+#### 2.5.0 ==win查看导出的obj模型没有颜色==
+
+用meshlab导出的obj模型在win自带的3D查看器没有颜色,大抵是因为.mtl描述文件的问题
+
+```
+newmtl material_0
+Ka 0.200000 0.200000 0.200000 # white
+Kd 1.000000 1.000000 1.000000 # white
+# Ks 1.000000 1.000000 1.000000 # black # 这就是有问题的,主要是这行,它的值太影响颜色,全部改成0,其它的不变都是ok的。
+Ks 0.000 0.000 0.000 # black # 这样子就能正常显示了
+
+Tr 1.000000
+illum 2
+Ns 0.000000
+map_Kd texture.png
+```
+
+ok的:
+
+```
+Ka 1.000 1.000 1.000 # white
+Kd 1.000 1.000 1.000 # white
+Ks 0.000 0.000 0.000 # black
+```
+
+#### 2.5.1 周围有很多噪点的图片数据
+
+原始图片:周围有很多噪点:
+
+
+- colmap得到的稠密点云+直接此稠密点云poisson-mesh,效果很差:
+
+ 稠密点云(有很多杂质):
+
+ 直接根据上面的稠密点云直接mesh的结果(根本出不来效果):
+
+
+- 在上述杂质很多的稠密点云上做了手动去噪(删掉90%以上不感兴趣的点云区域),再用colmap的泊松建面,效果还是可以的:
+ ![image-20230605101024892](illustration/image-20230605101024892.png)
+
+#### 2.5.2 干净数据与周围有噪点数据对比
+
+原图-->colmap直接得到的稠密点云-->从未处理的稠密点云直接colmap来mesh得到的模型:
+
+- 干净数据:
+
+
+ colmap直接得到的稠密点云:
+
+
+ 从未处理的稠密点云直接colmap来mesh得到的模型:
+
+
+- 周围有很多噪点:
+
+
+ colmap直接得到的稠密点云:
+ ![image-20230605102745815](illustration/image-20230605102745815.png)
+
+ 从未处理的稠密点云直接colmap来mesh得到的模型:
+ ![image-20230605102803202](illustration/image-20230605102803202.png)
+
+总结:干净的数据在一定程度上可直接出来好的结果,但一定程度上可以说是无需再做3D点云的一个处理,会节省很多时间和工作量。
+
+#### 2.5.3 colmap与meshlab建面的差别
+
+ 当稠密点云数据一样,且都是用Poisson建面算法,colmap与meshlab得到的模型结果也是有比较大的差异的:
+
+- colmap会舍弃掉一些数据不足的点,模型周围小的噪点机会就没干扰,但缺点就是会造成模型本身一些点位不够的位置的一个破面:
+
+
+- meshlab则会把所有点都保留,那模型周围的一些噪点也会被连接起来,生成大片毫无意义的面,还需手动去除,但优点是会把模型本身数据不足的地方还是补全取来,虽然因为数据不足补全的面很是凹凸不平,精度有很大的问题,但是不会有破面的出现:
+
+
+## 三、openMVS
+
+### 3.1 安装
+
+这主要是用来做mesh建面和贴图的。需要cuda才能运行。
+
+- windows版本:预编译好的二进制版本,[下载地址](https://github.com/cdcseacave/openMVS_sample)。
+- linux版本:找到它的源码,看它的“Dockerfile_CUDA”文件,然后按照里面的方式去编译就好好了.
+ - 注意:编译Eign时一定要加cmake -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda/ .. 不然在make编译项目时会报很多错误,类似于“no suitable constructor exists to convert from "float" to "Eigen::half”,而且还有其它的错误,也差不多是由这引起的,ubuntu版本尽量20.04往上。
+ - 在cmake时可能会报 not found GLFW3,需要去下源码自己编译安装,不然编译时不会编译View这个程序,后续不同格式模型转换就可能会有问题。(好像也可以 apt-get install libglfw3-dev,源码编译也很简单)
+
+---
+
+ 非常重要的注意事项:使用openmvs时,`图片的后缀名一定要是小写`,可以看它源码“libs/IO/Image.cpp”中的 CImage* CImage::Create(LPCTSTR szName, IMCREATE mode)方法,然后它是不支持jpeg或者格式为大写的,可以直接改源码,改在jpg里面,参考[这](https://blog.csdn.net/weixin_46367784/article/details/125065337)。
+
+ 否则像大象那个数据“DSC07775.JPG”,其实没问题,但因为代码写法,就会报错“failed loading image header”,然而图片的路径确是实际存在的。如下:
+![image-20230612110529958](illustration/image-20230612110529958.png)
+
+这是一方面,还有一个方面是,环境有问题,导出虽然编译出来了,但是运行这里一直报这个错误,然后用它的“Dockerfile_CUDA”文件搞的新一个镜像容器就是ok的。
+
+### 3.2 openMVG重建
+
+B站[视频](https://www.bilibili.com/video/BV1F54y1T7m5?t=1024.8),openMVG+openMVS,主要参考的是这个[教程](https://www.freesion.com/article/74461469369/)。效果很一般,很多东西都出不来,(但可以看下它关于openMVS的使用)
+
+ 使用:直接下载openMVG的win上编译好的版本,然后使用里面的“python tutorial_demo.py”,可以直接运行它的示例demo,如果是自己的数据集,直接在tutorial_demo.py中修改自己的数据地址,后续的openMVS的使用根据上面教程来就可。
+
+ 自定义数据的话,tutorial_demo.py中要改自己的参数,要获取以像素为单位的相机焦距,这个参数一定要指定,不然openMVG_main_IncrementalSfM就没结果,就是下面的 -f,它的值算法公式:-f 1.2×max(img_w, img_h),[这](https://github.com/openMVG/openMVG/wiki/OpenMVG-on-your-image-dataset)是官方说明。
+
+`pIntrisics = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_SfMInit_ImageListing"), "-i", input_dir, "-o", matches_dir, "-f", "2755"] )`
+
+基本可以放弃这个openMVG了。
+
+这是大象的效果,就没效果,桃子的直接出不来:
+
+
+用openMVG创建的数据,再用openMVS来处理,几乎没有效果,elephant、桃子等,都没好的效果。直接暂时放弃这。
+
+### 3.3 colmap重建
+
+ [教程](https://blog.csdn.net/weixin_44543463/article/details/125628410)地址参考。简单来说,还是colmap那一套,然后后续的减面、贴图处理用openMVS。跟超链接教程有些不同的是,colmap的sparse数据不需要转成TXT格式,.bin格式可以直接转换。
+
+#### 3.3.1 colmap仅稀疏重建
+
+ 这是仅用colmap进行稀疏重建,然后拿到数据直接用openMVS进行稠密重建、mesh面化、网格细分(不是必须,但这会直接减面,效果也会更好)、贴图(这里也会导出模型,默认为ply,还有obj、glb、gltf可选)。
+
+注:==仅使用稀疏重建,一定要指定ImageReader.camera_model为“PINHOLE”的model==。
+
+ 把脚本放进colmap的“*.db”的同级目录运行就好了,最终生成的结果再“mvs”文件夹中。
+
+```sh
+# 1.特征提取
+colmap feature_extractor --database_path ./colmap.db --image_path ./images --ImageReader.camera_model PINHOLE # 这里一定要指定PINHOLE,不然openMVS后面会报错“error: no valid cameras (make sure they are in PINHOLE model)”
+
+# 2.特征匹配
+colmap exhaustive_matcher --database_path ./colmap.db
+# 3.稀疏重建
+mkdir sparse
+colmap mapper --database_path ./colmap.db --image_path ./images --output_path ./sparse
+
+# 上面是colmap的稀疏重建,下面是用openMVS做处理了
+
+cp ./sparse/0/*.bin ./sparse/ # 不用把.bin文件转成.txt
+mkdir colmap_sparse_mvs && cd colmap_sparse_mvs
+
+# 1.转换colmap数据为mvs数据
+InterfaceCOLMAP -i .. -o scene.mvs --image-folder ../images/
+
+# 2.稠密重建
+DensifyPointCloud scene.mvs # 或 -i scene.mvs -o 自己命名.mvs
+# 2.稠密重建之可以带mask掩码(背景是黑的,这里就是忽略像素为0的,还可以是其他值)
+DensifyPointCloud scene.mvs --ignore-mask-label 0 --mask-path ../mask/
+注:存的mask掩码图,一定都要以“.mask.png”作为结尾,一定。
+
+# 3.mesh网格化
+ReconstructMesh scene_dense.mvs # scene_dense.mvs是上一步不给-o参数默认生成的
+# 4.生成精细网格(用cuda比cpu快太多了)
+RefineMesh scene_dense_mesh.mvs --max-face-area 16 --cuda-device 1 # 其它都默认用cuda,这步默认是用的cpu,scene_dense_mesh.mvs是上一步不给-o参数默认生成的
+# 5.贴图并导出三维模型
+TextureMesh scene_dense_mesh_refine.mvs # 可以 -o 指定生成模型名字
+# 5这步会直接成对应的名为 scene_dense_mesh_refine_texture.ply模型和scene_dense_mesh_refine_texture.png这贴图
+# 5.这一步也可以在贴图时同时选择成成的格式,默认为ply,还可选 obj、glb、gltf
+TextureMesh scene_dense_mesh_refine.mvs --export-type obj
+# TextureMesh的参数“--texture-size-multiple”,从源码看了,不为0的话就return ((sizeTex+mult-1)/mult)*mult; 这个mult就是传进来的参数--texture-size-multiple,只会让贴图更大,不会让其变小,已经实验过了
+```
+
+以上命令的一些可选参数,可参看一下[官方文档](https://github.com/cdcseacave/openMVS/wiki/Usage),就几个,也没参数文档,还是看源码吧。
+
+ 优点:速度很快,colmap的稀疏重建很快,openMVS的速度也很快,经过测试,一个模型的处理,==能把时间控制在10分钟左右==。
+
+- 使用 TextureMesh 进行格式的转换选择更多,比官方说的用View来转好用。
+- RefineMesh细分网格时,参数“--max-face-area”的默认值是32,是针对由稀疏点云直接网格化的数据;官方文档中,针对由稠密点云网格化的数据,给的是 --max-face-area 16
+- 若给的名字不对,不会报错也不会有提示,很快运行完没结果,就要去看是不是名字给错了。
+- DensifyPointCloud scene.mvs && ReconstructMesh scene_dense.mvs && RefineMesh scene_dense_mesh.mvs --max-face-area 16 --cuda-device 1 && TextureMesh scene_dense_mesh_refine.mvs
+
+
+
+TransformScene scene_dense_mesh_refine.mvs --transform-file ./trans.txt
+
+#### 3.3.2 colmap稠密重建
+
+ 即用colmap完成整个稀疏、稠密重建,仅使用openMVS的mesh面化,网格细分、贴图导模型等。把下面的脚本放进colmap的“*.db”的同级目录运行就好了,最终生成的结果再“mvs”文件夹中。
+
+```sh
+# 1.特征提取(这里用默认的SIMPLE_RADIAL也是可以的,并不需要像3.3.1那样指定为PINHOLE)
+colmap feature_extractor --database_path ./colmap.db --image_path ./images
+# 2.特征匹配
+colmap exhaustive_matcher --database_path ./colmap.db
+# 3.稀疏重建
+mkdir sparse
+colmap mapper --database_path ./colmap.db --image_path ./images --output_path ./sparse
+# 4.稠密重建(这很消耗时间)
+mkdir -p dense/0
+colmap image_undistorter --image_path ./images --input_path ./sparse/0 --output_path ./dense/0 --output_type COLMAP --max_image_size 2000
+colmap patch_match_stereo --workspace_path ./dense/0 --workspace_format COLMAP --PatchMatchStereo.geom_consistency true
+colmap stereo_fusion --workspace_path ./dense/0 --workspace_format COLMAP --input_type geometric --output_path ./dense/0/fused.ply
+
+# 上面是colmap的稀疏重建,下面是用openMVS做处理了
+
+mkdir colmap_dense_mvs && cd colmap_dense_mvs
+# 1.转换colmap数据为mvs数据
+InterfaceCOLMAP -i ../dense/0/ -o scene.mvs --image-folder ../dense/0/images/
+# 2.这就没有了DensifyPointCloud整个步骤
+ReconstructMesh scene.mvs && RefineMesh scene_mesh.mvs --max-face-area 16 --cuda-device 1 && TextureMesh scene_mesh_refine.mvs --export-type obj # 最后导模型时可看上面
+```
+
+注:
+
+- 使用这种方式,colmap特征提取时并不需要指定“PINHOLE”模式,用别的也行。
+- 上面第18行InterfaceCOLMAP,处理时有一个操作Reading points,就会去读取colmap第12行生成的“fused.ply”以及“fused.ply.vis”(必须要,没有也能跑,到得到的模型就是一团,有问题),以及生成的深度图、normal图等。
+- 缺点:效果并没有说就比3.3.1好,甚至有的还会差一点,然后因为colmp的稠密重建会比较耗时,所以==整体时间可能会花到30分钟左右==。
+- --max_image_size 给的参数的值的说明,看[这](https://github.com/colmap/colmap/issues/1920)。默认值给的是3200。
+
+---
+
+**3.3.1、3.3.2效果对比**:
+
+ 就小黄人数据来说,在--ImageReader.camera_model PINHOLE 模式下,使用colmap做了稀疏、稠密重建,然后再把这两种得到的数据用openMVS做处理,发现openMVS用colmap稀疏重建的数据得到的结果更好。
+
+ 可能colmap在做稠密重建的时候把一些比较离散的点给去掉了,所以小黄人腿部缺失。同时也可以看到第一幅图(稀疏)后面的报纸空白的面比第二幅图(稠密)的更大,即在保留更多细节的时候就会留住更多噪点。
+
+- colmap仅稀疏重建,然后openMVS做处理:
+ ![image-20230614131941271](illustration/image-20230614131941271.png)
+- colmap稀疏重建后再稠密重建,openMVS在此基础上再做处理:
+ ![image-20230614132042610](illustration/image-20230614132042610.png)
+
+#### 3.3.3 与instant-ngp的预处理的对比
+
+记录了一下instant-ngp使用colmap做稀疏重建的命令:
+
+```sh
+# 1.特征提取
+colmap feature_extractor --ImageReader.camera_model OPENCV --ImageReader.camera_params "" --SiftExtraction.estimate_affine_shape=true --SiftExtraction.domain_size_pooling=true --ImageReader.single_camera 1 --database_path colmap.db --image_path "images"
+# 2.特征匹配
+colmap exhaustive_matcher --SiftMatching.guided_matching=true --database_path colmap.db
+# 3.稀疏重建
+mkdir sparse
+colmap mapper --database_path colmap.db --image_path "images" --output_path sparse/
+colmap bundle_adjuster --input_path sparse/0/ --output_path sparse/0/ --BundleAdjustment.refine_principal_point 1
+```
+
+colmap mapper --database_path colmap.db --image_path "images" --output_path sparse/ && colmap bundle_adjuster --input_path sparse/0/ --output_path sparse/0/ --BundleAdjustment.refine_principal_point 1 && colmap model_converter --input_path ./sparse/0 --output_path ./my_sparse.ply --output_type PLY
+
+与2.3colmap默认命令行重建区别:
+
+- 1.特征提取以及2.特征匹配都加了自己的一些参数。
+- 增加了第8行的colmap bundle_adjuster处理。
+
+---
+
+ 对比效果:可能不好确定,有数据的偶然性,但就这次的“礼盒数据”实验来看,同时仅使用colmap做稀疏重建,得到的数据再用openMVS做处理,最终模型的结果是:==使用colmap默认参数稀疏重建的最终效果比使用instnt-ngp的这种colmap稀疏重建的效果要好==(有的数据也一样不好)。
+![image-20230613175330552](illustration/image-20230613175330552.png)
+![image-20230613175343611](illustration/image-20230613175343611.png)
+
+对比:
+
+- 标签1处,下图明显比上图好多了。
+- 标签2处,下图的破面比上图大太多。
+
+---
+
+### 3.4 openMVS的boundingbox
+
+ openMVS可以设置感兴趣区域的boundbox的坐标,还可以使用.mask掩码的方式,(后者还未探究,前者基本搞定),这个的思路来自官方的[Issue](https://github.com/cdcseacave/openMVS/issues/787)。
+
+- 先用colmap做稀疏点云的重建,后转化弄成openMVS的数据格式,InterfaceCOLMAP -i .. -o scene.mvs --image-folder ../images/ # scene.mvs得到的就是稀疏点云
+- 查看此点云的感兴趣格式:DensifyPointCloud scene.mvs --export-roi-file original.txt # 关于参数--export-roi-file用-h是看不到的,是隐藏了的,可以去源码中参看,关于 original.txt 得到的参数,也是从源码中去看含义,一层层点进去,最终是在"libs/Comman/[OBB.h](https://github.com/cdcseacave/openMVS/blob/f62b38d5d02266452bb16dbcf153963001c09f93/libs/Common/OBB.h#L45)"中的:
+ ![image-20230619133312711](illustration/image-20230619133312711.png)
+- 拿到 original.txt 后,去手动修改后面的参数(主要是减小这些参数,结果对比看这“C:\Users\Administrator\Desktop\elephant-boundbox-openMVS-千万别删”),将改了的txt保存为changed.txt,然后回写原来的scene.mvs:DensifyPointCloud scene.mvs -o changed.mvs --import-roi-file changed.txt
+ - length:应该是左右一起对称减少
+ - axis:只减少的下半段,(然后一些圆的边缘,也会被修得有点像矩形)
+ - length:只减少的上半段,(然后一些圆的边缘,也会被修得有点像矩形)
+- 接下去就是做一样的稠密重建、mesh建面、网格细分、做贴图
+ DensifyPointCloud changed.mvs && ReconstructMesh changed_dense.mvs --cuda-device 1 && RefineMesh changed_dense_mesh.mvs --max-face-area 16 --cuda-device 1 && TextureMesh changed_dense_mesh_refine.mvs -o result.obj --export-type obj
+
+### 3.5 mask
+
+就还是使用3.5.1的方式吧,其它的多少有点问题。
+
+#### 3.5.1 openMVS加mask
+
+推荐这种方式:图片放在./images/123.jpg,mask放在./mask/123.mask.png (一定是“.mask.png”结尾,前面的名字是对应图片的名字)
+
+==这是colmap仅稀疏重建,跟上面一样,然后在openMVS中加mask==。
+
+- openMVS-2.1版本会自动close-hole,但是DensifyPointCloud没有加入mask的参数;
+- openMVS-2.2版本可以加入mask掩码可,但似乎会有大的破面。
+- DensifyPointCloud scene.mvs --ignore-mask-label 0 --mask-path ../mask/
+ - 这就是把mask中像素值为0的部分(一般为黑色背景)全部忽略掉,其它像素值都会保留,当然也可以给其他值。
+ - TextureMesh这步也可以加这两个参数,但目标明确的话,就直接从DensifyPointCloud这步处理了,这会加快后续的处理速度,因为直接去掉了背景噪点。
+
+#### 3.5.2 用mask把原图直接进行处理
+
+这是把image用mask直接进行处理,然后把结果存下来,去进行重建:
+
+ 把原图和mask图位或叠加后的图直接去做重建:就是这种:(去掉了背景)
+
+
+分两种方式:(都是用的peach数据)
+
+- 全程仅用colmap处理:(最中得不到结果)
+ - 在peach数据的稀疏重建后还得到了“No good initial image pair found.”,但还是可以做。不可以做的后面还有提示“ERROR: failed to create sparse model”;
+ - 但最终得到的“meshed-poisson.ply”是出不来效果的。
+- colmap稀疏重建+openMVS后续处理:(有结果)
+ - 这里用的是上一个方式peach用colmap稀疏重建的结果,稀疏重建结果拿到openMVS中去重建,还是得到了结果;
+ - 但是桃子的底部比3.5.1方式缺失得更多。
+
+#### 3.5.3 colmap加mask
+
+ colmap也可以加mask,图片是在./images/123.png,那mask对应的就是./mask/123.png.png #即原图名(带后缀的)+".png",命令:
+
+```sh
+colmap feature_extractor --database_path ./colmap.db \
+ --image_path ./images \
+ --ImageReader.mask_path ./mask/ \
+ --ImageReader.camera_model PINHOLE
+```
+
+结果:怎样做都不推荐
+
+- 全程仅用colmap:
+ - 针对peach数据:它的稠密点云“fused.ply”还有很多杂质;然后用这稠密点云mesh的时间特别长,基本上都是一小时起步(最近一次都2个小时),但最终得到的“meshed-poisson.ply”却是完全没出来结果的。
+ - 针对elephant数据:它的稠密点云“fused.ply”几乎没任何变化;然后这稠密点云mesh的时间也是特别长,基本一小时起步(最近一次卡了一下午都没出结果),最终得到的“meshed-poisson.ply”是有结果,但是背景完全没去掉。
+- colmap稀疏重建+openMVS做后续:即一开始colmap就加了mask
+ - 针对peach数据:能出来结果,但是问题较多:
+
+ - 针对elephant数据:也能出来结果,但还剩杂质:
+ ![image-20230714140105720](illustration/image-20230714140105720.png)
+
+### 3.6 transfer matrix
+
+由于colmap做出来的模型都是倒着的,需要将其摆正,现在openMVS加了旋转矩阵。
+
+ 在TransformScene.cpp中,参数是“--transform-file”也可以是“-t”,我去修改了它的源码并提交了PR。
+
+ 以后模型做完细分后来做模型的旋转(虽然测试过在做深度重建前就可以先执行这旋转,但还是最后来做旋转吧)
+
+```sh
+# scene_dense_mesh_refine.mvs是细分后得到的
+TransformScene scene_dense_mesh_refine.mvs --transform-file ./trans.txt
+```
+
+- 其中./trans.txt中存放的变换矩阵如下:(这是绕X轴旋转180°)
+
+ > 1 0 0 0
+ > 0 -1 0 0
+ > 0 0 -1 0
+ > 0 0 0 1
+
+ 计算机图形学中:齐次坐标有利于进行仿射变换,可以看这[教程](https://blog.csdn.net/lbwnbnbnbnbnbnbn/article/details/125309749)的变换矩阵,写得非常好。
+
+- 这里把3D绕轴旋转的变换矩阵写这里:(旋转多少度,带进去α就好了)
+ ![image-20230711172912698](illustration/image-20230711172912698.png)
+ 旋转一个模型就会是:变换矩阵×[x, y, z, 1]^T^
+
+ 同样的,最后生成的obj模型,或是其它的模型,要绕x轴旋转180°,可以代码直接读取模型,然后根据旋转矩阵的特性,x值不变,y值变为相反数、z值也变为相反数,可以结合3D场景进行想象。
+
+
+
+
+
+
+
+要改空洞处的默认颜色:
+
+TextureMesh scene_dense_mesh_refine.mvs --empty-color 16711719 # 绿色填充
+
+- 默认颜色是:->default_value(0x00FF7F27) // 这是一个十六进制的颜色,传入的时候要传入一个整型;
+- 它前面颜色给的是BGR([源码](https://github.com/cdcseacave/openMVS/blob/0d9460537d53a7fc1c6e3c24da771963985c26fb/libs/MVS/SceneTexture.cpp#L2248C72-L2248C72)中看的),后面应该是透明度。 0x00FF0027(红色),用c++直接把它输出就会得到一个整型==16711719==,然后传参时必须传这个整型,0x0000FF27(绿色),感觉它的颜色有些混乱,不完全是BGR这么来的,上面举例的这两个颜色就是证明。
+
+
+
+
+
+openMVS默认得到的.ply模型文件可以直接导入到meshlab中做减面的处理,再导出成obj格式。
+
+
+
+### colmap直接解决纯色的问题,肯定是不行的。所以考虑结合instant-ngp一起使用
+
+
+
+cp *.ply *.mtl *.obj *.jpg *.png ~/3d_datas/
+
+
+
+
+
+最后放一个使用open3d进行贴图的[教程](https://zhuanlan.zhihu.com/p/569974846),还未使用过,放这里吧。
+
+
+
+
+
+ply转换:colmap model_converter --input_path ./sparse/0 --output_path ./my_sparse.ply --output_type PLY
+
+
+
+## 四、Neus
+
+不支持做贴图:https://github.com/Totoro97/NeuS/issues/4
+
+有效果,但需要的数据预处理
\ No newline at end of file
diff --git "a/tutorial/\345\217\252\347\224\250FFmpeg+Win32\345\256\236\347\216\260\344\270\200\344\270\252\346\222\255\346\224\276\345\231\250.md" "b/tutorial/\345\217\252\347\224\250FFmpeg+Win32\345\256\236\347\216\260\344\270\200\344\270\252\346\222\255\346\224\276\345\231\250.md"
new file mode 100644
index 0000000..ce0fdd3
--- /dev/null
+++ "b/tutorial/\345\217\252\347\224\250FFmpeg+Win32\345\256\236\347\216\260\344\270\200\344\270\252\346\222\255\346\224\276\345\231\250.md"
@@ -0,0 +1,997 @@
+这是教程的[博客地址](https://www.cnblogs.com/judgeou/p/14724951.html)。写的非常好,一步步循序渐进,一共有三个,这下面的算是第一个教程跟着写的代码,也还没整完,因为没有GPU硬件加速,走不下去了。 从[这里](https://www.freeaihub.com/post/109842.html)来的。
+
+- 把本地视频地址换成rtsp也是可以直接使用的
+
+- 要去看博客,这个要把链接器->系统->子系统 设置为“窗口” ,不然会说无法解析main; 同理,其它是普通main函数作为入口的,一定要把这个选项设置为“控制台”,不然就是无法解析WinMain。
+
+- 在linux上,关于ffmpeg使用cuda硬件解码、软解码,转到opencv的Mat(需要opencv编译时支持cuda),还要有英伟达的==h264_cuvid==,放个[代码](https://github.com/chinahbcq/ffmpeg_hw_decode)参考吧(有更进一步实际需求时再去深入吧)。硬解码,也参考[官方文档](http://ffmpeg.org/doxygen/trunk/hw_decode_8c-example.html)(==函数API,定义解释这些也可以在这里面搜索==)和[这](https://www.cnblogs.com/gongluck/p/10827950.html)。
+
+- 打印错误:(把返回的错误数字代表的信息打印出来)
+
+ ```c++
+
+ // 一般头文件里都有包含这个 av_make_error_string
+ char av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ #define av_err2str(errnum) av_make_error_string(av_error, AV_ERROR_MAX_STRING_SIZE, errnum)
+
+ int ret = {/* */};
+ std::cout << av_err2str(ret) << std::endl;
+ ```
+
+- [FFmpeg解封装、解码音频和视频(分别使用OpenGL和OpenAL播放)](https://blog.csdn.net/GrayOnDream/article/details/122158294)(参考吧)
+
+---
+
+准备工作:
+
+- 看下面120行的RegisterClass(&wndClass); # 要在属性设置中的“配置属性-->常规-->字符集(改成Unicode)”;
+- 属性设置中,在属性-->链接器-->系统-->子系统(改为窗口,不能是控制台) # 最好的方法出现了,子系统的值改成==未设置==,这样,两种都可以直接使用。
+ - 设置为“窗口” ,不然会说`无法解析的外部符号main`; # 注意拿到别人的代码是WinMain作为入口的,直接运行报这个错,怎么去改;
+ - 其它是普通main函数作为入口的,一定要把这个选项设置为“控制台”,不然就是无法解析WinMain。
+
+### 第一阶段:只是把第一帧的画面,黑白显示出来
+
+```c++
+#include
+#include
+#include
+#include
+
+extern "C" {
+#include
+#pragma comment(lib, "avcodec.lib")
+
+#include
+#pragma comment(lib, "avformat.lib")
+
+#include
+#pragma comment(lib, "avutil.lib")
+}
+
+struct Color_RGB {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+
+// 获取第一帧画面
+AVFrame* getFirstFrame(const char* filepath) {
+ AVFormatContext *fmtCtx = nullptr;
+ avformat_open_input(&fmtCtx, filepath, NULL, NULL);
+ avformat_find_stream_info(fmtCtx, NULL);
+
+ int VideoStreamIndex;
+ AVCodecContext *vcodecCtx = nullptr;
+ for (int i = 0; i < fmtCtx->nb_streams; i++) {
+ AVStream *stream = fmtCtx->streams[i];
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ // 这是软解码
+ const AVCodec *codec = avcodec_find_decoder(stream->codecpar->codec_id);
+ VideoStreamIndex = i;
+ vcodecCtx = avcodec_alloc_context3(codec);
+ avcodec_parameters_to_context(vcodecCtx, fmtCtx->streams[i]->codecpar);
+ avcodec_open2(vcodecCtx, codec, NULL);
+ }
+ }
+
+ while (1) {
+ AVPacket *packet = av_packet_alloc();
+ int ret = av_read_frame(fmtCtx, packet);
+ if (ret == 0 && packet->stream_index == VideoStreamIndex) {
+ ret = avcodec_send_packet(vcodecCtx, packet);
+ if (ret == 0) {
+ AVFrame *frame = av_frame_alloc();
+ ret = avcodec_receive_frame(vcodecCtx, frame);
+ if (ret == 0) {
+ av_packet_unref(packet);
+ avcodec_free_context(&vcodecCtx);
+ avformat_close_input(&fmtCtx);
+ return frame;
+ }
+ else if (ret == AVERROR(EAGAIN)) {
+ av_frame_unref(frame);
+ continue;
+ }
+ }
+ }
+ av_packet_unref(packet);
+ }
+}
+
+
+/*
+ YUV420P格式会把Y、U、V三个值分开存储到三个数组,AVFrame::data[0] 就是Y通道数组,我们简单的把亮度值同时放进RGB就可以实现黑白画面了。接下来写一个函数对处理出来的RGB数组进行渲染,我们这里先使用最传统的GDI绘图方式,,但是这种方式太慢了
+*/
+//void StretchBits(HWND hwnd, const std::vector &bits, int width, int height) {
+// auto hdc = GetDC(hwnd);
+// for (int x = 0; x < width; x++) {
+// for (int y = 0; y < height; y++) {
+// auto &pixel = bits[x + y * width];
+// SetPixel(hdc, x, y, RGB(pixel.r, pixel.g, pixel.b)); // 主要是SetPixel这个函数效率太低
+// }
+// }
+// ReleaseDC(hwnd, hdc);
+//}
+
+void StretchBits(HWND hwnd, const std::vector &bits, int width, int height) {
+ auto hdc = GetDC(hwnd);
+ BITMAPINFO bitinfo = {};
+ auto &bmiHeader = bitinfo.bmiHeader;
+ bmiHeader.biSize = sizeof(bitinfo.bmiHeader);
+ bmiHeader.biWidth = width;
+ bmiHeader.biHeight = -height; // 注意负号,否则会画面颠倒
+ bmiHeader.biPlanes = 1;
+ bmiHeader.biBitCount = 24;
+ bmiHeader.biCompression = BI_RGB;
+
+ // StretchDIBits 函数就快了很多
+ StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, &bits[0], &bitinfo, DIB_RGB_COLORS, SRCCOPY);
+ ReleaseDC(hwnd, hdc);
+
+}
+
+
+// 主函数入口
+int WINAPI WinMain(
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPSTR lpCmdLine,
+ _In_ int nShowCmd
+) {
+ SetProcessDPIAware();
+
+ auto className = L"MyWindow";
+ WNDCLASSW wndClass = {};
+ wndClass.hInstance = NULL;
+ wndClass.lpszClassName = className;
+ wndClass.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ };
+
+
+ // 下面这个宏函数的定义(可点进去),要看是否启用了Unicode编译,不然类型不行,这个网址一个介绍:https://blog.csdn.net/huashuolin001/article/details/95620424
+ RegisterClass(&wndClass);
+ auto window = CreateWindow(className, L"Hello World 标题", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, NULL, NULL);
+
+ ShowWindow(window, SW_SHOW);
+
+
+ //
+ std::string file_path = "C:\\Users\\Administrator\\Videos\\keypoint_result.mp4";
+ AVFrame *firstframe = getFirstFrame(file_path.c_str());
+ int width = firstframe->width;
+ int height = firstframe->height;
+ std::vector pixels(width * height);
+ for (int i = 0; i < pixels.size(); i++) {
+ uint8_t r = firstframe->data[0][i];
+ uint8_t g = r;
+ uint8_t b = r;
+ pixels[i] = { r, g, b };
+
+ }
+
+ StretchBits(window, pixels, width, height);
+
+
+ MSG msg;
+ while (GetMessage(&msg, window, 0, 0) > 0) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ return 0;
+}
+```
+
+---
+
+### 第二阶段:改成不移动鼠标也能自动播放
+
+- 这里面还有一个节点,就是会自动播放,但是需要鼠标放在上面
+
+```c++
+#include
+#include
+#include
+#include
+
+extern "C" {
+#include
+#pragma comment(lib, "avcodec.lib")
+
+#include
+#pragma comment(lib, "avformat.lib")
+
+#include
+#pragma comment(lib, "avutil.lib")
+}
+
+struct Color_RGB {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+
+// 把获取第一帧那个函数拆分了一下
+struct DecoderParam {
+ AVFormatContext *fmtCtx;
+ AVCodecContext *vcodecCtx;
+ int width;
+ int height;
+ int VideoStreamIndex;
+};
+
+void InitDecoder(const char* filepath, DecoderParam ¶m) {
+ AVFormatContext *fmtCtx = nullptr;
+ avformat_open_input(&fmtCtx, filepath, NULL, NULL);
+ avformat_find_stream_info(fmtCtx, NULL);
+
+ AVCodecContext *vcodecCtx = nullptr;
+ for (int i = 0; i < fmtCtx->nb_streams; i++) {
+ const AVCodec *codec = avcodec_find_decoder(fmtCtx->streams[i]->codecpar->codec_id);
+ if (codec->type == AVMEDIA_TYPE_VIDEO) {
+ param.VideoStreamIndex = i;
+ vcodecCtx = avcodec_alloc_context3(codec);
+ avcodec_parameters_to_context(vcodecCtx, fmtCtx->streams[i]->codecpar);
+ avcodec_open2(vcodecCtx, codec, NULL);
+ }
+ }
+ param.fmtCtx = fmtCtx;
+ param.vcodecCtx = vcodecCtx;
+ param.width = vcodecCtx->width;
+ param.height = vcodecCtx->height;
+}
+
+AVFrame* RequestFrame(DecoderParam ¶m) {
+ auto &fmtCtx = param.fmtCtx;
+ auto &vcodecCtx = param.vcodecCtx;
+ auto &VideoStreamIndex = param.VideoStreamIndex;
+
+ while (1) {
+ AVPacket *packet = av_packet_alloc();
+ int ret = av_read_frame(fmtCtx, packet);
+ if (ret == 0 && packet->stream_index == param.VideoStreamIndex) {
+ ret = avcodec_send_packet(vcodecCtx, packet);
+ if (ret == 0) {
+ AVFrame *frame = av_frame_alloc();
+ ret = avcodec_receive_frame(vcodecCtx, frame);
+ if (ret == 0) {
+ av_packet_unref(packet);
+ return frame;
+ }
+ else if (ret == AVERROR(EAGAIN)) {
+ av_frame_unref(frame);
+ }
+ }
+ }
+
+ av_packet_unref(packet);
+ }
+ return nullptr;
+}
+
+void StretchBits(HWND hwnd, const std::vector &bits, int width, int height) {
+ auto hdc = GetDC(hwnd);
+ BITMAPINFO bitinfo = {};
+ auto &bmiHeader = bitinfo.bmiHeader;
+ bmiHeader.biSize = sizeof(bitinfo.bmiHeader);
+ bmiHeader.biWidth = width;
+ bmiHeader.biHeight = -height; // 注意负号,否则会画面颠倒
+ bmiHeader.biPlanes = 1;
+ bmiHeader.biBitCount = 24;
+ bmiHeader.biCompression = BI_RGB;
+
+ // StretchDIBits 函数就快了很多
+ StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, &bits[0], &bitinfo, DIB_RGB_COLORS, SRCCOPY);
+ ReleaseDC(hwnd, hdc);
+
+}
+
+// 主函数入口
+int WINAPI WinMain(
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPSTR lpCmdLine,
+ _In_ int nShowCmd
+) {
+
+ SetProcessDPIAware();
+
+ auto className = L"MyWindow";
+ WNDCLASSW wndClass = {};
+ wndClass.hInstance = NULL;
+ wndClass.lpszClassName = className;
+ // // 这一个为了自动播放较前面的做了修改
+ wndClass.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
+ switch (msg) {
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ };
+
+ std::string file_path = "C:\\Users\\Administrator\\Videos\\keypoint_result.mp4";
+
+ DecoderParam decoderParam;
+ InitDecoder(file_path.c_str(), decoderParam);
+ int width = decoderParam.width;
+ int height = decoderParam.height;
+ auto &fmtCtx = decoderParam.fmtCtx; // 不知道它这都习惯定义变量时用 & 引用
+ auto &vcodecCtx = decoderParam.vcodecCtx;
+
+
+ // 下面这个宏函数的定义(可点进去),要看是否启用了Unicode编译,不然类型不行,这个网址一个介绍:https://blog.csdn.net/huashuolin001/article/details/95620424
+ RegisterClass(&wndClass);
+ auto window = CreateWindow(className, L"Hello World 标题", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, NULL, NULL);
+
+ ShowWindow(window, SW_SHOW);
+
+
+ MSG msg;
+ //// GetMessage是收到消息才会执行,就要鼠标就要一直动,不然就会卡在那里,就要改,成下面的
+ //while (GetMessage(&msg, window, 0, 0) > 0) {
+ // AVFrame *frame = RequestFrame(decoderParam);
+ // std::vector pixels(width * height);
+ // for (int i = 0; i < pixels.size(); i++) {
+ // uint8_t r = frame->data[0][i];
+ // uint8_t g = r;
+ // uint8_t b = r;
+ // pixels[i] = { r, g, b };
+ // }
+ // av_frame_free(&frame);
+ // StretchBits(window, pixels, width, height);
+ // TranslateMessage(&msg);
+ // DispatchMessage(&msg);
+ //}
+
+ while (1) {
+ BOOL hasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ if (hasMsg) {
+ if (msg.message == WM_QUIT) break;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else {
+ AVFrame *frame = RequestFrame(decoderParam);
+ std::vector pixels(width * height);
+ for (int i = 0; i < pixels.size(); i++) {
+ uint8_t r = frame->data[0][i];
+ uint8_t g = r;
+ uint8_t b = r;
+ pixels[i] = { r, g, b };
+ }
+
+ av_frame_free(&frame);
+ StretchBits(window, pixels, width, height);
+ }
+ }
+ return 0;
+}
+```
+
+---
+
+### 第三阶段:不再是黑白,添加色彩
+
+- 同时修改优化,在debug下不那么卡(主要是把vector的分配拿到循环之外);
+- 本地视频播放完后的处理需要弄一下,不然内存泄露了,内存直接狂飙占满。
+
+```c++
+#include
+#include
+#include
+#include
+
+extern "C" {
+#include
+#pragma comment(lib, "avcodec.lib")
+
+#include
+#pragma comment(lib, "avformat.lib")
+
+#include
+#pragma comment(lib, "avutil.lib")
+
+// 彩色画面要的
+#include
+#pragma comment(lib, "swscale.lib")
+}
+
+
+/*
+ yuvj×××这个格式被丢弃了,然后转化为yuv格式,
+ 不然有一个警告 deprecated pixel format used, make sure you did set range correctly,
+ 这个问题在前面和win32写api时可用,但是不知道其它地方会不会报错,就改过了
+*/
+AVPixelFormat ConvertDeprecatedFormat(enum AVPixelFormat format)
+{
+ switch (format) {
+ case AV_PIX_FMT_YUVJ420P:
+ return AV_PIX_FMT_YUV420P;
+ break;
+ case AV_PIX_FMT_YUVJ422P:
+ return AV_PIX_FMT_YUV422P;
+ break;
+ case AV_PIX_FMT_YUVJ444P:
+ return AV_PIX_FMT_YUV444P;
+ break;
+ case AV_PIX_FMT_YUVJ440P:
+ return AV_PIX_FMT_YUV440P;
+ break;
+ default:
+ return format;
+ break;
+ }
+}
+
+
+struct Color_RGB {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+
+// 把获取第一帧那个函数拆分了一下
+struct DecoderParam {
+ AVFormatContext *fmtCtx;
+ AVCodecContext *vcodecCtx;
+ int width;
+ int height;
+ int VideoStreamIndex;
+};
+
+void InitDecoder(const char* filepath, DecoderParam ¶m) {
+ AVFormatContext *fmtCtx = nullptr;
+ avformat_open_input(&fmtCtx, filepath, NULL, NULL);
+ avformat_find_stream_info(fmtCtx, NULL);
+
+ AVCodecContext *vcodecCtx = nullptr;
+ for (int i = 0; i < fmtCtx->nb_streams; i++) {
+ const AVCodec *codec = avcodec_find_decoder(fmtCtx->streams[i]->codecpar->codec_id);
+ if (codec->type == AVMEDIA_TYPE_VIDEO) {
+ param.VideoStreamIndex = i;
+ vcodecCtx = avcodec_alloc_context3(codec);
+ avcodec_parameters_to_context(vcodecCtx, fmtCtx->streams[i]->codecpar);
+ avcodec_open2(vcodecCtx, codec, NULL);
+ }
+ }
+ param.fmtCtx = fmtCtx;
+ param.vcodecCtx = vcodecCtx;
+ param.width = vcodecCtx->width;
+ param.height = vcodecCtx->height;
+}
+
+AVFrame* RequestFrame(DecoderParam ¶m) {
+ auto &fmtCtx = param.fmtCtx;
+ auto &vcodecCtx = param.vcodecCtx;
+ auto &VideoStreamIndex = param.VideoStreamIndex;
+
+ while (1) {
+ AVPacket *packet = av_packet_alloc();
+ int ret = av_read_frame(fmtCtx, packet);
+ if (ret == 0 && packet->stream_index == param.VideoStreamIndex) {
+ ret = avcodec_send_packet(vcodecCtx, packet);
+ if (ret == 0) {
+ AVFrame *frame = av_frame_alloc();
+ ret = avcodec_receive_frame(vcodecCtx, frame);
+ if (ret == 0) {
+ av_packet_unref(packet);
+ return frame;
+ }
+ else if (ret == AVERROR(EAGAIN)) {
+ av_frame_unref(frame);
+ }
+ }
+ }
+
+ av_packet_unref(packet);
+ }
+ return nullptr;
+}
+
+
+void StretchBits(HWND hwnd, const std::vector &bits, int width, int height) {
+ auto hdc = GetDC(hwnd);
+ BITMAPINFO bitinfo = {};
+ auto &bmiHeader = bitinfo.bmiHeader;
+ bmiHeader.biSize = sizeof(bitinfo.bmiHeader);
+ bmiHeader.biWidth = width;
+ bmiHeader.biHeight = -height; // 注意负号,否则会画面颠倒
+ bmiHeader.biPlanes = 1;
+ bmiHeader.biBitCount = 24;
+ bmiHeader.biCompression = BI_RGB;
+
+ // StretchDIBits 函数就快了很多
+ StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, &bits[0], &bitinfo, DIB_RGB_COLORS, SRCCOPY);
+ ReleaseDC(hwnd, hdc);
+
+}
+
+// 写一个转换颜色编码的函数
+std::vector GetRGBPixels(AVFrame *frame, std::vector &buffer) {
+ static SwsContext *swsctx = nullptr;
+ swsctx = sws_getCachedContext(swsctx,
+ frame->width, frame->height, static_cast(frame->format),
+ frame->width, frame->height, AVPixelFormat::AV_PIX_FMT_BGR24, NULL, NULL, NULL, NULL
+ ); // 这里原来的类型转换是用的 (AVPixelFormat)frame->format
+
+ // 每次循环调用这个函数,都会重新分配这个vector,debug下就很慢
+ //std::vector buffer(frame->width * frame->height);
+
+ //uint8_t* data[] = {(uint8_t*)&buffer[0]};
+ uint8_t* data[] = {reinterpret_cast(&buffer[0])}; // c++类型的指针风格转换
+ int linesize[] = { frame->width * 3 };
+ // sws_scale 函数可以对画面进行缩放,同时还能改变颜色编码,
+ sws_scale(swsctx, frame->data, frame->linesize, 0, frame->height, data, linesize);
+ return buffer;
+}
+
+
+// 主函数入口
+int WINAPI WinMain(
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPSTR lpCmdLine,
+ _In_ int nShowCmd
+) {
+
+ SetProcessDPIAware();
+
+ auto className = L"MyWindow";
+ WNDCLASSW wndClass = {};
+ wndClass.hInstance = NULL;
+ wndClass.lpszClassName = className;
+ wndClass.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
+ switch (msg) {
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ };
+
+ // 视频地址要对,不然会报错
+ // std::string file_path = "C:\\Users\\Administrator\\Videos\\keypoint_result.mp4";
+ std::string file_path = "rtsp://192.168.108.11:554/user=admin&password=&channel=1&stream=1.sdp?";
+
+ DecoderParam decoderParam;
+ InitDecoder(file_path.c_str(), decoderParam);
+ int width = decoderParam.width;
+ int height = decoderParam.height;
+ auto &fmtCtx = decoderParam.fmtCtx; // 不知道它这都习惯定义变量时用 & 引用
+ auto &vcodecCtx = decoderParam.vcodecCtx;
+
+
+ // 下面这个宏函数的定义(可点进去),要看是否启用了Unicode编译,不然类型不行,这个网址一个介绍:https://blog.csdn.net/huashuolin001/article/details/95620424
+ RegisterClass(&wndClass);
+ auto window = CreateWindow(className, L"Hello World 标题", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, NULL, NULL);
+
+ ShowWindow(window, SW_SHOW);
+
+ MSG msg;
+
+ // 进入循环,debug下,很慢,原来是每次调用 GetRGBPixels 函数,里面就要分配一个很大的vector
+ // 所以在循环前创建好,直接传进去,避免每次循环都去重新分配
+ std::vector buffer(width * height);
+
+ while (1) {
+ BOOL hasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ if (hasMsg) {
+ if (msg.message == WM_QUIT) break;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else {
+ AVFrame *frame = RequestFrame(decoderParam);
+ // 原来的格式是AV_PIX_FMT_YUVJ420P,被丢弃,会有一个警告:deprecated pixel format used, make sure you did set range correctly
+ frame->format = ConvertDeprecatedFormat(static_cast(frame->format));
+
+ /*
+ // 这是原来的写法
+ std::vector pixels(width * height);
+ for (int i = 0; i < pixels.size(); i++) {
+ uint8_t r = frame->data[0][i];
+ uint8_t g = r;
+ uint8_t b = r;
+ pixels[i] = { r, g, b };
+ }*/
+ std::vector pixels = GetRGBPixels(frame, buffer); // 解码调用
+ av_frame_free(&frame);
+ StretchBits(window, pixels, width, height);
+ }
+ }
+
+ return 0;
+}
+```
+
+---
+
+### 第四阶段:本地视频播放过快
+
+避免播放过快的一种解题思路: (因为现在视频播放速度是由cpu的计算决定的,可能就会很快)
+
+- 直接Sleep暂停,这种计算、渲染还要时间,速度就慢了
+- `std::this_thread::sleep_until` 能够延迟到指定的时间点,利用这个特性,即使解码和渲染占用了时间,也不会影响整体延迟时间,除非你的解码渲染一帧的时间已经超过了每帧间隔时间。(这个需要头文件`thread`)
+- 但这些方法都不会是最终方案。
+
+```c++
+ auto currentTime = std::chrono::system_clock::now(); // 需要头文件
+ MSG msg;
+ while (1) {
+ BOOL hasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ if (hasMsg) {
+ if (msg.message == WM_QUIT) break;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else {
+ AVFrame *frame = RequestFrame(decoderParam);
+ std::vector pixels = GetRGBPixels(frame, buffer); // 解码调用
+ av_frame_free(&frame);
+
+ // 为了正确的播放速度,以上的速度都是取决cpu运算速度
+ /*
+ AVCodecContext::framerate 可以获取视频的帧率,代表每秒需要呈现多少帧,他是 AVRational 类型,类似于分数,num 是分子,den 是分母。这里我们把他倒过来,再乘以1000得出每帧需要等待的毫秒数。
+
+ double framerate = (double)vcodecCtx->framerate.den / vcodecCtx->framerate.num;
+ Sleep(framerate * 1000);
+ // 但是纯上面这样做,整个画面就慢了,因为计算渲染还要时间
+ */
+
+ double framerate = (double)vcodecCtx->framerate.den / vcodecCtx->framerate.num;
+ std::this_thread::sleep_until(currentTime + std::chrono::milliseconds(
+ (int)(framerate * 1000)));
+ currentTime = std::chrono::system_clock::now();
+
+ StretchBits(window, pixels, width, height);
+ }
+ }
+```
+
+### 第五阶段:cuda硬解码
+
+- 获取当前设备环境所有可用的硬件解码器:
+
+ ```c++
+ #include
+ #include // 需要这个头文件
+
+ std::vector get_vdec_support_hwdevices() {
+ std::vector hwdevs;
+ hwdevs.clear();
+ enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
+ while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
+ hwdevs.push_back(av_hwdevice_get_type_name(type));
+ }
+ return hwdevs;
+ }
+ ```
+
+ 家里的电脑暂时得到了这些结果:
+ cuda
+ dxva2
+ qsv
+ d3d11va
+ opencl
+ vulkan
+
+硬件解码,改了这三个函数:
+
+- linux上需要添加这个头文件`#include `,才能用下面硬件相关的一些函数。
+
+```c++
+void InitDecoder(const char* filepath, DecoderParam ¶m) {
+ AVCodecContext *vcodecCtx = nullptr;
+ AVFormatContext *fmtCtx = nullptr;
+
+ // 之前是这种解码方式
+ avformat_open_input(&fmtCtx, filepath, NULL, NULL);
+ avformat_find_stream_info(fmtCtx, NULL);
+
+ for (int i = 0; i < fmtCtx->nb_streams; i++) {
+ const AVCodec *codec = avcodec_find_decoder(fmtCtx->streams[i]->codecpar->codec_id);
+ if (codec->type == AVMEDIA_TYPE_VIDEO) {
+ param.VideoStreamIndex = i;
+ vcodecCtx = avcodec_alloc_context3(codec);
+ avcodec_parameters_to_context(vcodecCtx, fmtCtx->streams[i]->codecpar);
+ avcodec_open2(vcodecCtx, codec, NULL);
+ }
+ }
+
+
+ // 启用硬件解码器 (加的是这一段)
+ AVBufferRef *hw_device_ctx = nullptr;
+ // linux下这个函数要这个头文件,#include ,vs上不用
+ int ret = av_hwdevice_ctx_create(&hw_device_ctx, AVHWDeviceType::AV_HWDEVICE_TYPE_DXVA2, NULL, NULL, NULL); // linux下,最后一个参数NULL改成0好些
+ vcodecCtx->hw_device_ctx = hw_device_ctx;
+ // 我在linux上,上面的类型用的cuda这个AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA,ret得到的是0,成功了,但是上一行代码赋值时,始终错误,跟公司没有gpu的win上报的错好像类似;不知道是不是因为linux是ffmpeg3.4的版本,换4点几的版本可能就好了
+
+ param.fmtCtx = fmtCtx;
+ param.vcodecCtx = vcodecCtx;
+ param.width = vcodecCtx->width;
+ param.height = vcodecCtx->height;
+}
+
+std::vector GetRGBPixels(AVFrame *frame, std::vector &buffer) {
+
+ AVFrame *swFrame = av_frame_alloc();
+ av_hwframe_transfer_data(swFrame, frame, 0);
+ frame = swFrame; // 这是为了硬件解码加的几行
+
+ static SwsContext *swsctx = nullptr;
+ swsctx = sws_getCachedContext(swsctx,
+ frame->width, frame->height, static_cast(frame->format),
+ frame->width, frame->height, AVPixelFormat::AV_PIX_FMT_BGR24, NULL, NULL, NULL, NULL
+ );
+
+ uint8_t* data[] = {reinterpret_cast(&buffer[0])}; // c++类型的指针风格转换
+ int linesize[] = { frame->width * 3 };
+ // sws_scale 函数可以对画面进行缩放,同时还能改变颜色编码,
+ sws_scale(swsctx, frame->data, frame->linesize, 0, frame->height, data, linesize);
+ av_frame_free(&swFrame); // 这样也是
+ return buffer;
+}
+```
+
+- 除了上面这两个函数,StretchBits这个函数也大改了,用的Direct3D 9 渲染,而不是前面的 GDI渲染的古法。(至于相关更细节的原理看博客吧)
+
+下面这个代码是简单先跑得起来的,跟前面的代码也差不多(但不是再用的GetRGBPixels这个函数,而是重新实现的另外的StretchBits),用的是cuda(AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA),这个代码用AV_HWDEVICE_TYPE_DXVA2或是AV_HWDEVICE_TYPE_D3D11VA都是会报错的,所以博客第一篇教程最后一点进行不下去。
+
+```c++
+#include
+#include
+#include
+#include
+#include // 这和thread是为了播放速度正常要用到的
+#include
+
+#include // D3D9渲染画面,Direct3D 9 渲染。(有些头文件在这里面暂时是没有用到的)
+#pragma comment(lib, "d3d9.lib")
+
+#include
+using Microsoft::WRL::ComPtr;
+
+//using namespace std::chrono;
+
+extern "C" {
+#include
+#pragma comment(lib, "avcodec.lib")
+
+#include
+#pragma comment(lib, "avformat.lib")
+
+#include
+#pragma comment(lib, "avutil.lib")
+
+ // 彩色画面要的,以及一些变换都要这个头文件
+#include
+#pragma comment(lib, "swscale.lib")
+}
+
+
+struct Color_RGB {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+
+// 把获取第一帧那个函数拆分了一下
+struct DecoderParam {
+ AVFormatContext *fmtCtx;
+ AVCodecContext *vcodecCtx;
+ int width;
+ int height;
+ int VideoStreamIndex;
+};
+
+void InitDecoder(const char* filepath, DecoderParam ¶m) {
+ AVCodecContext *vcodecCtx = nullptr;
+ AVFormatContext *fmtCtx = nullptr;
+
+
+ avformat_open_input(&fmtCtx, filepath, NULL, NULL);
+ avformat_find_stream_info(fmtCtx, NULL);
+
+ for (int i = 0; i < fmtCtx->nb_streams; i++) {
+ const AVCodec *codec = avcodec_find_decoder(fmtCtx->streams[i]->codecpar->codec_id);
+ if (codec->type == AVMEDIA_TYPE_VIDEO) {
+ param.VideoStreamIndex = i;
+ vcodecCtx = avcodec_alloc_context3(codec);
+ avcodec_parameters_to_context(vcodecCtx, fmtCtx->streams[i]->codecpar);
+ avcodec_open2(vcodecCtx, codec, NULL);
+ }
+ }
+
+ // 启用硬件解码器
+ AVBufferRef *hw_device_ctx = nullptr;
+ av_hwdevice_ctx_create(&hw_device_ctx, AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA, NULL, NULL, NULL);
+ vcodecCtx->hw_device_ctx = hw_device_ctx; // 没有GPU,这会报错
+
+
+ param.fmtCtx = fmtCtx;
+ param.vcodecCtx = vcodecCtx;
+ param.width = vcodecCtx->width;
+ param.height = vcodecCtx->height;
+}
+
+AVFrame* RequestFrame(DecoderParam ¶m) {
+ auto &fmtCtx = param.fmtCtx;
+ auto &vcodecCtx = param.vcodecCtx;
+ auto &VideoStreamIndex = param.VideoStreamIndex;
+
+ while (1) {
+ AVPacket *packet = av_packet_alloc();
+ int ret = av_read_frame(fmtCtx, packet);
+ if (ret == 0 && packet->stream_index == param.VideoStreamIndex) {
+ ret = avcodec_send_packet(vcodecCtx, packet);
+ if (ret == 0) {
+ // 把内存的申请放循环外,这是有问题的,会内存泄露,去看opencv_c++中,做了对应的修改
+ AVFrame *frame = av_frame_alloc();
+ ret = avcodec_receive_frame(vcodecCtx, frame);
+ if (ret == 0) {
+ av_packet_unref(packet);
+ return frame;
+ }
+ else if (ret == AVERROR(EAGAIN)) {
+ av_frame_unref(frame);
+ }
+ }
+ }
+
+ av_packet_unref(packet);
+ }
+ return nullptr;
+}
+
+void StretchBits(IDirect3DDevice9 *device, const std::vector &bits, int width, int height) {
+ ComPtr surface;
+ device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, surface.GetAddressOf());
+
+ D3DLOCKED_RECT lockRect;
+ surface->LockRect(&lockRect, NULL, D3DLOCK_DISCARD);
+
+ memcpy(lockRect.pBits, &bits[0], bits.size());
+
+ surface->UnlockRect();
+ device->Present(NULL, NULL, NULL, NULL);
+}
+// 这基本丢弃了前面的方法,把StretchBits、GetRGBPixels这俩函数重新别的原理实现了
+void GetRGBPixels(AVFrame* frame, std::vector &buffer, AVPixelFormat pixelFormat, int byteCount) {
+ AVFrame* swFrame = av_frame_alloc();
+ av_hwframe_transfer_data(swFrame, frame, 0);
+ frame = swFrame;
+
+ static SwsContext* swsctx = nullptr;
+ swsctx = sws_getCachedContext(
+ swsctx,
+ frame->width, frame->height, (AVPixelFormat)frame->format,
+ frame->width, frame->height, pixelFormat, NULL, NULL, NULL, NULL);
+
+ uint8_t* data[] = { &buffer[0] };
+ int linesize[] = { frame->width * byteCount };
+ sws_scale(swsctx, frame->data, frame->linesize, 0, frame->height, data, linesize);
+
+ av_frame_free(&swFrame);
+}
+
+// 主函数入口
+int WINAPI WinMain(
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPSTR lpCmdLine,
+ _In_ int nShowCmd
+) {
+
+ SetProcessDPIAware();
+
+ auto className = L"MyWindow";
+ WNDCLASSW wndClass = {};
+ wndClass.hInstance = NULL;
+ wndClass.lpszClassName = className;
+ wndClass.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
+ switch (msg) {
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ }; // 这一个较前面的做了修改
+
+
+ std::string file_path = "C:\\Users\\Administrator\\Videos\\keypoint_result.mp4";
+ //std::string file_path = "rtsp://192.168.108.11:554/user=admin&password=&channel=1&stream=1.sdp?";
+
+ DecoderParam decoderParam;
+ InitDecoder(file_path.c_str(), decoderParam);
+ int width = decoderParam.width;
+ int height = decoderParam.height;
+ auto &fmtCtx = decoderParam.fmtCtx; // 不知道它这都习惯定义变量时用 & 引用
+ auto &vcodecCtx = decoderParam.vcodecCtx;
+
+
+ // D3D9 初始化设备
+ ComPtr d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
+ ComPtr d3d9Device;
+
+ D3DPRESENT_PARAMETERS d3dParams = {};
+ d3dParams.Windowed = TRUE;
+ d3dParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3dParams.BackBufferFormat = D3DFORMAT::D3DFMT_X8R8G8B8;
+ d3dParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
+ d3dParams.BackBufferWidth = width;
+ d3dParams.BackBufferHeight = height;
+
+
+ // 下面这个宏函数的定义(可点进去),要看是否启用了Unicode编译,不然类型不行,这个网址一个介绍:https://blog.csdn.net/huashuolin001/article/details/95620424
+ RegisterClass(&wndClass);
+ auto window = CreateWindow(className, L"Hello World 标题", WS_OVERLAPPEDWINDOW, 0, 0, width, height, NULL, NULL, hInstance, NULL);
+
+ d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dParams, d3d9Device.GetAddressOf());
+
+ ShowWindow(window, SW_SHOW);
+
+ std::vector buffer(width * height * 4);
+
+ auto currentTime = std::chrono::system_clock::now();
+ MSG msg;
+ while (1) {
+ BOOL hasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ if (hasMsg) {
+ if (msg.message == WM_QUIT) break;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else {
+ AVFrame *frame = RequestFrame(decoderParam);
+ GetRGBPixels(frame, buffer, AVPixelFormat::AV_PIX_FMT_BGRA, 4);
+ av_frame_free(&frame);
+
+ // 看要不要和这个延迟的代码
+ //double framerate = (double)vcodecCtx->framerate.den / vcodecCtx->framerate.num;
+ //std::this_thread::sleep_until(currentTime + std::chrono::milliseconds(
+ // (int)(framerate * 1000)));
+ //currentTime = std::chrono::system_clock::now();
+
+ StretchBits(d3d9Device.Get(), buffer, width, height);
+ }
+ }
+
+ return 0;
+}
+```
+
+#### 可以改成无边框:
+
+auto window = CreateWindow(className, L"Hello World 标题", WS_POPUP, 100, 100, width, height, NULL, NULL, hInstance, NULL);
+
+- 主要是这行,WS_OVERLAPPEDWINDOW 这个参数改成了 WS_POPUP;
+- 然后 100, 100 指窗口左上角的位置坐标。
+
+#### 硬解码的一个思路
+
+这是群里一个问题的求助,放这里,以后可能会有一个参考:
+
+Q:请问有人做过ffmpeg硬解码rtsp,能控制在200 ms以内不,我现在遇到的问题使用h264cuvid解码 延迟有500ms,用h264软解码200ms,都是用opencv显示的,不知道为啥硬解码显示的延迟比软解码还大。
+
+A:一些参考
+
+- 硬件的时间应该主要消耗在数据的输入输出吧,ffmpeg有omx的开源代码可以参考,我们目前用的gstreamer的开源代码,硬解码怎么也要比软解码快;
+- 不知道是不是拷贝到cpu 这步骤消耗的时间,看你怎么操作的,包括数据的传递方式,资源的调度方式。
+- 硬解码可能延迟要比软解码高。有数据拷贝消耗的。如果你的cpu能实时过来,延迟应该是比较低的。
+- 但是500ms就太夸张了,软解码200ms也是合理但不是最快的。应该是协议协商的时候做了缓冲。
+- 是的,但是可以配置。可以做到每帧都解码,但是会丢失抗扰动能力。
+- 硬解码也可以做丢帧等策略降低延迟
+
+
+
+
+
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608174855705.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608174855705.png"
new file mode 100644
index 0000000..0f470d3
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608174855705.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180047602.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180047602.png"
new file mode 100644
index 0000000..85408bc
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180047602.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180411780.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180411780.png"
new file mode 100644
index 0000000..1c41473
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180411780.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180753199.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180753199.png"
new file mode 100644
index 0000000..9bc64ca
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608180753199.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181111499.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181111499.png"
new file mode 100644
index 0000000..1f03ba1
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181111499.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181242631.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181242631.png"
new file mode 100644
index 0000000..6e00d85
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181242631.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181929479.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181929479.png"
new file mode 100644
index 0000000..b6c5ac5
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608181929479.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182043733.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182043733.png"
new file mode 100644
index 0000000..aaac77e
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182043733.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182234561.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182234561.png"
new file mode 100644
index 0000000..f4aecff
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182234561.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182410708.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182410708.png"
new file mode 100644
index 0000000..d5cf9f0
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182410708.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182600684.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182600684.png"
new file mode 100644
index 0000000..7a679a5
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182600684.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182718595.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182718595.png"
new file mode 100644
index 0000000..e909bc0
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608182718595.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183452842.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183452842.png"
new file mode 100644
index 0000000..ba3345c
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183452842.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183612619.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183612619.png"
new file mode 100644
index 0000000..1ca2093
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183612619.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183746594.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183746594.png"
new file mode 100644
index 0000000..6d158b3
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608183746594.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184006094.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184006094.png"
new file mode 100644
index 0000000..a4d23f3
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184006094.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184050852.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184050852.png"
new file mode 100644
index 0000000..2bd4508
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184050852.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184622914.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184622914.png"
new file mode 100644
index 0000000..d7d2e56
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184622914.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184710530.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184710530.png"
new file mode 100644
index 0000000..5b541b8
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230608184710530.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609092441211.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609092441211.png"
new file mode 100644
index 0000000..0d867fe
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609092441211.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609092531619.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609092531619.png"
new file mode 100644
index 0000000..969d4ca
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609092531619.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093000865.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093000865.png"
new file mode 100644
index 0000000..6df35af
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093000865.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093356034.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093356034.png"
new file mode 100644
index 0000000..ade67b6
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093356034.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093729314.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093729314.png"
new file mode 100644
index 0000000..2d556dd
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093729314.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093945517.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093945517.png"
new file mode 100644
index 0000000..9d45d5d
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609093945517.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609094021529.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609094021529.png"
new file mode 100644
index 0000000..18bcba1
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609094021529.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609094529836.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609094529836.png"
new file mode 100644
index 0000000..aaa9d63
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609094529836.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095253369.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095253369.png"
new file mode 100644
index 0000000..225b207
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095253369.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095604720.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095604720.png"
new file mode 100644
index 0000000..b29072f
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095604720.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095717350.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095717350.png"
new file mode 100644
index 0000000..3afe911
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095717350.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095828430.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095828430.png"
new file mode 100644
index 0000000..2aba1b8
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095828430.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095959428.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095959428.png"
new file mode 100644
index 0000000..fff0be1
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609095959428.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100240193.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100240193.png"
new file mode 100644
index 0000000..183374d
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100240193.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100445526.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100445526.png"
new file mode 100644
index 0000000..3d7db39
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100445526.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100844121.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100844121.png"
new file mode 100644
index 0000000..9a01e96
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609100844121.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102432826.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102432826.png"
new file mode 100644
index 0000000..91afa59
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102432826.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102557347.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102557347.png"
new file mode 100644
index 0000000..3cf2ec8
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102557347.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102650929.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102650929.png"
new file mode 100644
index 0000000..95d20d3
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102650929.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102932820.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102932820.png"
new file mode 100644
index 0000000..5412998
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609102932820.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609103150012.png" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609103150012.png"
new file mode 100644
index 0000000..2596865
Binary files /dev/null and "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/illustration/image-20230609103150012.png" differ
diff --git "a/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/\345\267\245\344\270\232\347\233\270\346\234\272\343\200\201\351\225\234\345\244\264\345\217\202\346\225\260\344\273\213\347\273\215.md" "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/\345\267\245\344\270\232\347\233\270\346\234\272\343\200\201\351\225\234\345\244\264\345\217\202\346\225\260\344\273\213\347\273\215.md"
new file mode 100644
index 0000000..5076a4a
--- /dev/null
+++ "b/tutorial/\345\267\245\344\270\232\347\233\270\346\234\272/\345\267\245\344\270\232\347\233\270\346\234\272\343\200\201\351\225\234\345\244\264\345\217\202\346\225\260\344\273\213\347\273\215.md"
@@ -0,0 +1,281 @@
+![image-20230608184050852](illustration/image-20230608184050852.png)
+
+选型相机时,要注意看这家的相机是否支持==多播==、==组播==,即多个程序去读取一个相机。
+
+ 看到海康的一个GiGE相机,支持组播:“组播功能可以实现多个 PC 对同一个相机同时进行访问。在同一时刻,同一个相机只能被一个客户端以控制和接收模式或控制模式连接,但可被多个客户端以接收模式进行连接。客户端内每个相机的组播模式都是单独控制的。三种组播模式下,可对相机进
+行的操作请见表”。
+
+| 组播模式 | 功能介绍 |
+| -------------- | ------------------------------------------------------------ |
+| 控制和接收模式 | 可以读取及修改相机的参数,同时还可以获取相机的图像数据 |
+| 控制模式 | 可以读取及修改相机的参数,但不可以获取相机的图像数据 |
+| 接收模式 | 可以读取相机的参数,并获取相机的图像数据,但不能修改相机的参数 |
+
+- 有很多相关知识的[网址](https://www.optmv.com/train/machine.html),可去学习。
+
+## 一、工业镜头
+
+### 1.1 镜头焦距
+
+镜头焦距:f = (相机感光靶面×物距u) / 物体尺寸 (这是近似的,因为像距原小于物距)
+![image-20230608174855705](illustration/image-20230608174855705.png)
+
+---
+
+对焦:物体一个点反射的光线,有很多不同的方向,在不同的物距下,光线在像端汇聚的点位不同,距离不合适时,成像为弥散圆,那这样的点多了,成像就是模糊的,就需要调整镜片组与传感器平面之间的距离,使被检测物体在CCD/CMOS上成的像清晰。
+
+![image-20230608180047602](illustration/image-20230608180047602.png)
+
+所以简单说就是拧镜头使其变远变近就是在对焦。
+
+---
+
+对焦:一个工业镜头,一般是由几个镜片组成的,只是把它们等效成一个镜片,那这些不同镜片间的组合就能==改变这个等效镜片的焦距==,而非固定不变。
+
+![image-20230608180411780](illustration/image-20230608180411780.png)
+
+所以在变焦(一般的摄像头可能不支持变焦)后,就要去对焦,调整靶面位置。
+
+### 1.2 光圈
+
+光圈位于镜头内部,通过收缩与扩大控制入射光强度,也可以过滤入射光线角度。
+
+
+
+
+
+常规镜头:光圈是位于镜头的中间位 置
+
+远心镜头:光圈是位于它的镜头的焦点位置的
+
+![image-20230608180753199](illustration/image-20230608180753199.png)
+
+---
+
+光圈系数F:就是代表通光量,F越大,D就越小,那通过的光反而越少。
+
+![image-20230608181111499](illustration/image-20230608181111499.png)
+
+
+
+
+
+### 1.3 景深
+
+大、小景深:
+
+![image-20230608181242631](illustration/image-20230608181242631.png)
+
+原理不写了,记住结论:
+
+- 光圈越小,景深越大
+- 焦距越小,景深越大
+- 拍摄距离越大,景深越大
+
+### 1.4 远心镜头
+
+主要作用:消除透视(近大远小),因为远心镜头在焦点放置光圈,限制非平行入射光线成像,达到消除透视的效果
+
+效果:![image-20230608181929479](illustration/image-20230608181929479.png)
+
+
+
+原理:
+
+![image-20230608182043733](illustration/image-20230608182043733.png)
+
+
+
+种类:
+
+![image-20230608182234561](illustration/image-20230608182234561.png)
+
+
+
+应用场景:
+![image-20230608182410708](illustration/image-20230608182410708.png)
+
+
+
+### 1.5 评价镜头的好坏
+
+镜头分辨率:
+
+![image-20230608182600684](illustration/image-20230608182600684.png)
+
+---
+
+镜头的反差:反差越好,它亮的也能拍,暗的也能拍
+
+![image-20230608182718595](illustration/image-20230608182718595.png)
+
+---
+
+
+
+镜头的MTF曲线
+
+它是基于分辨率和反差计算的量化参数,越接近于1越好。代表镜头的成像质量,一般认为0.6为几个,0.8为良好
+
+
+
+### 1.6 镜头的接口
+
+C接口比较深,CS接口比较浅
+
+![image-20230608183452842](illustration/image-20230608183452842.png)
+
+下面是能直接卡进去,不用拧的
+
+![image-20230608183612619](illustration/image-20230608183612619.png)
+
+
+
+![image-20230608183746594](illustration/image-20230608183746594.png)
+
+### 1.7 镜头的几何像差
+
+![image-20230608184006094](illustration/image-20230608184006094.png)
+
+
+
+![image-20230608184050852](illustration/image-20230608184050852.png)
+
+### 1.8 选型要点
+
+1. 镜头靶面尺寸需要大于等于相机CCD靶面尺寸,否则成像有黑边。
+2. 镜头的接口和相机接口匹配,C镜头可通过转接口安装到CS相机,CS镜头无法安转到C接口相机。
+3. 最重要:计算正确的焦距。
+
+
+
+实际例子:
+
+- ![image-20230608184622914](illustration/image-20230608184622914.png)
+
+- ![image-20230608184710530](illustration/image-20230608184710530.png)
+
+## 二、工业相机
+
+### 1、像元尺寸
+
+![image-20230609092441211](illustration/image-20230609092441211.png)
+
+### 2、分辨率
+
+![image-20230609092531619](illustration/image-20230609092531619.png)
+
+### 3、彩色相机和黑白相机的区别
+
+![image-20230609093000865](illustration/image-20230609093000865.png)
+
+有真彩色相机、伪彩色相机。区别原理看这[视频](https://www.bilibili.com/video/BV18s4y1c7WA?t=37.0&p=13)吧。
+
+![image-20230609093356034](illustration/image-20230609093356034.png)
+
+所以彩色相机,可能有的时候图像的边缘会出现伪彩色的情况,但一般还是选用彩色相机,除非要处理的跟图像颜色无关,可选用黑白相机。
+
+### 4、CCD和CMOS的区别
+
+![image-20230609093729314](illustration/image-20230609093729314.png)
+
+
+
+![image-20230609093945517](illustration/image-20230609093945517.png)
+
+
+
+![image-20230609094021529](illustration/image-20230609094021529.png)
+
+
+
+CCD的成像质量比CMOS要好,但CMOS集成度高,且帧率也会比较高,制造成本也比较低,所以现在CMOS相机会比CCD多一点。视频建议是在对成像质量非常高,且对帧率要求不高的情况下才选择CCD
+
+### 5、工业相机的快门
+
+![image-20230609094529836](illustration/image-20230609094529836.png)
+
+所以:卷帘快门,可以在比较暗的行曝光时间长一些,让暗的更亮;在比较亮的行曝光时间短一点,这样就可以拍摄出一张对比度比较均衡的图片。
+
+ 全局快门,它拍摄出来的光线亮度就会比较均匀
+
+
+
+全局快门的一个缺点:
+![image-20230609095253369](illustration/image-20230609095253369.png)
+
+
+
+卷帘快门的一个缺点:(它优点是帧率高)
+
+![image-20230609095604720](illustration/image-20230609095604720.png)
+
+---
+
+选择:
+
+![image-20230609095717350](illustration/image-20230609095717350.png)
+
+
+
+### 6、一些参数
+
+#### 6.1 曝光
+
+![image-20230609095828430](illustration/image-20230609095828430.png)
+
+增益:就是调节这个感光芯片的感光度
+
+![image-20230609095959428](illustration/image-20230609095959428.png)
+
+#### 6.2帧率
+
+曝光时间越长,帧率就会越低。
+
+![image-20230609100240193](illustration/image-20230609100240193.png)
+
+#### 6.3 白平衡
+
+![image-20230609100445526](illustration/image-20230609100445526.png)
+
+#### 6.4 靶面尺寸
+
+指的是感光原件的对角线长度。
+
+![image-20230609100844121](illustration/image-20230609100844121.png)
+
+#### 6.5 像素位深
+
+![image-20230609102432826](illustration/image-20230609102432826.png)
+
+#### 6.6 相机输出接口
+
+![image-20230609102557347](illustration/image-20230609102557347.png)
+
+还有CameraLink相机和CoaXPress相机的区别:
+
+CameraLink相机和CoaXPress相机的区别有一下几方面:
+
+1、帧率。两者最大的区别在于帧率,就是每分钟所拍照的次数。很明显CoaXPress相机的传输速度明显要高很多,大概3倍左右。
+
+2、软件控制。CameraLink相机有专门的相机控制软件,而CoaXPress相机没有,只有靠图像采集卡来控制。
+
+3、附件部分。CameraLink相机所用线缆长度不能太长,一般不能超过5米,不然传输信号会有损失,而CoaXPress相机所用线缆可以做到上百米而信号稳定。
+
+#### 6.7 分辨率要如何确定
+
+
+
+![image-20230609102650929](illustration/image-20230609102650929.png)
+
+### 7、海康工业相机的型号怎么看
+
+![image-20230609102932820](illustration/image-20230609102932820.png)
+
+
+
+### 8、工业相机样子示例。
+
+[视频地址](https://www.bilibili.com/video/BV13u4y1Z7eX?t=581.7&p=23)。
+
+![image-20230609103150012](illustration/image-20230609103150012.png)
\ No newline at end of file
diff --git "a/\345\205\266\345\256\203/PIL.md" "b/\345\205\266\345\256\203/PIL.md"
new file mode 100644
index 0000000..4cfd3e4
--- /dev/null
+++ "b/\345\205\266\345\256\203/PIL.md"
@@ -0,0 +1,295 @@
+# 图片模式转换
+==img.convert('RGBA')==:
+
+| modes | Description |
+| ----- | -------------------------------------- |
+| 1 | 1位像素,黑白图像,存成8位像素 |
+| L | 8位像素,黑白 |
+| P | 9位像素,使用调色板映射到任何其他模式 |
+| RGB | 3\*8位像素,真彩 |
+| RGBA | 4\*8位像素,真彩+透明通道 |
+| HSV | H (色彩/色度), S (饱和度), V (亮度) |
+| CMYK | 4\*8位像素,印刷四色模式或彩色印刷模式 |
+| YCbCr | 3\*8位像素,色彩视频格式 |
+| I | 32位整型像素 |
+| F | 33位浮点型像素 |
+
+## 一、Image
+
+```PYTHON
+from PIL import Image
+from io import BytesIO
+# 读取一张图片
+im = Image.open('2.jpg') # 根据文件名读取图像
+b = BytesIO()
+im.save(b,'JPEG') # 保存图片
+data = b.getvalue() # 获取图片的二进制信息
+print(data)
+
+# 图片格式
+print('格式:', im.format)
+# 图片模式
+print('模式:', im.mode) # L:灰度 RGB:真彩色 CMYK:出版图像
+# 图像宽高
+print('宽高:', im.size)
+print('完整信息:', im.info)
+try:
+ im.verify() # 检查图像文件的完整性
+except:
+ pass
+finally:
+ im.show() # 显示一张图片
+ im.save('4.jpg', 'JPEG') # 保存图片
+```
+
+```PYTHON
+from PIL import Image
+from io import BytesIO
+import requests
+
+# 创建新图片:
+im1 = Image.new('RGBA', (400, 400), 'red')
+# 从二进制中读取图片
+res = requests.get('http://pic.sc.chinaz.com/files/pic/pic9/202003/zzpic24077.jpg').content
+im2 = Image.frombytes('RGB', (100, 100), data=res) # 只能解析纯二进制图片
+# 从网页中读取图片
+im2 = Image.open(BytesIO(res))
+b = BytesIO()
+im2.save(b, format="PNG")
+im2.show()
+
+# 两张图片相加:
+im1 = Image.new('RGBA', (400, 400), 'red')
+im2 = Image.new('RGBA', (400, 400), 'blue')
+Image.blend(im1, im2, 0.5) # 两个图片大小一定要一样(效果见图4)
+
+# 点操作:
+im1.point(lambda x: x * 1.2)
+
+# 图片裁剪:
+box = (100, 100, 200, 200)
+region = im.crop(box) # 设置要裁剪的区域
+
+# 图片粘贴(合并)
+im1.paste(region, box) # 粘贴box大小的region到原先的图片对象中。(见图5)
+# 可以不给第二个参数,就是把图region的按左上角贴到图im1的左上角
+# im1.paste(region, (100, 100)) # 指定左上角位置为(100, 100),不给就默认是(0, 0)
+
+im1 = Image.new('RGB', (400, 400), 'red')
+im2 = Image.new('RGBA', (400, 400), 'blue')
+
+# 通道分离:
+r, g, b = im1.split() # 分割成三个通道,此时r,g,b分别为三个图像对象。
+print(r, g, b)
+# 通道合并:
+im3 = Image.merge("RGB", (b, g, r)) # 将b,r两个通道进行翻转。
+
+# 改变图片的大小:
+im4 = im1.resize((300, 200))
+im4.show()
+
+# 图片翻转
+im5 = im1.transpose(Image.FLIP_LEFT_RIGHT) # 左右翻转
+im6 = im2.transpose(Image.FLIP_TOP_BOTTOM) # 上下翻转
+im5.show()
+im6.show()
+
+# 获取某个像素位置的值
+im1.getpixel((43, 23))
+
+# 写某个像素位置的值
+im1.putpixel((10, 20), (255, 32, 43))
+
+# 创建图像缩略图
+im1.thumbnail((10, 20)) # 图片大小为(10,20)
+im1.show()
+
+# 旋转图像
+im8 = im1.rotate(45) # 逆时针旋转45度
+im8.show()
+注:RGB有三个通道,RGBA有四个通道,以上图片显示效果都在图6中
+```
+
+## 二、ImageDraw
+
+这个模块主要就是画图和打水印时用的,具体方法请看下面:
+
+```python
+from PIL import Image, ImageDraw
+
+# 画点
+im = Image.open('2.jpg')
+draw = ImageDraw.Draw(im)
+draw.point([100, 200], fill='blue') # 指定点的坐标和颜色
+
+# 创建一个正方形。[x1,x2,y1,y2]或者[(x1,x2),(y1,y2)] fill代表的为颜色
+draw.line([100, 100, 100, 600], fill='pink') # 直线
+draw.line([100, 100, 600, 100], fill='green')
+draw.line([600, 100, 600, 600], 'black')
+draw.line([100, 600, 600, 600], 'blue')
+
+# 弧形 [x1,x2,y1,y2] 弧度 颜色
+draw.arc([100, 100, 600, 600], 0, 360, fill='black')
+draw.arc([200, 100, 500, 600], 0, 360, fill='yellow')
+
+# 画圆 [x1,x2,y1,y2] outline边框颜色 fill填充颜色
+draw.ellipse([100, 100, 600, 600], outline='black', fill='white')
+
+# 画半圆 [x1,x2,y1,y2] 弧度 outline弦线颜色 fill填充颜色
+draw.chord([100, 100, 600, 600], 0, 360, outline=125)
+draw.chord([100, 100, 600, 600], 0, 90, outline=158)
+draw.chord([100, 100, 600, 600], 90, 180, outline=99, fill='gray')
+
+# 扇形 [x1,x2,y1,y2] 弧度 outline弦线颜色 fill填充颜色
+draw.pieslice([100, 100, 600, 600], 180, 210, outline=255)
+draw.pieslice([100, 100, 600, 600], 30, 80, fill=255)
+
+# 多边形
+draw.polygon([10, 23, 45, 6, 77, 87], outline='orange')
+draw.polygon([10, 20, 30, 40, 50, 90, 70, 80, 90, 100], fill='red')
+
+# 矩形(坐标给的是(x1, y1, x2, y2),即矩形的左上角、右下角)
+draw.rectangle((200, 200, 500, 500), outline="white")
+draw.rectangle((250, 300, 450, 400), fill=128)
+
+# 文字
+text = 'hello world '
+# 颜色
+draw.ink = 0 + 0 * 256 + 255 * 256 * 256
+# 加载到图片上
+draw.text([200, 100], text)
+im.show()
+```
+
+## 三、ImageEnhance
+
+主要是设置图片的颜色对比度亮度锐度啥的,增强图像。
+
+```python
+from PIL import ImageEnhance
+im1 = Image.open('4.jpg')
+
+# 调整图像的颜色平衡
+cl = ImageEnhance.Color(im1)
+ce = cl.enhance(1.2) # 对选择的属性数值增强1.3倍
+ce.show()
+
+# 调整图像的对比度
+ct = ImageEnhance.Contrast(im1)
+ch = ct.enhance(3.4)
+ch.show()
+
+# 调整图像的亮度
+br = ImageEnhance.Brightness(im1)
+be = br.enhance(2.2)
+be.show()
+
+# 调整图像的锐度
+sp = ImageEnhance.Sharpness(im1)
+se = sp.enhance(200)
+se.show()
+```
+
+## 四、ImageFont
+
+字体模块,主要是读取系统内字体以及给图片添加水印效果
+
+```python
+from PIL import Image
+from PIL import ImageDraw
+from PIL import ImageFont
+# simhei.ttf
+font = ImageFont.truetype(r'C:\Windows\Fonts\simsun.ttc', size=40)
+im = Image.open('4.jpg')
+dw = ImageDraw.Draw(im)
+# file还可以用颜色的方式 filee=(255, 0, 0) # 这也是红色,RGB的格式
+dw.text((50, 60), '你好,world', fill='red', font=font)
+# 还能获取输入文本的大小;
+text_size = dw.textsize("你好adadada", font=font) # (w, h),字体的高在font设置时就确定了,这里是40
+im.show()
+```
+
+ 注意:第4行中的字体一定是在存在的,代码中若使用到了的字体,尽量使用相对路径,然后把字体就放在相对路径。windows下的字体都是在C:\Windows\Fonts
+
+## 五、ImageGrab
+
+```python
+from PIL import ImageGrab
+im1 = ImageGrab.grab((0,0,800,200)) # 截取屏幕指定区域的图像
+im2 = ImageGrab.grab() # 不带参数表示全屏幕截图
+im1.show()
+im2.show()
+```
+
+## 六、ImageFilter
+
+过滤图像的效果。
+
+```python
+from PIL import Image, ImageFilter
+im = Image.open('4.jpg')
+
+# 高斯模糊
+im1 = im.filter(ImageFilter.GaussianBlur)
+im1.show()
+
+# 普通模糊
+im2 = im.filter(ImageFilter.BLUR)
+im2.show()
+
+# 边缘增强
+im3 = im.filter(ImageFilter.EDGE_ENHANCE)
+im3.show()
+
+# 找到边缘
+im4 = im.filter(ImageFilter.FIND_EDGES)
+im4.show()
+
+# 浮雕
+im5 = im.filter(ImageFilter.EMBOSS)
+im5.show()
+
+# 轮廓
+im6 = im.filter(ImageFilter.CONTOUR)
+im6.show()
+
+# 锐化
+im7 = im.filter(ImageFilter.SHARPEN)
+im7.show()
+
+# 平滑
+im8 = im.filter(ImageFilter.SMOOTH)
+im8.show()
+
+# 阙值平滑
+im9 = im.filter(ImageFilter.SMOOTH_MORE)
+im9.show()
+
+# 细节
+im10 = im.filter(ImageFilter.DETAIL)
+im10.show()
+```
+
+pillow 还算是比较强大的一个 模块,他可以轻松实现截屏 水印效果,并且还可以制作字符画,下面请看:
+
+```python
+from PIL import Image
+from PIL import ImageDraw
+from PIL import ImageFont
+
+txt = 'meimei'
+font = ImageFont.truetype(r'C:\Windows\Fonts\simsun.ttc', 9) # 9为字体大小
+im_path = '1.jpg' # 原图路径
+im = Image.open(im_path)
+width, height = im.size
+newImg = Image.new("RGBA", (width, height), (10, 10, 10)) # 背景色rgb,偏黑显示好一些
+x = 0
+for i in range(0, height, 9): # 需要与字体大小一致
+ for j in range(0, width, 9): # 需要与字体大小一致
+ a, b, c = im.getpixel((j, i)) # 获取像素
+ draw = ImageDraw.Draw(newImg)
+ draw.text((j, i), (txt[x % (len(txt)):x % (len(txt)) + 3]), fill=(a, b, c), font=font)
+ x += 3
+ del draw
+newImg.save('00.png', 'PNG')
+```
diff --git "a/\345\205\266\345\256\203/README.md" "b/\345\205\266\345\256\203/README.md"
new file mode 100644
index 0000000..6c101e1
--- /dev/null
+++ "b/\345\205\266\345\256\203/README.md"
@@ -0,0 +1,6 @@
+- [ffmpeg.md](./ffmpeg.md);
+- [matplotlib.md](./matplotlib.md);
+- [numpy.md](./numpy.md);
+- [PIL.md](./PIL.md);
+- [sklearn.md](./sklearn.md);
+- [torch.md](./torch.md);
\ No newline at end of file
diff --git "a/\345\205\266\345\256\203/ffmpeg.md" "b/\345\205\266\345\256\203/ffmpeg.md"
new file mode 100644
index 0000000..056e4d7
--- /dev/null
+++ "b/\345\205\266\345\256\203/ffmpeg.md"
@@ -0,0 +1,190 @@
+ffmpeg一个比较[入门的教程](https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/README-cn.md),很适合来学习。
+
+- [python-ffmpeg](https://github.com/kkroening/ffmpeg-python):用python来调用ffmpeg。
+
+这些参数,很多如果不给,就是采用原视频里面的。
+
+[这里](https://www.zhihu.com/question/47280477/answer/2301684673)还不错的一些相关的ffmpeg的参数。# 一定先去看,有你需要的
+
+windows:
+
+- ffmpeg列出当前设备的设备:
+ ffmpeg -list_devices true -f dshow -i dummy
+- ffplay调用本地摄像头:
+ ffplay -f dshow -i video="Logi C270 HD WebCam"
+ 或者:ffplay -f vfwcap -i 0
+- ffplay调用本地麦克风:(后面的名字是从ffmpeg里列出来的)
+ ffplay -f dshow -i audio="麦克风 (USB Audio Device)"
+
+linux:
+ 注意:dshow是win上特有的,linux上是不行的,linux上要用“x11grab”(这一般是用来录屏),可用`ffmpeg -devices`去查看支持的格式,具体输入输出可是可看[这里](https://zhuanlan.zhihu.com/p/629883381)。
+
+- 调用本地usb摄像头:ffplay -f v4l2 -i /dev/video0
+ 录像:ffmpeg -f v4l2 -framerate 25 -video_size 640x480 -i /dev/video0 output.mkv
+
+
+
+开发[官方学习文档](http://ffmpeg.org/doxygen/trunk/examples.html)。推拉流,代码上的一些实现,可以看看这个[博客](https://www.cnblogs.com/gongluck/category/1215138.html)。
+
+注意:
+
+- 当保存的文件已存在时,会询问是否覆盖,如果是在程序中作为外部命令代用则会卡在那里,故可以在每次命令最后面加参数来决定是否覆盖:加`-y`则表示如果输出文件已存在就同意覆盖;加`-n`则不同意覆盖,相当于什么也没做。这样子外部调用命令就会执行完,不会卡在那里。
+
+## ffplay快捷键
+
+| 作用 | 按键 |
+| ------------------------------------------------------ | ----------------- |
+| 退出 | q, ESC |
+| 暂停 | p, 空格 |
+| 全屏 | f |
+| 逐帧显示 | s |
+| 跳转到指定位置
(根据鼠标位置相对屏幕的宽度计算) | 鼠标右键点击屏幕 |
+| 向后10s/向前10s | 左方向键/右方向键 |
+| 向后1min/向前1min | 上方向键/下方向键 |
+| 向后10min/向前10min | page down/page up |
+| 显示音频波形 | w |
+
+对应的PotPlayer的一些常用快捷键:
+
+| | |
+| ------------------------------------------------ | ---------- |
+| 增加播放速度 | C |
+| 减慢播放毒素 | X |
+| 回到一倍速 | Z |
+| 复制当前画面到剪切板 | Ctrl+Alt+C |
+| 旋转画面
(注意下次打开会记住这次旋转的情况) | Alt+K |
+
+
+
+## 一、视频拆成图
+
+视频拆成图片:`ffmpeg -i input.flv -r 1 -f image2 image-%4d.jpeg` # 还可以在 -f image2 加上 -q:v 2 图片的质量会增加
+
+-i : 指定输入文件
+
+-r : 帧数 1 (好像不是帧数,是秒数)
+
+-f : 指定格式化的格式为image2
+
+生成的结果 image-%4d.jpeg %4d是指4位数字
+
+
+
+获取封面:`ffmpeg -i a.mp4 -y -f image2 -frames 1 a.jpg`
+获取更高质量:`ffmpeg -i a.mp4 -y -f image2 -q:v 2 -frames 1 a.jpg`
+
+---
+
+将多个图转成视频:`ffmpeg -f image2 -r 20 -i "./images/img_%d.jpg" ./out.mp4`
+
+- -r 20:代表帧数设置为20,也可以写做 -framerate 20 是一个意思
+- 还可以加一个参数:-vcodec libx264 # 代表以264格式编码
+- 特别注意,路径里面有空格、几级目录这些,用引号括起来
+- 还可在在合成过程中添加音频,参看[这里](https://www.cnblogs.com/lavezhang/p/15359148.html)。
+- 注意:opencv把图片存为视频,真有点奇怪啊,它最后保存的是视频总帧数始终只有图片数量的一半,无论保存图片时fps设置为多少,所以时长也只有ffmpeg转的一半。
+
+## 二、视频转gif(片段截取)
+
+视频转gif:`ffmpeg -ss 8 -t 15 -i 11.mp4 -s 600*400 -r 15 res.gif`
+
+- -ss 8 -t 15:从第8秒开始,往后截取15秒钟
+- -s:设定分辨率 # 可以不要就是原始大小
+- -r:设定帧数
+
+### 片段截取:
+
+- 同理视频片段截取(`-t`和`-to`):
+ - `ffmpeg -ss 6 -t 30 -i ./LOL.mp4 temp.mp4` # 从6秒开始截取30s,存为temp.mp4
+ - `ffmpeg -ss 6 -to 30 -i ./LOL.mp4 temp.mp4` # 这里就是从6秒开始,截取到30s,共24S
+
+注意:这样子会自适应降低视频码率,会较大的压缩视频大小(注重质量还是像下面一样带个 -c copy)
+
+- 时间还可以这么给(从第6秒到第10分25秒):
+
+`ffmpeg -ss 00:00:06 -to 00:10:25 -i ./sample.mp4 -c copy output.mp4` // -c copy 代表会个各种格式都按照原视频来
+
+## 三、改变码率。(压缩视频)
+
+视频的原码率是 2.1Mb/s ,压缩为 1.5Mb/s
+
+>ffmpeg -i Desktop/1.mov -b:v 1.5M -r 30 Desktop/1.mp4 # 还可以添加r参数,把原来的帧率改成30(一般是从大减小)
+>
+>ffmpeg -i .\out.avi -b:v 3.6M 123_1.mp4 # 还可以以此来转换视频格式(opencv只能avi格式写入,且码率很大很大,可以这样把它转换成mp4,减小码率,减小所占空间)
+
+- `-b:v` :指定视频的码率
+
+- `-b:a` :指定音频的码率
+
+- 1.5M:码率的值 1.5M 表示 1.5Mb/s
+
+ 码率越小,视频清晰度就降低,然后大小也会变小,然后就达到了压缩的目的
+
+avi的码率会很大,用这个做动态壁纸会比较吃资源,然后可以直接.avi转成.mp4,它会自己找一个合适和码率去转,不用降低帧率,占用差不多。
+
+## 四、修改编码格式(H.265),画面大小
+
+> 比如在转格式时,avi转mp4,其它什么都不指定,选择默认:
+>
+>ffmpeg -i out.avi 123_264.mp4 # 一般码率会比avi减小(但不会特别多),占用空间减小
+>
+>ffmpeg -i out.avi -c:v libx365 123_465.mp4 # 码率会比avi小很多很多(比上面那个还小),占用的空间也会小很多倍
+
+- `-c:v libx265` 代表以265的格式编码, -c:v是指定编码器,编码器列表可以使用ffmpeg -codecs查看
+
+还可以直接修改画面的大小:
+ffmpeg -i input.mp4 -c:v libx264 -vf scale=1280:-1 -y out123.mp4
+
+- -vf scale:指定输出视频的宽高,高-1代表按照比例自动适应,也可以直接 =640:480这样指定一个特定的。
+
+## 五、声画分离
+
+- 提取音频:`ffmpeg -i input.mp4 -vn -c:a copy output.aac` # 注意是.aac
+ - -vn:表示no video;
+ - -c:a copy:就是codec of audio,copy是直接拷贝视频中的原始的音频,这里不会涉及音频的编解码,速度会很快;也可以指定`-c:a mp3` 导出mp3格式的音频.
+ - 对于音频提取,可以使用`-b:a 128k` 指定音频的码率是128kb/s,`-ar 44k` 指定音频的采样频率为44kHz,完整命令如下:ffmpeg -i input.mp4 -vn -b:a 128k -ar 44k -c:a mp3 output.mp3
+- 提取视频:`ffmpeg -i input_file -an -vcodec copy output_file_name` # 就是视频消声:
+ - -an:表示no audio;
+ - 实例:`ffmpeg -i ./LOL.mp4 -an -vcodec copy 1.mp4` # 原视频是LOL.mp4,会创建一个副本1.mp4
+
+## 六、视频合并
+
+把要合并的视频放到一个文件夹里,然后把文件名写到txt,像这样:(假设下面就是`merge.txt`的内容)(只能是视频名称不能给绝对路径)
+
+>file video_1.mp4
+>file video_2.mp4
+>file video_3.mp4
+>file video_4.mp4
+>file video_5.mp4
+
+`ffmpeg -f concat -i merge.txt output.mp4` 看要不要`-c copy`
+
+## 七、音频格式转换
+
+1. MP3、wav之间:
+ - mp3转wav:ffmpeg -i 123.mp3 -f wav out.wav # wav转mp3也是一样
+ - wav/mp3文件的切分:ffmpeg -i out.wav -f segment -segment_time 30 -c copy output%03d.wav
+ - wav/mp3文件的拼接:`ffmpeg -i 0.wav -i 1.wav -filter_complex "[0:a:0] [1:a:0] concat=n=2:v=0:a=1 [a]" -map "[a]" output0.wav`
+ - 音频片段的截取跟视频是一样的:ffmpeg -ss 00:15:05 -to 00:15:35 -i .\20220110.mp3 -c copy out.mp3 # 注意如果是wav格式的切出来就是空的,那就先转成mp3格式
+ - mp3 to wav and change rate:ffmpeg -i song.mp3 -acodec pcm_u8 -ar 22050 song.wav
+2. wav转amr (安全帽调试里用到过,amr格式的音频文件会特别小,适合通过网络给终端设备发送)
+
+- ```
+ ffmpeg -i input.wav -ar 8000 -ab 12.2k -ac 1 output.amr
+ 参数解释:
+ -i input.wav:指定输入的WAV文件。
+ -ar 8000:设置音频的采样率为8000赫兹。
+ -ab 12.2k:设置音频的比特率为12.2kbit/s,这是AMR-NB格式的标准比特率。
+ -ac 1:设置音频的通道数为1,表示单声道。
+ output.amr:输出的AMR文件名。
+ ```
+
+
+
+wav音频格式转pcm格式的说明:
+[这个](https://blog.csdn.net/u011994171/article/details/88668897)、[这个](https://www.jianshu.com/p/fd43c1c82945),两个搭配起来看,。然后第转的时候,注意参数的值,可以先用ffprobe example.wav的格式再转
+
+## 八、部分画面截取
+
+ffmpeg -i a.mp4 -vf crop=200:400:0:120 -threads 4 -preset ultrafast -strict -2 b.mp4
+
+- crop的参数,分表代表,宽,高,起始x,起始y. 起点是视频的左上角
\ No newline at end of file
diff --git "a/\345\205\266\345\256\203/matplotlib.md" "b/\345\205\266\345\256\203/matplotlib.md"
new file mode 100644
index 0000000..08092a5
--- /dev/null
+++ "b/\345\205\266\345\256\203/matplotlib.md"
@@ -0,0 +1,507 @@
+一个点:要是让这个图不要在pycharm中显示,而是弹出来,那就在“Settings | Tools | Python Scientific | Show Plots in Toolwindow,去掉”
+
+`import matplotlib.pyplot as plt`
+
+x, y的值可以是列表,也可以是numpy和tensor
+
+[常见25个matplotlib图](https://mp.weixin.qq.com/s/JM3BMedF4A3JwhUT98LD4A)
+
+[又来四十个](https://mp.weixin.qq.com/s/csrQkUtr22dTa9b6WvDZ-g)
+
+或者又来50个,[原地址](https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-the-master-plots-python/),翻译成[中文](https://www.heywhale.com/mw/project/5f4b3f146476cf0036f7e51e/content)的地址,项目使用的[数据地址](https://www.heywhale.com/mw/dataset/5f4f6d4e3a0788003c4df2ce/file),这需要注册登录一个叫和鲸社区。
+
+
+
+```python
+"matplotlib中的颜色"
+cmap = plt.get_cmap('tab20b') # 里面可以放其它颜色,就成了渐变,看下面的补充连接
+print(cmap, type(cmap))
+# 或者一个固定常用的写法
+cmap = plt.get_cmap('rainbow')
+
+colors = [cmap(i) for i in np.linspace(0, 1, 20)]
+print(colors)
+
+bbox_color = random.sample(colors, 10)
+print(type(bbox_color[0]))
+print(tuple(map(lambda x: int(x*255), bbox_color[0][:3])))
+```
+
+看一下[这里](https://wangyeming.github.io/2018/11/07/matplot-cmap/)做补充。
+
+### 1、散点图(scatter)
+
+```python
+plt.scatter(x, y, c = (0, 0, 0.8), s=30, alpha=0.5,edgecolors='none')
+#s设置点的大小; #颜色c可自定义RGB(0~1之间);alpha是透明度
+```
+
+点默认蓝色+黑色的轮廓,这个参数就是不要轮廓,数据点特别多的时候使用,少的时候加上效果好
+
+##### 连续的图(.plot)
+
+```python
+plt.plot(x, y, '+', c="red")
+#给color是一样的;这个 ‘.’一定要紧随x,y坐标之后
+```
+
+##### 散点图颜色映射
+
+```python
+x = list(range(1, 501))
+y = [i**2 for i in x]
+plt.scatter(x, y, c=y, cmap=plt.cm.Oranges, s=10, edgecolors='none')
+```
+
+这是颜色映射,c给的是一堆值,那一堆值最好是从小到大,这样图就是连续渐变的一定要和cmap一起使用,值小的点颜色浅,值大的颜色深。
+
+```python
+data = np.array([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]])
+label = [0, 1, 2, 3, 4]
+colors = ['#ff0000', '#ffff00', '#00ff00', '#00ffff', '#0000ff']
+
+plt.subplot(1, 2, 1)
+for i in range(5):
+ plt.plot(data[i], data[i], '.', c=colors[i]) #这是centerloss里的画法
+plt.legend(['1', '2', '3', '4', '5'], loc='upper left')
+
+plt.subplot(1, 2, 2)
+plt.scatter(data[:, 0], data[:, 1], c=label)
+plt.legend(['1', '2', '3', '4', '5'], loc='upper left')
+plt.show()
+```
+
+但是也可以不要cmap,但这样一般就是c = target,target一般是(0, 1, 2)这种类别(可以是无序的,数据也是无序的,但是要一一对应起来),那么数据对对应自己的类别就是一种颜色,**不同类别颜色不同**(不给cmap就是系统默认;可以先写错,看有哪些值,需要注意的是,别用Oranges这类。不然就跟上面颜色映射差不多了),但是这还是是指一幅图,注意两者的不同:
+
+```Python
+from sklearn.cluster import KMeans
+
+data = np.random.randn(1000, 2)
+cls = KMeans(n_clusters = 8)
+pre = cls.fit_predict(data)
+plt.scatter(data[:, 0], data[:, 1], c=pre, cmap='twilight')
+print(pre) #注意这个是无序的,data也是无序的,但两者是一一对应起来的
+print(cls.cluster_centers_) #获取各类的中心点
+plt.show()
+```
+
+
+
+subplot,有意思的地方
+
+```python
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Generate random data
+data = np.random.rand(100)
+
+# Plot in different subplots
+fig, (ax1, ax2) = plt.subplots(1, 2)
+ax1.plot(data)
+
+ax2.plot(data)
+
+ax1.plot(data+1)
+
+plt.show()
+```
+
+### 2、折线图(plot)
+
+
+
+```python
+plt.plot(x, y, color="red", linewidth=12, linestyle=":", label="经济走势图", marker='o')
+# (":""-""--""-.",线型有这4种)(marker='o',可突出转折点,可选择的值跟plot画散点图是一样的)
+plt.legend(loc="upper left") # (必须有这行,才能显示上面的label,括号里的是显示label的位置)
+plt.legend(loc="best") # 自己找最好的位置
+plt.show()
+```
+
+##### 关于legend()
+
+```python
+plt.scatter(x, y, c='b', s=15, label="data")
+# 注意这些 label的内容是显示图例在图上,搭配legend一起的,这里有了legend里面就不用放东西了
+plt.plot(x_test, y_1, color='r', label='max_depth=1')
+plt.plot(x_test, y_2, color='y', label='max_depth=3')
+plt.plot(x_test, y_3, color='black', label='linear_model')
+plt.xlabel("data")
+plt.ylabel("target")
+plt.title("Decision Tree Regression", fontdict={'size': '16', 'color': 'orange'})
+# 可以这样来设置字体大小及颜色,上面也是同理
+
+plt.legend() # 上面有了label,括号里就不用放东西了;且散点图默认在折线图之后;它会自动找最优的位置
+# plt.legend(['max_depth=1', 'max_depth=3', 'linear_model', 'data'], loc = 'upper left')
+# 上面你两行类似,如果画图时没有给 label ,可以自己后面手动添加(注意顺序)
+```
+
+### 3、柱状图(及并列柱状图)
+
+#### bar() barh() 是横着的那种
+
+并列柱状图就是两组数据放一起对比
+
+tick_label这个参数能改变x轴的显示
+
+```python
+name_list = ["A","B","C","D"]
+num_list = [10,8,5,9] #注意两个要一样长
+
+x = list(range(len(name_list))) #bar里x的位置可以是range(4)或range(len(name_list))或[0,1,2,3];这里是为了下一个柱状图加宽度时循环好用
+plt.bar(x, num_list, width=0.4, color="r", tick_label=name_list)
+#可以直接给range(4):横轴; 给值;柱子宽度,默认0.8;颜色:红绿蓝黄,首字母,个数不够就自动循环;横轴标签
+
+num_list1 = [9,2,1,11]
+for i in range(4): #这里是将第二个柱状图的位置移动一点
+ x[i]+=0.4 #注意这个加的宽度与柱状图一样,不然会重合或有间隙
+plt.bar(x, num_list1, width=0.4, color="y", tick_label=name_list) #注意这句不能放进循环里
+plt.legend() #将两个柱状图放在一起
+plt.show()
+```
+
+"""给柱状图上加上数字""" #结合上面的数据看
+
+#### 加文字
+
+```python
+plt.rcParams["font.sans-serif"] = ["simHei"] (给个字体,使用汉字才能显示出来)
+for x, y in enumerate(num_list):
+ plt.text(x, y+5, y, ha="center", va="bottom") #在柱状图上显示具体数值, ha参数控制水平对齐方式, va控制垂直对齐方式
+ #(前面两个值是控制位置+5只是让位置好看,可调,y代表多少数值;记清enumerate的作用)
+
+from matplotlib.font_manager import fontManager
+for i in fontManager.ttflist:
+ print(i.name, i.name) # 这就获取了所有的字体
+plt.rcParams["font . sans-serif"] = ["字体"] # 上面的一些字体放进去就好了
+```
+
+#### 显示负号
+
+```python
+plt.rcParams["axes.unicode_minus"] = False # 正常显示负号
+```
+
+### 4、饼状图(pie)
+
+饼状图也可以不用给颜色,系统会自动分配
+
+```python
+name_list = ["A","B","C","D"]
+num = [12,30,50,8] #尽量加起来等于1,不然就会自动分配,就不是预想值了
+ex = [0,0,0.1,0] #有值得那个,代表那一块脱离出来,表示重点突出
+plt.axis(aspet=1) #画个一等分的圆
+plt.pie(x=num, autopct="%.3f%%", explode=ex, labels=name_list, colors="rgby", shadow=True, startangle=15)
+# 3代表3位小数; #注意这里面给颜色的关键词是colors 阴影,有立体感;角度,合适即可
+plt.show()
+```
+
+### 5、直方图(hist)
+
+```python
+plt.hist(IOUS, bins=20, density=0, facecolor="red", edgecolor="black")
+ """绘制直方图
+ data: 必选参数,绘图数据
+ bins: 直方图的长条形数目,可选项,默认为10
+ density: 是否将得到的直方图向量归一化,可选项,默认为0,代表不归一化,显示频数。
+ normed = 1,表示归一化,显示频率。
+ facecolor: 长条形的颜色
+ edgecolor: 长条形边框的颜色
+ alpha: 透明度"""
+```
+
+### 6、动态变化图
+
+##### 有移轴的方法
+
+```python
+x = np.linspace(-30,30,60) # 给出很多数
+w = -5
+
+plt.ion() #1 打开图形 为了使图形不一闪而过
+for i in range(50):
+ plt.clf() #3 清除上一条线的轨迹,没有就是每处出现的线都会停留,就有很多线
+ y = w*x
+ plt.xlim(-20,20) #4(限制x,y的坐标范围)
+ plt.ylim(-80,80)
+ plt.plot(x,y) # 要注意这要在下面坐标变换之前
+
+ ax = plt.gca()
+ ax.spines["right"].set_color("none") # 6这两行是让那里的两条线消失,可以去试试看
+ ax.spines["top"].set_color("none") #6 这之前一定要先限制x,y坐标
+
+ ax.spines["left"].set_position(("data",0)) # 5这两行是让坐标原点移动到中间(注意这要限制x、y的值是对称的)
+ ax.spines["bottom"].set_position(("data",0)) #5
+ plt.pause(0.1) #2 暂停0.1秒
+ w+=1
+plt.ioff() #1 关闭图形
+```
+
+##### plt中给汉字字体
+
+```
+plt.rcParams["font.sans-serif"] = ["simHei"]
+```
+
+```python
+import matplotlib.pyplot as plt
+from matplotlib.font_manager import fontManager
+for i in fontManager.ttflist :
+ print(i.name , i.name) # 这就获取了所有的字体
+plt.rcParams["font . sans-serif"] = ["字体"] # 上面的一些字体放进去就好了
+```
+
+### 7、subplot()
+
+取出MNIST数据集中前25个数,并在一个图上表示
+
+```python
+import matplotlib.pyplot as plt
+import torchvision as tv
+datasets = tv.datasets.MNIST("datasets/", train=True, download=False)
+
+for i in range(25):
+ plt.subplot(5, 5, i+1) #(5,5,10)代表分为5行5列,第10个图(从左至右,由上而下数) (用这个时,之前千万不要plt.clf()。)
+ plt.imshow(datasets.data[i], cmap="Accent") # plt中imshow,将矩阵数据转换为图像 . # cmap是设置背景颜色,可以先输错,下面会提示有哪些
+ plt.title(datasets.targets[i].item()) # 或者 plt.title(int((datasets.targets[i])) )
+ plt.axis("off") # 关掉轴
+ plt.xticks([])
+ plt.yticks([]) # 这两行也是关掉轴(与上面二选一即可)
+plt.show() # show()放最后循环外
+```
+
+```python
+# 有时候为了横轴上字体倾斜,不重在一起了
+plt.xticks(range(len(x), x), rotation=45) # 这是角度倾斜,前面是分组距吧,用的时候看
+```
+
+##### 改变轴刻度范围
+
+```python
+plt.axis([0, 20, 0, 210])
+#四个值,x轴最小/大值;y轴最小/大值
+```
+
+##### 改变轴刻度字体大小
+
+```python
+plt.tick_params(axis='both', labelsize=14)
+#可以单给x, y
+```
+
+##### 给轴标签
+
+```python
+plt.xlabel('Value', fontsize=14)
+```
+
+##### 保存图片
+
+```python
+plt.savefig('456.png', bbox_inches='tight')
+#保存图片,后面这个参数只是将多余的空白裁剪掉
+```
+
+##### 时间长坐标
+
+画图时有时间坐标,且坐标很长有覆盖时
+
+```python
+import matplotlib.pyplot as plt
+from random import choice
+from datetime import datetime #画图时x有日期这种的时候建议把x中标像这样转化一下,
+
+dates = []
+datas = []
+for i in range(1, 15):
+ date = '2020-03-{}'.format(10+i)
+ date = datetime.strptime(date, '%Y-%m-%d') # 将其变成datetime中的一个时间对象
+ """ '%Y-' 让 Python 将字符串中第一个连字符前面的部分视为四位的年份; '%m-' 让 Python 将第二个连字符前面的部分视为表示月份的数字;而 '%d' 让 Python 将字符串
+的最后一部分视为月份中的一天( 1~31 )"""
+ dates.append(date)
+
+ temp = choice(list(range(10, 30)))
+ datas.append(temp)
+
+fig = plt.figure(figsize=(10, 6)) #设置一下图大小
+plt.plot(dates, datas)
+
+fig.autofmt_xdate() #这个是关键,让时间坐标斜起来不覆盖
+
+plt.show()
+```
+
+重点是fig = plt.figure();fig.autofmt_xdate();以及要把x的时间格式改成datatime 的格式
+
+```python
+from datetime import datetime
+
+""" '%Y-' 让 Python 将字符串中第一个连字符前面的部分视为四位的年份; '%m-' 让 Python 将第二个连字符前面的部分视为表示月份的数字;而 '%d' 让 Python 将字符串
+的最后一部分视为月份中的一天( 1~31 )"""
+
+具体看下
+```
+
+| 实参 | 含义 |
+| ---- | --------------------------------- |
+| %A | 星期的名称,如 Monday |
+| %B | 月份名,如 January |
+| %m | 用数字表示的月份( 01~12 ) |
+| %d | 用数字表示月份中的一天( 01~31 ) |
+| %Y | 四位的年份,如 2015 |
+| %y | 两位的年份,如 15 |
+| %H | 24 小时制的小时数( 00~23 ) |
+| %I | 12 小时制的小时数( 01~12 |
+| %p | am 或 pm |
+| %M | 分钟数( 00~59 |
+| %S | 秒数( 00~61 ) |
+
+##### 颜色填充
+
+在两条x值一样,但是y值不一样的折线中间填充颜色
+
+plt.fill_between()
+
+```python
+plt.plot(x, y_1, c='red', alpha=0.5)
+plt.plot(x, y_2, c='blue', alpha=0.5)
+
+plt.fill_between(x, y_1, y_2, facecolor='blue', alpha=0.1)
+```
+
+##### 给每条线的标记
+
+```python
+plt.legend(['1', '2', '3', '4', '5'], loc='upper left')
+```
+
+用在plt.plot上比较多
+
+### 8、聚类画的轮廓系数图
+
+#### 包含了随机生成一个16进制的颜色
+
+```python
+import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams["font.sans-serif"] = ["simHei"]
+plt.rcParams["axes.unicode_minus"] = False
+
+
+def Draw(silhouette_avg, sample_silhouette_values, y, k):
+ # 创建一个 subplot with 1-row 2-column
+ fig, ax1 = plt.subplots(1)
+ fig.set_size_inches(18, 7)
+
+ # 第一个 subplot 放轮廓系数点
+ # 范围是[-1, 1]
+ ax1.set_xlim([-0.2, 0.5])
+
+ # 后面的 (k + 1) * 10 是为了能更明确的展现这些点
+ ax1.set_ylim([0, len(sample_silhouette_values) + (k + 1) * 10])
+
+ y_lower = 10
+
+ for i in range(k): # 分别遍历这几个聚类
+ ith_cluster_silhouette_values = sample_silhouette_values[y == i]
+ ith_cluster_silhouette_values.sort()
+ size_cluster_i = ith_cluster_silhouette_values.shape[0]
+ y_upper = y_lower + size_cluster_i
+
+ # 每次随机一个颜色,生成的是RGB,然后转乘十六进制
+ color = "#"
+ color_num = np.random.randint(255, size=(3,))
+ for color_ in color_num:
+ hex_str = hex(color_)
+ if len(hex_str) == 4:
+ color += hex_str[2:]
+ else:
+ color += hex_str[2:] + "0"
+
+ ax1.fill_betweenx(np.arange(y_lower, y_upper),
+ 0,
+ ith_cluster_silhouette_values,
+ facecolor=color,
+ edgecolor=color,
+ alpha=0.7)
+ # 在轮廓系数点加上聚类的类别号及个数
+ ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, "{}类:{}个".format(i, len(ith_cluster_silhouette_values)),
+ fontdict={'size': '10', 'color': color})
+ # 计算下一个点的 y_lower y轴位置
+ y_lower = y_upper + 10
+
+ # 在图里搞一条垂直的评论轮廓系数虚线
+ ax1.axvline(x=silhouette_avg, color='red', linestyle="--") # x=横坐标上的一点的值
+ ax1.text(0.005, 0.95, "总个数:{}".format(len(sample_silhouette_values)),
+ transform=ax1.transAxes, fontdict={'size': '14', 'color': 'orange'})
+ ax1.text(0.005, 0.90, "平均轮廓系数:{}".format(round(silhouette_avg, 4)),
+ transform=ax1.transAxes, fontdict={'size': '14', 'color': 'orange'})
+ plt.xlabel("轮廓系数", fontdict={'size': '12'})
+ plt.title("聚类图", fontdict={'size': '16', 'color': 'orange'})
+
+ plt.show()
+
+
+if __name__ == '__main__':
+ k = 4
+ silhouette_avg = 0.43125298764496467 # 平均轮廓系数
+ sample_silhouette_values = np.array([0.91922182, 0.22907922, 0.15196871, 0.20541037, 0.23250248, 0.95771555,
+ 0.94055218, 0.12501744, 0.17844737, -0.16478621, 0.11131155, 0.9489616,
+ 0.19678223, 0.94595924, 0.2104268, 0.13250568, 0.17998956, 0.22572757,
+ 0.95779809, 0.9404685]) # 每个点的轮廓系数
+ # 这是每个点的分类
+ catrgory = np.array([2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 3, 1])
+ Draw(silhouette_avg, sample_silhouette_values, catrgory, k)
+```
+
+#### 文本位置及subplot的一些
+
+```python
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.collections as collections
+t = np.arange(0.0, 2, 0.01)
+s1 = np.sin(2*np.pi*t)
+s2 = 1.2*np.sin(4*np.pi*t)
+
+fig = plt.figure(figsize=(15, 6))
+ax = fig.add_subplot(1, 2, 1)
+
+ax.set_title('using span_where')
+# 设置曲线
+ax.plot(t, s1, color='black')
+# 设置横轴
+ax.axhline(0, color='black', lw=2)
+# 设置文本,目标位置 左上角,距离 Y 轴 0.01 倍距离,距离 X 轴 0.95倍距离
+ax.text(0.01, 0.95, "I am here ", transform=ax.transAxes, fontdict={'size': '16', 'color': 'b'})
+
+collection = collections.BrokenBarHCollection.span_where(
+ t, ymin=0, ymax=1, where=s1 > 0, facecolor='green', alpha=0.8)
+ax.add_collection(collection)
+
+collection = collections.BrokenBarHCollection.span_where(
+ t, ymin=-1, ymax=0, where=s1 < 0, facecolor='red', alpha=0.5)
+ax.add_collection(collection)
+"""
+ax2
+"""
+ax2 = fig.add_subplot(1, 2, 2)
+
+ax2.set_title('using span_where')
+ax2.plot(t, s1, color='black')
+ax2.axhline(0, color='black', lw=2)
+# 设置文本,目标位置 左上角,距离 Y 轴 1.91 倍距离(这里说明它是以最左边为轴,需要自行调节),距离 X 轴 0.95倍距离
+ax2.text(1.91, 0.95, "I am here too", transform=ax.transAxes, fontdict={'size': '16', 'color': 'b'})
+
+collection = collections.BrokenBarHCollection.span_where(
+ t, ymin=0, ymax=1, where=s1 > 0, facecolor='green', alpha=0.8)
+ax2.add_collection(collection)
+
+collection = collections.BrokenBarHCollection.span_where(
+ t, ymin=-1, ymax=0, where=s1 < 0, facecolor='red', alpha=0.5)
+ax2.add_collection(collection)
+plt.show()
+```
+
diff --git "a/\345\205\266\345\256\203/numpy.md" "b/\345\205\266\345\256\203/numpy.md"
new file mode 100644
index 0000000..6345396
--- /dev/null
+++ "b/\345\205\266\345\256\203/numpy.md"
@@ -0,0 +1,468 @@
+`import numpy as np`
+
+弧度和度之间的转换,通过两个点算出直线的斜率slope=1,意味着就是45°,那么
+
+np.arctan(slope) * 180 / np.pi # np.arctan(slope)先求出来的是弧度,然后要转成弧度
+
+
+
+100 个 Numpy 闯关小例子,[这里](https://mp.weixin.qq.com/s/mMQcm7ntXmp4PcYv32Vyug)。
+
+### 0. np.info()
+
+用来获取np中函数的说明及一些简单示例:
+
+```python
+np.info(np.stack)
+```
+
+### 1.stack()与.concatenate()
+
+==数据拼接==:
+
+"""故可以总结,一般来说,如果每个数据是一维,torch和numpy都用.stack()
+如果每个数据都是2维以上,用torch.cat(),和numpy.concatenate();
+注意括号里可以是类似于(x,y)的元祖,也可以是每个元素是单个数据组成的列表
+即类似于是np.stack(list); np.concatenate(list, axis = 0),"""
+
+```python
+a = np.array([1, 2, 3])
+b = np.array([4, 5, 6])
+c = np.array([7, 8, 9])
+ls = [a, b, c]
+print(ls)
+
+x = np.arange(6).reshape(2, 3)
+y = np.arange(6).reshape(2, 3)
+print(np.stack((x, y))) #这会增加维度,变成3维了
+print(np.concatenate((x, y))) #依旧是2维
+```
+
+### 1.切片索引
+
+##### [...,2]的意义
+
+```python
+a = np.arange(125).reshape(5, 5, 5)
+print(a)
+print(a[0:4:2, 0]) #索引的时候是可以给步长的
+print(a[[0, 3], 4]) #这代表具体到,第一个维度,我只要第0个和第3个(就是针对不连续的,特定的),然后他们的第4个数(记得那个括号)
+# print(a[[0, 3], [2,8]]) #这跟上面同理,就是为了要获取特定的不连续的数据
+```
+
+```
+print(a[..., 1]) #这就是前面维度不管,只要最后一个维度的第一个数据;;这会降低一个维度,切片嘛
+```
+
+```python
+x = np.arange(10, 1, -1) # 起始比末尾大,给个-1倒着来
+```
+
+### 2.重复某个维度(repeaet)
+
+```python
+x = np.random.randn(2, 4, 3)
+print(x)
+
+y = x.repeat(6, axis = 1) #可以选定复制扩张的维度,这就代表将1轴上的所有都复制为原来的6倍
+# y = x.repeat((6, 6, 6, 6), axis = 1) #这行的意思跟上一行一模一样
+# y = x.repeat((3, 2, 4, 8), axis = 1) #选定复制轴后,也可以给一个元祖,但这个元祖的个数必须和所在轴的元素个数一样(这里1轴就有4个元素),这里就代表第一个复制3次;第二个复制2次,第三个复制4次,第四个复制8次
+print(y)
+```
+
+### 2. 广播(tile)
+
+```python
+x = np.random.randint(0, 10, size = (3, 4, 5))
+print(np.tile(x, (1, 2, 3)).shape) #(3, 8, 15)
+#要在哪个维度上广播几次就是几,不广播的要写1,形状要一样
+
+"""这跟repeat()重复是不一样的"""
+y = np.random.randint(0, 10, size = (2,))
+print(y) #[5 4]
+print(np.tile(y, 2)) #[5 4 5 4]
+print(np.repeat(y, 2)) #[5 5 4 4]
+```
+
+### 3.np.random.permute()
+
+==将一组数据无序打乱==:
+
+```python
+a = np.arange(40).reshape(4, 2, 5)
+x = np.random.permutation(40) #生成一个一维数组(40个数),顺序全被打乱,也可将40改成np.arange(20, 50)
+y = np.random.permutation(a) #也可以是一个多维数组,按照第0维度打乱
+
+np.random.permutation(ndaarray) # 结果是将这个数组或者列表里的数据打乱
+```
+
+### 4.np.maximum(x,y)
+
+==多个值同时做大小比较,并行计算==:
+
+```python
+x = [1, 2, 3, 4, 5]
+y = [2, 4, 1, 3, 6]
+print(np.maximum(y, x)) #这种的话,x,y的长度和形状必须一样,对应位置作比较
+print(np.maximum(3, x)) #顺序无所谓,长的那个去和那一个值逐一比较,并返回值
+```
+
+torch.max跟np.max有点不一样,torch.max可以直接实现np.maximum
+
+### 4.np.max()
+
+==这个max不简单,不要轻易用==:
+
+```python
+a = np.arange(24).reshape(2,3,4)
+a[0,1,2] = 66 #修改一个参数,一定要看这66的位置
+print(a)
+print("=============")
+b = np.max(a,axis=1) #还是把这个几行代码运行看看“0”“1”“2”的区别吧
+print(b)
+```
+
+### 5.tensor 转 ndarray
+
+```python
+boxes = []
+x1 = torch.tensor(120)
+y1 = torch.tensor(200)
+x2 = torch.tensor(301)
+y2 = torch.tensor(452) #这只能是一个值的,若是torch.tensor([12, 56])都是不行的
+cls = torch.tensor(0.6)
+boxes.append([x1, y1, x2, y2, cls])
+boxes.append([x1, y1, x2, y2, cls]) #这样是可行的,只要最低维度的那个数是one element tensors
+print(boxes)
+print(np.array(boxes))
+
+"""所以极度建议就是np.stack(boxes);一般这boxes是一个列表"""
+np.array(list)和np.stack(list)效果一模一样;但是如上所说np.array()是有局限性的。
+```
+
+ndarray转成list:==.tolist()==
+
+```python
+c = [[1, 2], [3, 4], [5, 6]]
+x = np.array(c)
+print(x.tolist())
+```
+
+### 6.sort() 与 .argsort()
+
+==可按照指定位置的值从小到大排序,返回相对应的索引==:
+
+```python
+""".sort()会直接改变当前值,没有返回值"""
+a = np.array([[5,4],[3,2],[1,8],[7,9]])
+# print(a)
+# a.sort(axis = 1) #0是各列自己比较自上而下,从小到大;1是各行之间比较,自左而右,从小到大,默认值是axis = 1 # 慎用,结果很可能不是你想要的那种
+# print(a)
+
+""".argsort(),"""
+index = (a[:, 1]).argsort() #注意加一个“负号”,即(-a[:, 1]).argsort(),就是从大到小;也可以将里面的参数descending设置为True
+print(a[:, 1])
+print(a)
+print(index)
+print(a[index]) #这样就是每组数据间,整组在排序,就不会打乱每组数据内部的值
+```
+
+### 7.np.divide()和np.true_divide()
+
+上面两个是一样的;另外有一个np.floor_divide()是只保留整数
+
+### 8.np.where(condition, x, y)
+
+==根据condition,满足条件置为x,不满足条件置为y==:
+
+```
+比如得到给一组图片加噪声时,所有的像素值都加上一个值20,那有的就会超过255,这时就是:
+data = np.whrer(data>255, 255, data) # 就是说data里的值大于255就置为255,否则就不变
+```
+
+```
+"""详解:
+condition一般是一个比较运算,前面是一个ndarray,后面一个值,得到的结果的形状就是ndarray,值不是x就是y
+若只是np.where(condition),得到的结果跟np.nonzero(condition)一模一样;他们的结果都可以直接当做索引去取值,
+例如:x = np.arange(27).reshape(3, 3, 3),
+print(x[np.where((x>5))])
+print(x[np.nonzero(a>5)])
+print(x[x>5]) 这三个得到的结果一模一样,且都是一维的;有时候可以直接用那个简单的代替,但有的情况是不行的"""
+```
+
+#### np.where(x>3)
+
+这是获取x中大于3的值的索引,,针对其它二维及以上建议使用==torch.nonzero()==获取索引
+
+```
+"""针对(n,1)的形状"""
+x = np.array([6, 2, 3, 4, 5, 2, 1, 4]).reshape(-1, 1)
+index = np.where(x>3)[0]
+print(index)
+
+"""针对一维数据"""
+x = np.array([6, 2, 3, 4, 5, 2, 1, 4])
+index = np.where(x>3)[0]
+print(index)
+```
+
+ Tips:在与opencv结合使用时,==可能在使用了np.where后数据类型是int32,而导致后续操作opencv时报错,可能是imshow时,所以用完后,后面跟个.astype("float32")或uint8==。
+
+#### np.argwhere()
+
+==获取所有非零元素或满足条件的值的索引==:
+
+```python
+a = np.array([[1,2,3,4],[5,6,7,8]])
+index = np.argwhere(a>3)
+print(index) # 结果就就是array,里面的值共5个,都是满足的索引
+或者
+print(np.argwhere([0, 1, 2, 0, 5])) # 这得到的就是1,2,5这些非零值的索引
+```
+
+#### np.clip()
+
+跟where差不多,一坑会简洁一些:
+
+> Examples
+> --------
+> >>> a = np.arange(10)
+> >>> np.clip(a, 1, 8)
+> >>> array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])
+> >>>
+> >>> a
+> >>> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+> >>> np.clip(a, 3, 6, out=a)
+> >>> array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6])
+> >>>
+> >>> a = np.arange(10)
+> >>> a
+> >>> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+> >>> np.clip(a, [3, 4, 1, 1, 1, 4, 4, 4, 4, 4], 8)
+> >>> array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])
+
+### 9.astype()
+
+==转数据类型==:
+
+```python
+x = x.astype("float32")
+x = x.astype("uint8")
+x = x.astype(np.int64)
+```
+
+### 10. 0、1矩阵
+
+```python
+a = np.zeros(shape=[4,10],dtype=np.float32)
+print(a)
+b = np.ones(shape=[4,10],dtype=np.float32)
+print(b)
+c = np.empty(shape=[4,10],dtype=np.float32) #empty()表示:跟最近的内存空间上数据一样,前面没有就是随机生成一个
+print(c)
+
+d = np.eye(3) #生成的是单位矩阵,行列式为1
+```
+
+### 11. 矩阵相乘
+
+```python
+print(a.dot(b))
+print(a@b) #两个是一个意思
+print(a.T) #就是得到a 的转置矩阵
+```
+
+### 12.transpose() 与 swapaxes()
+
+==交换轴==:
+
+```python
+a = np.arange(24).reshape(2, 3, 4)
+b = a.transpose((2,0,1)) #要括号,且要写上所有轴的顺序
+c = np.swapaxes(a, 2, 0) #换的轴不要括号,指定两轴之间交换
+print(b.shape) #记不大清时,就np开头
+print(c.shape)
+```
+
+### 13.随机数|各种分布
+
+随机种子:
+ 在生成随机数前加上np.random.seed(1);就会将后面生成的随机数全部固定,即无论运行多少次,每次都相同。
+
+```python
+a = np.random.randint(0, 10, size = (5,5)) #生成0~10范围内的整数(包括0但不包括10)
+
+b = np.random.randn(3, 3) #标准正态分布
+b = np.random.normal(1, 5, 100) #生成指定的正态分布(1为对称轴,5为y上最大幅度,共100个数)
+
+c = np.random.raemnd(5,5) #也可以只放一个数字,唯独这不要括号
+c = np.random.random((4,3))
+c = np.random.ranf((3,3)) #上面三个都是0~1之间的均匀分布
+c = np.random.uniform(-1,1,100) #指定均为分布
+```
+
+### 15.random.sample()
+
+==随机取样==:
+
+```python
+rndom.sample(range(12), k =5) #从第一个参数(列表,元组里)随机取出k个不重复的数
+```
+
+```python
+np.random.sample(5, size=3, replace=False) # 从[0, 5)之间取3个数,replace默认是True:有放回取样(就可能会重复); False:不放回取样,没有重复
+
+np.random.sample(['a', 'b', 'c'], 2, replace=False, p=[0.5, 0.3, 0.2]) # p是用来生成一个不均匀的样本,相当于带权重
+```
+
+### 16. 打印零元素索引(nonzero)
+
+```python
+print(np.nonzero(a)) # 打印a中非零元素的索引
+print(np.nonzero(a-1)) # 打印a中零元素的索引
+```
+
+### 17.floor()与ceil()
+
+==取整==:
+
+```python
+ny.floor() #向下取整
+ny.ceil() #向上取整
+
+import math
+math.floor(1.2) #向下取整
+math.ceil(1.3) # 结果是2,向上取整
+```
+
+### 18.增减轴
+
+.squeeze()减轴
+
+```python
+x = np.random.randint(10, size = (5,1,3,1,4))
+y = x.squeeze() #减少对应轴,且该轴值为1,否则不会改变;也可不给轴,那就是默认去掉所有值为1的轴
+```
+
+加轴
+
+```python
+x = np.random.randint(10, size = (5,1,3,1,4))
+z = x[:, np.newaxis, :, np.newaxis, :, np.newaxis]这就是在对应位置加轴加轴;
+z = x[:, None, :, None, :, None] #这里的None同np.np.newaxis
+print(z.shape) #(5, 1, 1, 1, 3, 1, 1, 4)
+#当所加的轴在最开始或最后时,可以结合[None, ...] [..., None]这去
+```
+
+但是都能通过reshape达到想要的效果,结果是一样的,已测试。
+
+新增一个:假设 input_img 的形状是(3, 256, 256)
+
+```python
+input_img = np.expand_dims(input_img, 0) # 现在的shape就是(1, 3, 256, 256)
+# input_img = input_img[np.newaxis, ...] # 这种也是可以的
+```
+
+### 19.mean()|std()
+
+a = np.array([1, 2, 3])
+
+==求均值==:numpy和pytorch求均值都是要求那个通道均值,就给哪个维度
+
+```python
+x = torch.randn(2, 3, 5, 5)
+# print(x)
+print(x.mean(dim = 1).shape)
+print(x.mean(dim = (0, 2, 3))) #做采样时,这样就是除了c全做mean,剩下值的个数就是3了
+```
+
+==求标准差==:
+
+- np.std(a) # 结果是0.816496580927726 因为这个公式中,求完和,是除以n并不是除以n-1
+- np.std(a, ddof=1) # 结果是1.0 这里就是除以的 n-ddof,即n-1, 这严格意义算是标准差的公式,更多的一个解释说明可以看看[这里](https://blog.csdn.net/zbq_tt5/article/details/100054087)。
+
+==求方差==:(标准差就是方差开算术平方根来的)
+
+- np.var(a) # 结果是 0.6666666666666666 ,开算术平方根就是 0.816496580927726
+- np.var(a, ddof=1) # 这个就是同上了
+
+### 20.np.split()
+
+```python
+x = np.random.randn(4, 10, 10, 3)
+images = np.split(x, 4, axis = 0) # 4是分成的份数,后面是在那个轴上分;得到的是list
+```
+
+### 21. np.linspace
+
+==生成ndarray==:
+
+```python
+a = np.linspace(0, 2, 10) # 0到2之间平均分成10份的数,能取到2
+print(a)
+```
+
+### 22.Nan说明
+
+==首先声明任何nan是不等于nan的==。
+
+ 所以假设有一个数组是a,那么可以a != a,统计结果中有多少个True,那就是有多少个nan,np.count_nonzero(a!=a);
+
+ 也可以直接是np.count_nonzero(np.isnan(a))
+
+获取非空的值:values = values[~np.isnan(values)] # 通过 ~ 来取反
+
+### 23. save && load
+
+numpy.save(file, arr, allow_pickle=True, fix_imports=True)
+
+后面两个参数用默认值即可,allow_pickle:布尔值,允许使用Python pickles保存对象数组,fix_imports:为了方便Pyhton2中读取Python3保存的数据
+
+np.save:保存单个数组为一个文件
+
+```python
+a = np.arange(5 * 6 * 7).reshape((5, 6, 7))
+np.save("123", a) # 就会得到 “123.npy” 文件,会自己加后缀名 .npy
+# 加载
+data = np.load("123.npy")
+```
+
+np.savez:保存多个数组为一个文件
+
+```python
+x = np.arange(5 * 6 * 7).reshape((5, 6, 7))
+y = np.sin(x)
+# 一、注意这传参
+np.savez("datas", x, y) # 这会得到 "datas.npz"
+datas = np.load("datas.npz")
+print(datas["arr_0"]) # 这种就是 arr_0 代表x, arr_1 代表y,key就是系统给的
+
+# 二、推荐使用这(自己指定key的名子,方便后面load时使用)
+np.savez("key_datas", x=x, sin_x=y) # 这会得到 "key_datas.npz"
+datas = np.load("key_datas.npz", allow_pickle=True)
+print(datas["x"]) # 这种就是 x 就是上面自己指定的x的key
+print(datas["sin_x"]) # sin_x就是上面自己指定的y的key
+```
+
+Tips:
+
+- 如果加载出来的datas,不知道它的key的话,就把datas当字典来处理:keys = list(datas.keys()) # 这样就能拿到它所有key,或者 for key, value in datas.items(): 也是可以用的
+- 对于外界打包的.npz,可能要加上参数allow_pickle=True才能够查看数据;
+- .npz就是一个压缩文件,它其实是前面save的多个.npy文件压缩而来的,可以通过解压软件打开查看的。
+
+### 24. delete
+
+返回删除指定数据后的数组,==不改变原数组==。有点替代mask的味道
+
+```python
+import numpy as np
+arr = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
+arr1 = np.delete(arr, 1, axis=0) # 返回删除第index为1的行的数组
+arr2 = np.delete(arr, [0, 3], axis=0) # 返回删除第index为[0,3]行的数组
+# axis = 1 就是按列删除
+
+# 其它使用方式
+arr3 = np.delete(arr, [1,3,5], axis=None)
+# arr3: array([ 1, 3, 5, 7, 8, 9, 10, 11, 12]) # 变一维了
+```
+
diff --git "a/\345\205\266\345\256\203/sklearn.md" "b/\345\205\266\345\256\203/sklearn.md"
new file mode 100644
index 0000000..fe3dd66
--- /dev/null
+++ "b/\345\205\266\345\256\203/sklearn.md"
@@ -0,0 +1,184 @@
+## 余弦相似度
+
+```python
+import numpy as np
+from sklearn.metrics.pairwise import cosine_similarity, pairwise_distances
+# pairwise_distances函数是计算两个矩阵之间的距离,
+# cosine_similarity函数是计算多个向量互相之间的余弦相似度,
+# 这两的参数是一样的 ps:值可以是列表,但得是二维的
+
+可以是:
+m1 = [[1, 2, 3]]; m2 = [[4, 5, 6]]
+out = cosine_similarity(m1, m2); out = pairwise_distances(m1, m2)
+结果是类似这样 [[0.458]] 的一个值;
+
+也可以是:
+values = [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 8, 9, 10]] # 形状(n, m)
+out = cosine_similarity(values); out = pairwise_distances(values)
+得到的结果shape是(n,n);out[0, 0]是第一行跟第一行的结果,out[0, 1]就是第一行跟第二行的结果,out[1, 0]就是第二行跟第一行的结果
+
+1、首先看这里吧,不懂再去看上面
+pairwise_distances(values) # 默认方式得到的就是欧氏距离的值
+pairwise_distances(values, metric="cosine") # 得到的是余弦距离 = 1-余弦相似度
+cosine_similarity(values) # 得到的就是余弦相似度
+
+2、手写一个
+vector_a = np.mat(vector_a)
+vector_b = np.mat(vector_b)
+num = float(vector_a * vector_b.T)
+denom = np.linalg.norm(vector_a) * np.linalg.norm(vector_b)
+sim = num / denom # 这也是余弦相似度
+```
+
+## 特征处理
+
+### DictVectorizer
+
+```python
+# 对字典类型数据进行特征值化
+from sklearn.feature_extraction import DictVectorizer
+
+data = [{"city": "北京", "temperature": 100}, {"city": "上海", "temperature": 60}, {"city": "深圳", "temperature": 30}]
+dicvec = DictVectorizer(sparse=False) # 加了这个得到的就不是sparse矩阵了,默认为True
+
+data = dicvec.fit_transform(data) # 得到的是默认的sparse矩阵(因为scipy的封装),节约内存,方便读取处理;;就是有值得地方就给索引和值。0的地方不管
+print(dicvec.get_feature_names()) # 各列特征值的名称
+print(data) # 若是spare矩阵,可以data.toarray()
+
+data = dicvec.inverse_transform(data) # 就是把特征值又转换回去
+```
+
+### CountVectorizer
+
+```python
+# 对文本数据进行特征值化
+# 统计文本中出现的词,及其在一个元素中出现的个数; ps:单汉子、字母不统计
+# 一个元素里,可以是逗号隔开,也可以是空格
+from sklearn.feature_extraction.text import CountVectorizer
+corpus = ["life,is is short, i like python", "lifess is too long, i dislike python"]
+
+counvec = CountVectorizer(stop_words=) # 还可以给 stop_words
+# 返回词频矩阵
+data = counvec.fit_transform(corpus)
+data = counvec.fit_transform(corpus).todense() # 加个这,data就直接是ndarray了
+print(counvec.get_feature_names())
+print(data.toarray()) # 结果是二维的,因为列表是2个值
+```
+
+### TfidfVectorizer
+
+```python
+from sklearn.feature_extraction.text import TfidfVectorizer
+
+tfidf = TfidfVectorizer(stop_words="放需要停用的词")
+data = tfidf.fit_transform([c1, c2, c3]) # c1、c2、c3各自代表一篇文章,且已经用jieba分好词;
+print(tfidf.get_feature_names())
+print(data.toarray()) # 得到的应该就是词频矩阵,数值越大的越有用吧(就是重要性越大)
+```
+
+和起来常用的TFIDF算法
+
+```python
+# 将文本中的词语转换成词频矩阵,矩阵元素 a[i][j] 表示j词在i类文本下的词频
+corpus = ["life,is is short, i like python", "lifess is too long, i dislike python"]
+vectorizer = CountVectorizer()
+data = vectorizer.fit_transform(corpus) # 得到词频矩阵
+
+计算tfidf
+tfidf = transformer.fit_transform(data)
+# 获取词袋模型中的所有词语
+word = vectorizer.get_feature_names()
+# 将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
+weight = tfidf.toarray()
+```
+
+
+
+```python
+ from sklearn.metrics import classification_report # 函数 分类报告
+ from sklearn.metrics import confusion_matrix # 函数 混淆矩阵
+
+ print(classification_report(y_test_cls, y_pred_cls, target_names=categories))
+ print(confusion_matrix(y_test_cls, y_pred_cls))
+
+ # y_test_cls 数据本来的类别 [0 0 3 2 3 1]
+ # y_pred_cls 预测类别 [0 0 2 2 3 1]
+ # categories 这个也是一个列表,放着各种类别的名字 ["新闻", "财经", "房产", "教育"] 对应上面4个类别
+```
+
+
+
+## 数据预处理
+
+### 归一化、标准化
+
+```python
+from sklearn.preprocessing import MinMaxScaler, StandardScaler
+
+# 归一化 # 只适合传统精确小数据,离群点影响很大
+mm = MinMaxScaler() # 缩放的范围,可以不给,默认是0~1
+data = mm.fit_transform([[90, 2, 10], [60, 4, 15], [75, 3, 13]])
+print(data)
+
+
+# 标准化,把原始数据变换到均值为0,方差为1的范围内;x减去均值再除以方差
+
+ss = StandardScaler()
+ss.fit_transform("data")
+ss.mean_ # 原始数据每列的均值
+ss.std_ # 原始数据每列的方差
+```
+
+### 缺失值补全
+
+```python
+# numpy自带的fillna只能填补np.nan,而此处则可以指定空值的类型。比如? 或N/A
+from sklearn.impute import SimpleImputer
+# 比如当空值是?时,使用0填充:(策略里还可以是均值mean这些)
+sim_imp = SimpleImputer(missing_values="?", strategy='constant', fill_value=0) # 注意此处,data是如果是0维,则要先变为一维:
+# sim_imp = SimpleImputer(strategy="median")
+data = sim_imp.fit_transform(data)
+```
+
+## 数值降维
+
+```python
+from sklearn.feature_selection import VarianceThreshold
+
+# 删低于给定方差阈值的数据
+var = VarianceThreshold(threshold=0.0) # 默认值是0,即删除相同方差的数据;给的这个值就是方差阈值
+data = var.fit_transform([[0, 2, 0, 3], [0, 1, 4, 3], [0, 1, 1, 3]])
+print(data) # 第1列和第4列一样的,方差是0就删了
+```
+
+```python
+from sklearn.decomposition import PCA
+
+# 主成分分析,降维
+pca = PCA(n_components=0.9) # 这种给小数的就是保留90%的特征,一般就是给0.9~0.95吧;还可以给维度数
+data = pca.fit_transform([[2, 8, 4, 5], [6, 3, 0, 8], [5, 4, 9, 1]])
+print(data)
+```
+
+## 结果评价
+
+`"Precision, Recall and F1-Score..."`
+
+```python
+from sklearn.metrics import classification_report
+# 生成评价报告
+y_label_cls = np.array([0, 0, 2, 1, 3, 3]) # 这是这6个数据的真实标签
+y_pred_cls = np.array([0, 0, 2, 1, 3, 0]) # 假设这是测试的6个数据得到的预测结果
+
+"Precision, Recall and F1-Score..."
+out = classification_report(y_label_cls, y_pred_cls, target_names=["游戏", "体育", "经济", "民生"]) # 可以不给target_names,也就是每类的名字
+```
+
+`混淆矩阵:Confusion Matrix...`
+
+```python
+# 接着上面的数据
+from sklearn.metrics import confusion_matrix
+cm = confusion_matrix(y_label_cls, y_pred_cls)
+```
+
diff --git "a/\345\205\266\345\256\203/torch.md" "b/\345\205\266\345\256\203/torch.md"
new file mode 100644
index 0000000..b43d533
--- /dev/null
+++ "b/\345\205\266\345\256\203/torch.md"
@@ -0,0 +1,294 @@
+`import torch`
+torch.cuda.empty_cache()
+
+[这是](https://mp.weixin.qq.com/s/Q9AMwGNV9fLaRZws4oaW4g)pytorch的几十个常用操作代码片段,用到时可以来看看。
+
+## 1.stack()与.cat()
+
+### 数据拼接
+
+"""故可以总结,一般来说,如果每个数据是一维,torch和numpy都用.stack()如果每个数据都是2维以上,用torch.cat(),numpy.concatenate();注意括号里可以是类似于(x,y)的元祖,也可以是每个元素是单个数据组成的列表即类似于是torch.stack(list);torch.cat(list, dim = 0)"""
+
+```python
+a = torch.tensor([1, 2, 3])
+b = torch.tensor([4, 5, 6])
+c = torch.tensor([7, 8, 9])
+ls = [a, b, c]
+print(ls)
+print(torch.stack(ls)) #堆叠
+print(torch.cat(ls, 0)) #续接
+#这俩的相反操作是torch.split()和torch.chunk()
+
+x = torch.arange(6).view(2, 3)
+y = torch.arange(6).view(2, 3)
+ls= [x, y]
+print(torch.stack((x, y))) #这会增加维度,变成3维了
+print(torch.cat((x, y))) #依旧是2维
+print(123)
+print(torch.stack(ls))
+print(torch.cat(ls))
+```
+
+## 2.expand()与.repeat()
+
+### 重复某个维度数据
+
+```python
+x = torch.randn(2,1,5) #若这里是(2, 2, 5),都不能使用extend去扩张复制为(2, 4, 5)
+print(x)
+y = x.expand(2, 4, 5) #注意,toech.extend();这里的形状维度必须和原tensor一样,且其只能扩张所在维度为1的tensor;
+print(y)
+z = x.repeat(1, 2, 1) #torch.repeat(),里面的形状也要跟原tensor一样,里面的数字代表所在维度复制为原来的几倍
+print(z)
+```
+
+## 3.eq() lt() gt() le() ge()
+
+### 比较
+
+eq:等于,gt:大于,lt:小于,ge:大于等于,le:小于等于,
+
+```python
+x = torch.Tensor([9., 4., 7., 6., 0., 3., 2., 8., 5., 1.]).view(10, 1)
+result = torch.lt(x, 5) #结果返回的是所有值和5比较后的布尔值(小于5为True)
+也可以两个对比值都是tensor:
+print(torch.lt(torch.Tensor([[1, 2], [3, 4]]), torch.Tensor([[1, 1], [4, 4]])))
+```
+
+## 4.masked_select(y, mask)
+
+### 通过掩码选定数据
+
+```python
+x = np.random.permutation(40)
+x1 = torch.Tensor([9., 4., 7., 6., 0., 3., 2., 8., 5., 1.]).view(10,1)
+y = torch.Tensor(x).view(10, 4)
+mask = torch.gt(x1, 5)
+print(mask)
+print(y)
+print(torch.masked_select(y, mask)) #前面是被筛选的数据,后面是掩码,一定是这顺序
+#注意两个的第一个维度数目必须一样,一般mask都是(n,1)的布尔值,然后前面y可以是(n,m),m不用确定的二维的
+#注意:最终结果会被拉成一维的,按顺序每m个为y中第0维度的1组数据
+```
+
+### .index_select()
+
+#### 通过指定索引获取数据
+
+可搭配torch.histc()使用;注意这个一定就只是去取东西,也可以作为索引的label比元数据长,但是值不能超过其长度,因为是作为索引去取值,那么就能够按照标签规律去扩长原数据。
+
+```python
+""".idnex_select()"""
+x = torch.tensor([1, 2, 3, 4, 5])
+print(x.index_select(dim = 0, index = torch.tensor([0, 2, 4]))) #[1, 3, 5]
+
+x = torch.tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
+print(x.index_select(dim = 0, index = torch.tensor([1]))) #[[6, 7, 8, 9, 10]]
+print(x.index_select(1, torch.tensor(1))) #[[2], [7]]
+# index_select通过给的索引index按照给的dim获前面x取值,注意类似于切片,不会降低维度。
+# 给的作为索引的tensor的数据一定要.long()一下,类似于.float()
+
+""".histc 统计不重复元素的个数"""
+label = torch.tensor([1, 0, 2, 1, 0, 1, 2]).float()
+count = torch.histc(label, bins = int(max(label).item() + 1), min = int(min(label).item()), max = int(max(label).item()))
+#bins是不重复元素的种类,一般是标签来,因为有0,所以就是其最大值+1;min和max就是最小/大值;注意这的数据类型不能是long,参数都得是int,不一定要item(),只是保险
+print(count) #[2., 3., 2.]
+#注意得到的结果是按照给的标签的里面从小到大的顺序给的,这里即按照(0,1,2)的个数来给的
+#就是因为上面的特性,常与.index_select()一起用,但是标签中的数一定要连续,即类似(0, 1, 2, 3),不能是(0, 2, 3)这种,
+```
+
+## 5.torch.nonzero(a )
+
+### 得到非0值的索引
+
+"""二维及以上推荐使用这,且不是(n, 1)这样的二维""";一维及(n,1)这样建议使用np.where(a>3)去获取满足条件的索引。
+
+```python
+a = torch.tensor([[1,2],[0,3],[1,6]])
+print(torch.nonzero(a)) #结果是(5,2)得到a中所有的非0索引。结果维度同a
+```
+
+### mask掩码使用
+
+```python
+x = torch.randn(2,13,13,3,15) #假设这的意义是(n, h, w , c, iou+偏移率+分类),想YOLO
+mask = x[..., 0]>0
+print(mask.shape) #(2, 13, 13, 3),上面是通过最后一个维度的第0个值去看
+
+indexes = torch.nonzero(mask) #根据最后一个维度来,这种索引的形状一定是前面三个那种
+values = x[mask]
+
+print(indexes.shape) #(m, 4) m是代表有不定的很多个,后面的4就代表了n,h,w,c所在维度的值
+print(values.shape) #(m, 15) 上下这两个m的值一样大,后面的15就代表了最后那个维度的所有特征
+```
+
+## 6.tensor转ndarray
+
+### 把单个tensor组成的一个列表,再把这样的多个列表放进一个列表中,可以直接转成numpy
+
+```python
+boxes = []
+x1 = torch.tensor(120)
+y1 = torch.tensor(200)
+x2 = torch.tensor(301)
+y2 = torch.tensor(452) #这只能是一个值的,若是torch.tensor([12, 56])都是不行的
+cls = torch.tensor(0.6)
+boxes.append([x1, y1, x2, y2, cls])
+boxes.append([x1, y1, x2, y2, cls]) #这样是可行的,只要最低维度的那个数是one element tensors
+print(boxes)
+print(np.array(boxes))
+```
+
+```python
+print(torch.tensor(c)) 或者c = torch.from_numpy(c) # c是ndarray转torch的tensor
+```
+
+## 7.argsort()
+
+### 可按照指定位置的值从小到大排序,返回相对应的索引
+
+```python
+"""还有.argmax() .argmin()获取最大值,最小值索引"""
+
+a = torch.tensor(([[5,4],[3,2],[1,8],[7,9]]))
+index = (a[:, 1]).argsort() #注意加一个“负号”,即(-a[:, 1]).argsort(),就是从大到小;也可以将里面的参数descending设置为True
+print(a[:, 1])
+print(index)
+print(a)
+print(a[index]) #这样就是每组数据间,整组在排序,就不会打乱每组数据内部的值
+```
+
+## 8.torch.abs()
+
+### 取所有值的绝对值
+
+```python
+x = torch.tensor([1, -1, -2])
+print(torch.abs(x)) #[1, 1, 2]
+```
+
+## 9.torch.where(condition, x, y)
+
+根据condition,满足条件置为x,不满足条件置为y详解可去参考numpy的,但是注意:torch.nonzero()跟这个不太一样;上面的x,y也要是tensor
+
+## 10.tensor()与Tensor()
+
+```python
+x1=torch.tensor(3) 结果是tensor(3) #数据是整形
+x2 = torch.Tensor(3) 结果是tensor([a,b,c]) #a,b,c是三个随机数,且数据是浮点型
+```
+
+## 11. one_hot编码
+
+### torch.scatter()
+
+```python
+x = torch.zeros(5,5)
+print(x)
+index = torch.randint(5,(5,1))
+print(index)
+x = torch.scatter(x,1,index,20) #(需要修改的tensor;轴;索引(类型为tensor);重新填入的值)
+print(x)
+```
+
+```python
+torch.nn.functional.one_hot()
+```
+
+## 12. unsqueeze 与squee
+
+### 增减轴
+
+```python
+a = torch.randint(10,(5,1,1,5))
+a = torch.unsqueeze(a,dim =1) #unsqueeze,对应轴位置增加一个维度
+
+a = torch.squeeze(a,dim =2) #squeeze,减少对应轴,且该轴值为1,否则不会改变;也可不给轴,那就是默认去掉所有值为1的轴
+```
+
+## 13. numel()
+
+### 获取tensor的个数
+
+ndarray就是直接x.size,注意没括号
+
+```python
+x = torch.randn(3, 4, 5)
+print(x.numel())
+```
+
+## 14.随机种子
+
+随机种子打开后,得到的x是一定的,哪怕是后面关掉随机种子运行几次后,再打开随机种子,得到的数据还是第一次打开随机种子得到的数据
+
+```python
+torch.manual_seed(0)
+x = torch.randn(3, 3)
+```
+
+## 15.clamp()
+
+### 将一组值至于一定范围内
+
+ 黑图片加噪点的时候也可以用这个诶,加完噪点后的图片值可能会超,直接就是torch.clamp(data, 0, 255)
+
+```python
+y = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8])
+print(torch.clamp(y, min = 3, max = 6))
+#将tensor压缩到[min, max]范围内,本就在范围内的不变,小于的置为min,大于的置为max;;可以只给min或max
+```
+
+- 实现原理:
+
+ ```c++
+ float clamp(float value, float min, float max) {
+ return std::max(min, std::min(max, value));
+ }
+ ```
+
+## 16. randperm()
+
+### 返回一组从0到k-1的打乱的tensor
+
+```python
+x = torch.randperm(k) #k只能是一个常数
+print(x)
+```
+
+## 17.torch.topk()
+
+### 求tensor中某维度的前k大或者前k小的**值**以及对应的索引(就是这顺序,先值后索引)
+
+```python
+x = torch.randn(1, 1, 10)
+value, index = torch.topk(x, k=5, dim=-1)
+"""
+k:指明是得到前k个数据以及其index
+dim: 指定在哪个维度上排序, 默认是最后一个维度
+largest:默认为True,按照大到小排序; 如果为False,按照小到大排序
+"""
+```
+
+## 18.torch.multinomial()
+
+### 按照权重进行取值,返回索引
+
+所以在一堆概率值里,概率大的被选中的概率也大
+
+```python
+weights = torch.Tensor([[0, 10, 3, 0]])
+print(torch.multinomial(weights, 2)) # 2就是采样2次
+# 对weights这个数组进行n次采样,采样的权重也是weights,所以结果是[2,1]的概率比[1,2]的概率大很多,多试几次
+# 这种是有放回的采样,每次采样后放回再采样
+```
+
+## 19.torch.gather()
+
+### 通过索引,按照某维度取值
+
+```python
+a = torch.Tensor([[1, 2], [3, 4]])
+index = torch.tensor([[1, 1], [1, 0]]) # 索引必须是tensor
+print(torch.gather(a, dim=1, index)) #[[2., 2.], [4., 3.]] # 这两个形状一定要统一,可以是一维的
+```
+