Simon Shi的小站

人工智能,机器学习, 强化学习,大模型,自动驾驶

0%

[TOC]

相关书籍

入门 C++ Primer Plus
C++ Primer
提高 深度探索C++对象模型
More Effective C++ 35
Effective C++ 55
Effective Modern C++
进阶篇 STL 源码剖析
C++标准库
GOF23 设计模式 C++
C++并发编程实战
C++性能优化指南
网络编程 Linux多线程-服务端编程-
使用muduo c++ 网络库
Unix网络编程 三件套
TCP/UP协议 三卷

APIs

标准库

1.int/float to string/array:

C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点型等)的数字转换为字符串,下面列举了各函数的方法及其说明。
● itoa():将整型值转换为字符串
● ltoa():将长整型值转换为字符串。
● ultoa():将无符号长整型值转换为字符串。
● gcvt():将浮点型数转换为字符串,取四舍五入。
● ecvt():将双精度浮点型值转换为字符串,转换结果中不包含十进制小数点。
● fcvt():指定位数为转换精度,其余同ecvt()。

除此外,还可以使用sprintf系列函数把数字转换成字符串,其比itoa()系列函数运行速度慢

2. string/array to int/float

C/C++语言提供了几个标准库函数,可以将字符串转换为任意类型(整型、长整型、浮点型等)。

● atof():将字符串转换为双精度浮点型值。
● atoi():将字符串转换为整型值。
● atol():将字符串转换为长整型值。
● strtod():将字符串转换为双精度浮点型值,并报告不能被转换的所有剩余数字。
● strtol():将字符串转换为长整值,并报告不能被转换的所有剩余数字。
● strtoul():将字符串转换为无符号长整型值,并报告不能被转换的所有剩余数字。

数据类型&变量&常量

int to string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1.
int a = 10;
char *intStr = itoa(a);
string str = string(intStr);

//2
stringstream ss;
ss << a;
string str = ss.str();

//3 c++11
#include <string>
std::string s = std::to_string(42);

//4 C++ 98
#include <sstream>
#define SSTR(x) static_cost<std::ostringstream &> ((std::ostringstream() << std::dec << x)).str()

//5 Boost
#include <boost/lexical_cast.hpp>
int num = 4;
std::string str = boost::lexical_cast<std::string>(num);

rand

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdlib.h>

int N = rand() % 11; // 0~10的随机数
int N = rand() % 10; // 0~9的随机数
int N = 1 + rand() % 10; // 1-10
a + rand() % n


if1==rand()%10
//10%的概率达成,这里编辑余下代码
else
//90%的概率没达成,这里编辑余下代码

修饰符

unsinge

存储类

  • auto

  • register

  • static 全局变量

  • extern 全局变量的引用

  • mutable

  • thread_local (C++11)

  • const (只读变量)

  • constptr (常量)

    • C++ 11标准中,为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。

    • C++11 constexpr和const的区别详解

从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。

运算符 循环 判断

函数

1
2
3
void func(Cobject* object);
void func(Cobect& object);
void func(Cobject object);

有什么区别呢?

  1. void func(Cobject* object)

    这种方式是将类指针作为参数,函数压入参数时实际上复制了指针的值(其实指针可以看作一个存放地址值的×××),实际复制的指针仍指向原对象的空间,所以func函数对该指针的操作是对原对象空间的操作。

  2. void func(Cobject& object)

    这种方式和传指针类似,但函数压入参数时实际上复制了object对象的this指针,其实this指针不是一个真正存在的指针,可以看作是每个对象的数据空间起始地址。func函数中对this指针的操作,实际上也是对原对象空间的操作

  3. void func(Cobject object)

    这种函数是非常不建议的,因为函数参数压栈时,对object进行了复制(还记得拷贝构造函数吗),所以函数对object的操作实际上是对新的对象空间进行的操作,不会影响原对象空间。由于不必要的拷贝对象是十分浪费时间的,也没有意义,我们完全可以用下面函数代替

    1
    func(const Cobject& object);

    这样也能保护对象的只读性质。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、值传递
实参是变量,表达式等值。
find(int x){}
y= find(z);
上面的例子中,z是实参,x是形参。x变z不变。
在值传递过程中,实参和形参位于内存中两个不同地址中,实参先自己复制一次拷贝,再把拷贝复制给形参。所以,在值传递过程中,形参的变化不会对实参有任何的影响。

2、地址传递(也称引用传递)

实参是指针。
在函数调用的时候,实参传递给你的是指针地址,地址一样也就意味着实参和形参是一样的,当你的形参发生改变时,实参也会发生改变。

find(int &x){}
y= find(z);
上面的例子中,z是实参,x是形参。z随x而改变。

数字运算

cos, sin tan, log, pow,

hypot, sqrt, abs, fabs, floor

数组

[C++]指针数组以及数组初始化

1
int *pia = new int[10]; // array of 10 uninitialized ints

枚举enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//    定义格式:枚举类型的定义格式为:
enum <类型名> {<枚举常量表>};
enum color_set1 {RED, BLUE, WHITE, BLACK}; //定义枚举类型color_set1
enum week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; //定义枚举类型week


各枚举常量的值可以重复。例如:
enum fruit_set {apple, orange, banana=1, peach, grape}
//枚举常量apple=0,orange=1, banana=1,peach=2,grape=3。
enum week {Sun=7, Mon=1, Tue, Wed, Thu, Fri, Sat};
//枚举常量Sun,Mon,Tue,Wed,Thu,Fri,Sat的值分别为7、1、2、3、4、5、6。
枚举常量只能以标识符形式表示,而不能是整型、字符型等文字常量。例如,以下定义非法:
enum letter_set {'a','d','F','s','T'}; //枚举常量不能是字符常量
enum year_set{2000,2001,2002,2003,2004,2005}; //枚举常量不能是整型常量
可改为以下形式则定义合法:
enum letter_set {a, d, F, s, T};
enum year_set{y2000, y2001, y2002, y2003, y2004, y2005};

字符串String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
## string -> char*
#### data()方法,如:
1 string str = "hello";
2 const char* p = str.data();//加const 或者用char * p=(char*)str.data();的形式

#### c_str()方法,如:
1 string str=“world”;
2 const char *p = str.c_str();//同上,要加const或者等号右边用char*

#### copy()方法,如:
1 string str="hmmm";
2 char p[50];
3 str.copy(p, 5, 0);//这里5代表复制几个字符,0代表复制的位置,
4 *(p+5)=‘\0’;//注意手动加结束符!!!


## 二、char* -> string
####
1 string s;
2 char *p = "hello";//直接赋值
3 s = p;

## 三、stringchar[]
1 string pp = "dagah";
2 char p[8];
3 int i;
4 for( i=0;i<pp.length();i++)
5 p[i] = pp[i];
6 p[i] = '\0';
7 printf("%s\n",p);
8 cout<<p;

string 赋值

1
2
3
4
5
6
7
8
9
10
// 方法1 (ostringstream)
#include <sstream>
ostringstream buffer;
buffer << "sss," << 2015 << "_"<< 1;
string str = buffer.str();

// char --(sprintf)--> string
char buffer[BUFFER_SIZE];
sprintf(buffer,"%s_%d_%d","cplusplus",2015,1);
string str = buffer;

string.substr(起点, length)

1
2
// string对象.substr(起点 , 切片长度)
operate_str = initial_str.substr(0, 3);

string.erase()

1
2
3
4
5
6
1.erase(pos,n);
删除从下标pos开始的n个字符,比如erase(0,1)就是删除第一个字符
2.erase(position);
删除postion处的一个字符(position是一个string类型的迭代器)
3.erase(first,last)
删除从first到last之间的字符(first和last都是迭代器)

string 对比

1
2
3
4
5
6
7
std::string h2 = "BUS_0001";
std::string h3 = "BUS_0001";
printf(" h3 == h2: %d\n",h3 == h2);

if(h3 == h2)

h3.compare(h2) == 0 // 相等

string 高级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 1. 逆序
string orderstr = "hello world";
cout << "正序字符串:" << orderstr << endl;
reverse(orderstr.begin(), orderstr.end());
cout << "逆序字符串:" << orderstr << endl;


## 2.将一个字符串按照空格划分成单词
string str = "hello world, how are you?";
stringstream ss(str);
string word;
while (ss >> word) {
cout << word << endl;
}

指针

引用

memcpy…apis

memset(void *str, int c, size_t n)

复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

memcpy(void *destin, void *source, unsigned n);

从存储区 source复制n 个字节到存储区 destin。

将地址从src开始的前count个字节的内容拷贝到地址从dst开始的内存空间中。

文件读取I/O

C++文件读写详解(ofstream,ifstream,fstream)

img

1、基于控制台io

1
2
3
4
#include <iostream>
istream // 流读取 (input)
ostream // 写入流 (output)
iostram // 对流读写

2、基于文件IO [ifstream/ofstream]

1
2
3
4
5
6
#include <fstream>
ifstream //文件读操作,存储设备读区到内存中
ofstream //文件写操作 内存写入存储设备
fstream //读写操作,对打开的文件可进行读写操作

// 当 fstream/ifstream/ofstream 对象超出范围时,文件将自动关闭。

ref:

https://www.toptip.ca/2012/11/is-it-necessary-to-manually-close.html

3、基于字符串IO [i/ostringstream]

1
2
3
4
#include <sstream>
istringstream // 从string对象读取
ostringstream // 写入到string对象
stringstream // 对string对象读写

ref:

c++的输入输出 [一](istream,ostream,iostream)

c++的输入输出 [二](ifstream,ofstream,fstream)

c++的输入输出 [三](istringstream,ostringstream,stringstream)

C++文件读写详解(ofstream,ifstream,fstream)

c++多进程读取同一个文件

C Plus 第 6.1 节 带文件的输入/输出

c++多线程:互斥锁

c++输入文件流ifstream用法详解_陈 洪 伟的博客-CSDN博客_ifstream用法

时间

1、时间戳毫秒/秒

gettimeofday()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// linux
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
struct timeval time;

/* 获取时间s */
gettimeofday(&time, NULL);
//printf("s: %ld, ms: %ld\n", time.tv_sec, (time.tv_sec*1000 + time.tv_usec/1000));

/* 获取时间us, s */
struct timeval time_now{};
gettimeofday(&time_now, nullptr);
time_t msecs_time = (time_now.tv_sec * 1000) + (time_now.tv_usec / 1000);

cout << "seconds since epoch( s): " << time_now.tv_sec << endl;
cout << "milliseconds since epoch(ms): " << msecs_time << endl << endl;
}
chrono
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <chrono>
#include <iostream>
#include <sys/time.h>
#include <ctime>

using std::cout; using std::endl;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::seconds;
using std::chrono::system_clock;

int main() {
auto millisec_since_epoch = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
auto sec_since_epoch = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();

cout << "seconds since epoch: " << sec_since_epoch << endl;
cout << "milliseconds since epoch: " << millisec_since_epoch << endl;


auto tp = std::chrono::system_clock::now();

auto seconds = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
cout << seconds.count() << " s" << endl; // seconds from 1970 1651286747

return EXIT_SUCCESS;
}

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <cstdint>
#include <ctime>
#include <iostream>
#include <chrono>
#include <sstream>
#include <iomanip>

using namespace std;

void time_point_to_duration();
void duration_to_time_point();
void format_time_point();
void parse_from_string();

int main(int argc, char *argv[])
{
time_point_to_duration();

duration_to_time_point();

format_time_point();

parse_from_string();

return 0;
}

void time_point_to_duration()
{
auto tp=std::chrono::system_clock::now();

auto seconds=std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
cout<<seconds.count()<<" s"<<endl;//seconds from 1970

auto ms=std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
cout<<ms.count()<<" ms"<<endl;

auto us=std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch());
cout<<us.count()<<" us"<<endl;

auto ns=std::chrono::duration_cast<std::chrono::nanoseconds>(tp.time_since_epoch());
cout<<ns.count()<<" ns"<<endl;

cout<<tp.time_since_epoch().count()<<" default is ns"<<endl;
}

void duration_to_time_point()
{
std::uint64_t ticker=1609756793160376465;
auto ns=std::chrono::nanoseconds(ticker);

auto tp1=std::chrono::time_point<std::chrono::system_clock,std::chrono::nanoseconds>(ns);
auto tp2=tp1-std::chrono::hours(1);//time point before one hour
cout<<"tp1="<<tp1.time_since_epoch().count()<<endl<<"tp2="<<tp2.time_since_epoch().count()<<endl;
}

void format_time_point()
{
auto tp=std::chrono::system_clock::now();
auto time=std::chrono::system_clock::to_time_t(tp);
std::stringstream ss;
ss<<std::put_time(std::localtime(&time),"%Y-%m-%d %H:%M:%S");

cout<<ss.str()<<endl;
}

void parse_from_string()
{
std::stringstream ss;
ss<<"2021-01-06 18:47:35";

std::tm tm{};
ss>>std::get_time(&tm,"%Y-%m-%d %H:%M:%S");
auto tp=std::chrono::system_clock::from_time_t(std::mktime(&tm));
cout<<tp.time_since_epoch().count()<<endl;
}

//原文链接:https://blog.csdn.net/lianshaohua/article/details/112531154
掩耳盗铃
1
2
3
4
5
time_t now = time(nullptr);
time_t mnow = now * 1000;

cout << "seconds since epoch: " << now << endl;
cout << "milliseconds since epoch: " << mnow << endl << endl;

Windows/Linux Get Millisecond

get time in millisecond

2、时间戳(s)

1
2
3
4
5
6
7
// time for int s
time_t now = time(0);
// time to string format
tm *ltm = localtime(&now);
char s[100]{};
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", ltm);
string timestr(s);
1
2
3
4
std::time_t t = std::time(NULL);
std::tm tm = *std::localtime(&t);
std::cout.imbue(std::locale("CHS"));
std::cout << std::put_time(&tm,"%Y-%m-%d %H:%M:%S") << '\n';

3、localtime(time_t)

C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。

4、strftime()

size_t strftime(char *str, size_t count, const char *format, const struct tm *tm);

1
2
3
4
str, 表示返回的时间字符串
count, 要写入的字节的最大数量
format, 格式字符串由零个或多个转换符和普通字符(除%)
tm, 输入时间

基本的输入输出

数据结构

APIs C++ 11

auto

C++11这次的更新带来了令很多C++程序员期待已久的for range循环,每次看到javascript, lua里的for range,心想要是C++能有多好,心里别提多酸了。这次C++11不负众望,再也不用羡慕别家人的for range了。

字符串 & 数组

1
2
3
4
std::string str = “hello, world”;  
for(auto ch : str) {
std::cout << ch << std::endl;
}
1
2
3
4
int arr[] = {1, 2, 3, 4};  
for(auto i : arr) {
std::cout<< i << std::endl;
}

stl 容器

1
2
3
4
std::vector<std::string> str_vec = {“i”, “like”,  "google”};  
for(auto& it : str_vec) {
it = “c++”;
}

遍历stl map

1
2
3
4
std::map<int, std::string> hash_map = {{1, “c++”}, {2, “java”}, {3, “python”}};  
for(auto it : hash_map) {
std::cout << it.first << “\t” << it.second << std::endl;
}

算法

迭代器

对象

对象创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//隐式创建
Person p1;
cout<<endl;
Person p2(18, "lili");
cout<<endl;

//显式创建
Person p3 = Person();
cout<<endl;
Person p4 = Person(16, "xx");
cout<<endl;

// new 创建
Person *p5 = new Person();
cout<<endl;
Person *p6 = new Person(14, "yy");

VS 快捷键

1
2
多行注释:Ctrl+K+C(先按ctrl,再按K,最后按C)
取消多行注释:Ctrl+K+U(先按ctrl,再按K,最后按U)

Install by snap (Ubuntu)

anbox-kernel

1
2
3
4
5
6
7
$ sudo add-apt-repository ppa:morphis/anbox-support
$ sudo apt update
$ sudo apt install linux-headers-generic anbox-modules-dkms


$ sudo modprobe ashmem_linux
$ sudo modprobe binder_linux

anbox

1
$ sudo apt install anbox

链接:https://zhuanlan.zhihu.com/p/65463076

Using

Docker 点击anbox图标

adb安装

Ubuntu/Debain

1
sudo apt install android-tools-adb

adb使用:

image-20200928092932708

image-20200928093448259

adb logcat:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//格式1:打印默认日志数据
adb logcat

//格式2:需要打印日志详细时间的简单数据
adb logcat -v time

//格式3:需要打印级别为Error的信息
adb logcat *:E

//格式4:需要打印时间和级别是Error的信息
adb logcat -v time *:E

//格式5:将日志保存到电脑固定的位置,比如D:\log.txt
adb logcat -v time >D:\log.txt

python 备忘录

[TOC]

简单API

FUN: 重命名目录下文件

  • filter
  • lambda
1
2
3
4
5
6
def format_filename(dir):
fs = os.listdir(dir)
for f in fs:
new_n = ''.join(filter(lambda ch: ch not in '# (){}#', f))
print(f, ' ----> ', new_n)
os.rename(dir+f, dir+new_n)

traceback

1
2
3
4
5
6
def log_error(e):
print('err:', e)
print('err:', request.form)
print('err file:', e.__traceback__.tb_frame.f_globals["__file__"]) # 发生异常所在的文件
print('err file line:', e.__traceback__.tb_lineno) # 发生异常所在的行数
print('err ', traceback.print_exc())

Time

1
2
3
4
5
6
>>> time.ctime()
'Thu May 5 14:58:09 2011'
>>> time.ctime(time.time())
'Thu May 5 14:58:39 2011'
>>> time.ctime(1304579615)
'Thu May 5 15:13:35 2011'

os

1
os.mkdir()

os.path

os.path.basename()

os.path.dirname()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
path = '/home/User/Documents'
dirname = os.path.dirname(path)
print(dirname)


path = '/home/User/Documents/file.txt'
dirname = os.path.dirname(path)
print(dirname)


path = 'file.txt'
dirname = os.path.dirname(path)
print(dirname)

# 三个的结果:
>>>/home/User
>>>/home/User/Documents
>>>

expanduser

python的os.path 模块提供了一个expanduser函数,它可以将参数中开头部分的 ~ 或 ~user 替换为当前用户的home目录并返回,仅看定义难以理解,我在linux系统和winodws系统下分别实验它的功能。

在linux系统下,我的账号是kwsy,这个用户的home目录是/home/kwsy,下面的代码演示如何使用expanduser函数。

1
2
3
4
5
6
Python 3.7.0 (default, Jun 28 2018, 13:15:42)
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.path.expanduser('~/.config/')
'/home/kwsy/.config/'

expandvars

Python中的方法用于扩展给定路径中的环境变量。它将给定路径中形式为$name$${name}$的子字符串替换为环境变量name的值。

1
todo

random

1
random.randint(0, N) -> one of [0,1, ...,N]

str

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
text = 'hello world'

# 1. in
if 'w' in text:
print('find w in text')

if 'hello' in text:
print('find')

if 'hello' not in text:
print(' not find ')


# 2.find func
if text.find('xxx') != -1:
print('find position=', text.find('xxx'))

# 3 index func
try:
position = text.index('not exits char')
except ValueError:
print('not find ')

dict_2_obj

1、手动映射字段

1
2
3
4
5
6
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

person = Person(data['name'], data['age'])

2、自动映射object_hook

使用 json.loads() 的 object_hook 参数,自动将字典转换为对象‌

1
2
3
4
5
6
7
class JsonObject:
def __init__(self, d):
self.__dict__ = d

json_str = '{"name": "John", "age": 30}'
obj = json.loads(json_str, object_hook=JsonObject)
print(obj.name) # 输出:John

3、动态属性拓展json to object

支持动态解析复杂JSON结构(如嵌套字典或列表)‌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import json
class obj(object):
def __init__(self, dict_):
self.__dict__.update(dict_)

def dict2obj(d):
return json.loads(json.dumps(d), object_hook=obj)

d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'cjavapy'}]}
# 使用1
o = dict2obj(d)
print(o)
print(o.a)
print(o.b)
print(o.d)
print(o.d[1].foo)

进阶1 自定义+递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class obj(object):
def __init__(self, d):
for a, b in d.items():
if isinstance(b, (list, tuple)):
setattr(self, a, [obj(x) if isinstance(x, dict) else x for x in b])
else:
setattr(self, a, obj(b) if isinstance(b, dict) else b)
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "cjavapy"}]}
o = obj(d)
print(o)
print(o.a)
print(o.b)
print(o.d)
print(o.d[1].foo)

json

1
2
3
4
5
6
7
8
9
10
11
12
13
import json
class obj(object):
def __init__(self, dict_):
self.__dict__.update(dict_)
def dict2obj(d):
return json.loads(json.dumps(d), object_hook=obj)
d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'cjavapy'}]}
o = dict2obj(d)
print(o)
print(o.a)
print(o.b)
print(o.d)
print(o.d[1].foo)

进阶2 namedtuple + 递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from collections import namedtuple
class struct(object):
def __new__(cls, data):
if isinstance(data, dict):
return namedtuple(
'struct', data.keys()
)(
*(struct(val) for val in data.values())
)
elif isinstance(data, (tuple, list, set, frozenset)):
return type(data)(struct(_) for _ in data)
else:
return data
d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'cjavapy'}]}
o = struct(d)
print(o)
print(o.a)
print(o.b)
print(o.d)
print(o.d[1].foo)

进阶3 自定义编码、解码器

通过继承 json.JSONEncoder 和 json.JSONDecoder 处理特殊数据类型(如日期、枚举)

1
2
3
4
5
6
7
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)

json_str = json.dumps(obj, cls=CustomEncoder)

进阶4 复杂递归

1
2
3
4
5
6
7
8
9
10
11
class NestedObject:
def __init__(self, d):
for key, value in d.items():
if isinstance(value, dict):
setattr(self, key, NestedObject(value))
else:
setattr(self, key, value)

json_str = '{"books": {"语文": "济南的冬天"}}'
obj = json.loads(json_str, object_hook=NestedObject)
print(obj.books.语文) # 输出:济南的冬天

数据文件读写

json

1
2
3
4
5
6
7
json.dumps()
json.loads()
import json

# json.dumps()函数的使用,将字典转化为字符串
dict1 = {"age": "12"}
json_info = json.dumps(dict1)
1
2
3
4
5
6
7
# Writing JSON data
with open('data.json', 'w') as f:
json.dump(data, f)

# Reading data back
with open('data.json', 'r') as f:
data = json.load(f)

np

1
2
3
4
import numpy as np

a=np.array(a)
np.save(‘a.npy’,a) # 保存为.npy格式
1
2
a=np.load(‘a.npy’)
a=a.tolist()

csv

1
2
3
4
import pandas as pd

readme = pd.read_csv(‘读我.txt’,sep=’:’,encoding=“utf-8”, engine=‘python’,header=None)
readme = np.array(readme)

高级API

组合函数 itertools.combinations()元素不可重复

1
2
3
itertools.combinations(iters,n)元组输出迭代器,需List显示,全组合直接append分三个元组。
combinations('ABCD', 2) --> AB AC AD BC BD CD
combinations(range(4), 3) --> 012 013 023 123

ref:组合blog

combinations

1
2
3
4
5
6
7
import itertools 
combinations2 = itertools.combinations(iterable, r)
for combo in combinations2:
print(combo)
(1, 2)
(1, 3)
(2, 3)

combinations_with_replacement() 元素可重复的组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import itertools  

# 定义一个可迭代对象
iterable = [1, 2, 3]

# 设置组合长度为2
r = 2

# 生成可重复组合的迭代器
combinations = itertools.combinations_with_replacement(iterable, r)

# 打印每个组合
for combo in combinations:
print(combo)

out

1
2
3
4
5
6
(1, 1)  
(1, 2)
(1, 3)
(2, 2)
(2, 3)
(3, 3)

@property的理解和使用

1
2
3
4
5
6
7
@property
def password(self):
raise AttributeError('password is not a readable attribute')

@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
通常赋值函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student(object):

def get_score(self):
return self._score

def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value


>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
@property
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
但是为了方便,节省时间,我们不想写s.set_score(9999)啊,直接写s.score = 9999不是更快么,加了方法做限制不能让调用的时候变麻烦啊,@property快来帮忙….

class Student(object):

@property
def score(self):
return self._score

@score.setter
def score(self,value):
if not isinstance(value, int):
raise ValueError('分数必须是整数才行呐')
if value < 0 or value > 100:
raise ValueError('分数必须0-100之间')
self._score = value

看上面代码可知,把get方法变为属性只需要加上@property装饰器即可,此时@property本身又会创建另外一个装饰器@score.setter,负责把set方法变成给属性赋值,这么做完后,我们调用起来既可控又方便

>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!

MP

1
2
3
4
5
6
对于一般进程间共享数据来说,使用
multiprocessing.Manager().Value
和multiprocessing.Manager().list()
和multiprocessing.Manager().dict()即可。
Or:
Queue()

pyLint

1
2
3
4
5
6
7
8
9
10
11
12
# Install
pip install pylint
pylint --version

#使用,生成默认配置文件:
pylint --persistent=n --generate-rcfile > .pylintrc

# 检查单个文件:
pylint [options] m1.py

# 整个项目
pylint [options] project_path

re

去除中文标点符号

s = re.sub(r'[.,"\'-?:!;]', '', s)

去除中文

temp = re.sub('[\u4e00-\u9fa5]','',text)

去除英文

temp = re.sub('[a-zA-Z]','',text)

del数字

temp = re.sub('[\d]','',text) # [0-9]

del 空格

temp = re.sub('[\s]','',text) #temp = text.strip()

RUN

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1 normal run
python -u main.py


# 2 subdir run
python demo/main.py
--修改main文件
import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))


# 3 -m 运行
python -m model.main

image inpainting图像修复

传统的图形学和视觉的研究方法,主要还是基于数学和物理的方法。然而随着近几年深度学习在视觉领域取得的卓越的效果,视觉领域研究的前沿已经基本被深度学习占领。在这样的形势之下,越来越多的图形学研究者也开始将目光投向深度学习。在图形学和视觉交叉的领域,一系列问题的研究正在围绕深度学习火热展开,特别是在图像编辑(image editing)和图像生成(image generation)方面,已经初见成效。今天我们讨论的问题,图像补全(image inpainting),正是介于图像编辑和图像生成之间的一个问题。

图像补全最初是一个传统图形学的问题。问题本身很直观:在一幅图像上挖一个洞,如何利用其它的信息将这个洞补全,并且让人眼无法辨别出补全的部分。这个问题对我们人类似乎很容易,比如下面这个洞,大家很容易脑补出洞里应该有窗户和门,背景是墙,如果还有一些绘画天赋的话,大概就能想象着把它补出来。但是这个任务对于计算机却显得格外困难,首先这个问题没有唯一确定的解,其次如何利用其它的信息?如何判断补全结果是否足够真实?

以深度学习为代表的机器学习,正在逐渐席卷整个图形学研究领域。研究者们逐渐发现,当传统的基于物理的模型发展遇到瓶颈的时候,机器学习的方法也许能够帮助我们解释这些复杂的数理模型。毕竟只有理解了图像的深层结构,才能更好地指导图像的生成和处理。

[toc]

论文推荐–CV


1. [CVPR 2016] Context-Encoders

(CNN+GAN, *鼻祖级的* NN修复方法)

链接: Feature Learning by Inpainting;

Github代码:

pathak22/context-encodergithub.com

image-20201013112431560

2. [CVPR 2017]High Resolution Inpainting

(Context-Encoders+CNNMRF)

链接: High-Resolution Image Inpainting using Multi-Scale Neural Patch Synthesis;

Github代码:

leehomyc/Faster-High-Res-Neural-Inpaintinggithub.com图标

3. [CVPR 2017] Semantic Image Inpainting

code

Semantic Image Inpainting with Perceptual and Contextual Losses

4. [ICCV 2017] On demanding learning

(感觉也是Context-Encoders的衍生版…)

链接:On-Demand Learning for Deep Image Restoration

Github代码: rhgao/on-demand-learninggithub.com

5. [SIGGRAPH 2017] (ACM ToG) Global and locally …

Globally and Locally Consistent Image Completion

(CE中加入Global+Local两个判别器的改进),

Github代码:

1)https://github.com/satoshiiizuka/siggraph2017_inpainting

2)https://github.com/shinseung428/GlobalLocalImageCompletion_TF

其中第二个实现稍微不同于原论文。但是展示效果非常棒。

img

6. [2017.12 ] Deep image prior [机构]

项目主页:deep_image_prior

适用场景:
1)难以建模图像退化过程
2)难以得到训练图像进行监督训练

7. [ICLR 2018] Nvidia

New AI Imaging Technique Reconstructs Photos with Realistic Results

Image Inpainting for Irregular Holes UsingPartial Convolutions

号称秒杀PS的AI图像修复神器,来自于Nvidia 研究团队。引入了局部卷积,能够修复任意非中心、不规则区域),代码还没有放出来

[1804.07723] Image Inpainting for Irregular Holes Using Partial Convolutionsarxiv.org

8. [CVPR 2018] Deepfill

的**Generative Image Inpainting with Contextual Attention**,

一作大佬**jiahui Yu** 后续还有个工作: Deepfill v2: Free-Form Image Inpainting with Gated Convolution,

Github代码:

JiahuiYu/generative_inpainting

9. [2018] Shift-Net

哈工大左旺孟老师他们也有一篇**Shift-Net: Image Inpainting via Deep Feature Rearrangement**

效果也不错,代码还没有放

https://github.com/Zhaoyi-Yan/Shift-Net

image-20200903155833399

10. [ECCV 2018] Contextual-base ….

ECCV 2018的**Contextual-based Image Inpaintinginpainting大佬Chao Yang(NPS的一作)**等人的又一力作:

Contextual-based Image Inpainting arxiv.org

11. [NIPS 2018] inpainting_gmcnn

Image Inpainting via Generative Multi-column Convolutional Neural Networks,用了不少trick,

Github代码:

shepnerd/inpainting_gmcnn

12. [ACM MM 2018] PGN

Semantic Image Inpainting with Progressive Generative Networks简称PGN,采用了由外至内的步进式修补策略,Github代码:

crashmoon/Progressive-Generative-Networks

13. [ArXiv 2019] EdgeConnect

使用对抗边缘学习进行生成图像修复

论文链接:ArXiv | BibTex

项目地址:https://github.com/knazeri/edge-connect#testing

EdgeConnect,一种基于边缘补全的图像修复新方法,这篇文章将图像修复的工作分成了两个部分,首先利用利用启发式的生成模型得到了缺失部分的边缘信息,随后将边缘信息作为图像缺失的先验部分和图像一起送入修复网络进行图像重建。(from 安大略技术大学)

具体来说,作者们提出了一个二阶段生成对抗网络 EdgeConnect,它包括一个边缘生成器,然后是一个图像补全网络。边缘生成器在图像的缺失区域(规则和不规则)生成预测边缘,然后图像补全网络使用预测边缘作为先验填充缺失区域。研究者通过公开可用的数据集 CelebA、Places2 和 Paris StreetView 对模型进行端到端评估,并表明它在数量和质量上优于当前最先进的技术。

详见这里

img

14. [IJCAI 2019] MUSICAL

的**MUSICAL: Multi-Scale Image Contextual Attention Learning for Inpainting,武汉大学杜博老师组的工作(注:第一作者为我校计院的一名本科生**…广大CV狗瑟瑟发抖!)。引入一个多尺度的上下文注意力模块,避免信息滥用/误用导致的纹理模糊等问题,损失函数部分联合了风格损失、感知损失、对抗损失,来保证补绘内容的一致性和清晰水平。

武汉大学地学智能感知与机器学习研究组

15. [CVPR 2019] Foreground-aware Image Inpainting

Foreground-aware Image Inpainting, 思路类似于上面的工作,也是先推断生成轮廓边缘,辅助缺失区域进行修复,不知道上面的哥们看了这篇会是什么感受…速度也很重要啊…

ARXIV-Foreground-aware Image Inpainting

16. [CVPR 2019] Pluralistic Image Completion

Pluralistic Image Completion

论文与Github代码:https://arxiv.org/abs/1903.04227

lyndonzheng/Pluralistic-Inpainting

17. ICCV 2019 CSA-inpainting

的**Coherent Semantic Attention for Image Inpainting,论文作者为@Kuma , 文中提出了一个全新的Attention模块**,该模块不仅有效的利用了上下文信息同时能够捕捉到生成补丁之间的相关性。同时提出了一个新的损失函数配合模块的工作,最后利用一个新的特征感知辨别器对细节效果进行加强,代码过段时间会公开

作者主页: KumapowerLIU - Overview

Paper:《Coherent Semantic Attention for Image Inpainting》

Code:github ICCV 2019 CSA impainting

效果对比:

image-20200903170835494

软件架构:

file

18. [ECCV-2020] RethingImpainting

《Rethinking Image Inpainting via a Mutual Encoder Decoder with Feature Equalizations》(ECCV2020 Oral)

Rethinking-Inpainting

19. [1807] GAN-based RGB-D inpainting

image-20200903165822523

综述论文

《 Image inpainting: A review》

https://arxiv.org/abs/1909.06399

参考链接:

1.https://www.zhihu.com/question/56801298

2.https://blog.csdn.net/muyiyushan/article/details/79093806

[TOC]

高分辨率

  • 单张照片
  • 多张照片

SISR 高清图片

SRCNN [ECCV2014]

开山之作,三个卷积层,输入图像是低分辨率图像经过双三次(bicubic)插值和高分辨率一个尺寸后输入CNN。

图像块的提取和特征表示,特征非线性映射和最终的重建。使用均方误差(MSE)作为损失函数。

img

code: http://mmlab.ie.cuhk.edu.hk/projects/SRCNN.html

SCN ICCV 2015

FSRCNN [ECCV2016]

特征提取:低分辨率图像,选取的核9×9设置为5×5。

**收缩:**1×1的卷积核进行降维。

**非线性映射:**用两个串联的3×3的卷积核可以替代一个5×5的卷积核。

扩张:1×1的卷积核进行扩维。

反卷积层:卷积层的逆操作,如果步长为n,那么尺寸放大n倍,实现了上采样的操作。

FSRCNN与SRCNN都是香港中文大学Dong Chao, Xiaoou Tang等人的工作。FSRCNN是对之前SRCNN的改进。

主要在三个方面:

​ 一是在最后使用了一个反卷积层放大尺寸,因此可以直接将原始的低分辨率图像输入到网络中,而不是像之前SRCNN那样需要先通过bicubic方法放大尺寸。

​ 二是改变特征维数,使用更小的卷积核和使用更多的映射层。

​ 三是可以共享其中的映射层,如果需要训练不同上采样倍率的模型,只需要fine-tuning最后的反卷积层。

相对于SRCNN:

在最后使用了一个反卷积层放大尺寸,因此可以直接将原始的低分辨率图像输入到网络中;改变特征维数,使用更小的卷积核和使用更多的映射层;可以共享其中的映射层,如果需要训练不同上采样倍率的模型,只需要fine-tuning最后的反卷积层。

img

https://blog.csdn.net/sinat_39372048/article/details/81628945

code: http://mmlab.ie.cuhk.edu.hk/projects/FSRCNN.html

ESPCN [CVPR2016]

核心概念是亚像素卷积层,输入原始低分辨率图像,三个卷积层,将 $H \times W \times r^2$ 的特征图像被重新排列成 $rH \times rW \times 1$的高分辨率图像。

ESPCN激活函数采用tanh替代了ReLU。损失函数为均方误差。

img

img

github(tensorflow): https://github.com/drakelevy/ESPCN-TensorFlow

github(pytorch): https://github.com/leftthomas/ESPCN

github(caffe): https://github.com/wangxuewen99/Super-Resolution/tree/master/ESPCN

VDSR–7 [CVPR2016]

该论文利用残差学习,加深网络结构(20层),在图像的超分辨率上取得了较好的效果。

只学习高分辨率图像和低分辨率图像之间的高频部分残差即可——残差网络

输入低分辨率图像插值后的图像,再将这个图像与网络学到的残差相加得到最终的网络的输出。

特点:

1.加深了网络结构(20层),:更深的网络结构使得后面的网络层拥有更大的感受野,该文章采取3X3的卷积核,从而使得深度为D的网络,拥有(2D+1)X(2D+1)的感受野,从而可以根据更多的像素点去推断结果像素点

2.采用残差学习(自适应梯度裁剪将梯度限制在某一范围)。

3.卷积补0操作,保证特征图和最终的输出图像在尺寸上都保持一致。

4.多尺度图像共同训练

img

code: https://cv.snu.ac.kr/research/VDSR/

github(caffe): https://github.com/huangzehao/caffe-vdsr

github(tensorflow): https://github.com/Jongchan/tensorflow-vdsr

github(pytorch): https://github.com/twtygqyy/pytorch-vdsr

https://blog.csdn.net/wangkun1340378/article/details/74231352

DRCN [CVPR 2016]

递归神经网络结构

输入的是插值后的图像,分为三个模块,第一个是Embedding network,相当于特征提取,第二个是Inference network, 相当于特征的非线性映射,第三个是Reconstruction network,即从特征图像恢复最后的重建结果。其中的Inference network是一个递归网络,即数据循环地通过该层多次。将这个循环进行展开,等效于使用同一组参数的多个串联的卷积层.

img

img

​ 在递归次数比较多的时候,这种基本的递归形式的网络模型会有两个比较典型的问题,一是很容易出现梯度消失或者梯度爆炸的现象,导致基本上无法训练;二是类似于RNN,输出与输入之间有着较长的依赖关系,这种依赖关系导致模型很难获得输入图片中较好的细节。

img 鉴于此,作者提出了递归监督的方法,来使得递归层中间的输出经过重建之后也可以加上监督的信息;然后采用残差学习的方法来解决输入与输出之间的这种长期依赖的关系,来使得输出获得较好的细节。

code: https://cv.snu.ac.kr/research/DRCN/

githug(tensorflow): https://github.com/jiny2001/deeply-recursive-cnn-tf

https://zhuanlan.zhihu.com/p/76868378

RED [NIPS2016]

对称的卷积层-反卷积层构成的网络结构

RED网络的结构是对称的,每个卷积层都有对应的反卷积层。卷积层用来获取图像的抽象内容,反卷积层用来放大特征尺寸并且恢复图像细节。

用到了与VDSR相同的Idea:网络中有一条线是将输入的图像连接到后面与最后的一层反卷积层的输出相加。

RED中间的卷积层和反卷积层学习的特征是目标图像和低质图像之间的残差。RED的网络深度为30层,损失函数用的均方误差。

img

VDSR [CVPR. (2016)]

DRRN [CVPR. (2017)]

LapSRN [CVPR. (2017)]

MSLapSRN (2017)

ENet-PAT [ICCV. (2017)]

MemNet [CVPRW 2017]

DRRN [CVPR2017]:

DRRN的作者应该是受到了(VDSR, DRCN,残差网络)启发,采用了更深的网络结构来获取性能的提升。作者也在文中用图片示例比较了DRRN与上述三个网络的区别,比较示例图如下所示。

img

ResNet是链模式的局部残差学习。VDSR是全局残差学习。DRCN是全局残差学习+单权重的递归学习+多目标优化。DRRN是多路径模式的局部残差学习+全局残差学习+多权重的递归学习。

选用的是1个递归块和25个残差单元,深度为52层的网络结构

github(caffe): https://github.com/tyshiwo/DRRN_CVPR17

LapSRN [CVPR2017]

改进前面大部分算法

论文中作者先总结了之前的方法存在有三点问题

​ 一是有的方法在输入图像进网络前,需要使用预先定义好的上采样操作(例如bicubic)来获得目标的空间尺寸,这样的操作增加了额外的计算开销,同时也会导致可见的重建伪影。而有的方法使用了亚像素卷积层或者反卷积层这样的操作来替换预先定义好的上采样操作,这些方法的网络结构又相对比较简单,性能较差,并不能学好低分辨率图像到高分辨率图像复杂的映射。

​ 二是在训练网络时使用 l2 型损失函数时,不可避免地会产生模糊的预测,恢复出的高分辨率图片往往会太过于平滑。

​ 三是在重建高分辨率图像时,如果只用一次上采样的操作,在获得大倍数**(8倍以上)的上采样因子**时就会比较困难。

LapSRN通过逐步上采样,一级一级预测残差的方式,在做高倍上采样时,也能得到中间低倍上采样结果的输出。由于尺寸是逐步放大,不是所有的操作都在大尺寸特征上进行,因此速度比较快。LapSRN设计了损失函数来训练网络,对每一级的结果都进行监督,因此取得了不错的结果。

img

github(matconvnet): https://github.com/phoenix104104/LapSRN

github(pytorch): https://github.com/twtygqyy/pytorch-LapSRN

github(tensorflow): https://github.com/zjuela/LapSRN-tensorflow

SRDenseNet [CVPR 2017]

SRDenseNet将稠密块结构应用到了超分辨率问题上,这样的结构给整个网络带来了减轻梯度消失问题、加强特征传播、支持特征复用、减少参数数量的优点

DenseNet: 结构给整个网络带来了减轻梯度消失问题、加强特征传播、支持特征复用、减少参数数量的优点。

img

SRGAN(SRResNet) [CVPR2017]

在这篇文章中,将生成对抗网络(Generative Adversarial Network, GAN)用在了解决超分辨率问题上

《Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network》

用均方误差优化SRResNet(SRGAN的生成网络部分),文章中的实验结果表明,用基于均方误差的损失函数训练的SRResNet,得到了结果具有很高的峰值信噪比,但是会丢失一些高频部分细节,图像比较平滑。而SRGAN得到的结果则有更好的视觉效果。

SRGAN利用感知损失(perceptual loss)和对抗损失(adversarial loss)来提升恢复出的图片的真实感。

其中,又对内容损失分别设置成基于均方误差、基于VGG模型(损失函数)低层特征和基于VGG模型高层特征三种情况作了比较,在基于均方误差的时候表现最差,基于VGG模型高层特征比基于VGG模型低层特征的内容损失能生成更好的纹理细节。

img

​ 在生成网络部分(SRResNet)部分包含多个残差块,每个残差块中包含两个3×3的卷积层,卷积层后接批规范化层(batch normalization, BN)和PReLU作为激活函数,两个2×亚像素卷积层(sub-pixel convolution layers)被用来增大特征尺寸。在判别网络部分包含8个卷积层,随着网络层数加深,特征个数不断增加,特征尺寸不断减小,选取激活函数为LeakyReLU,最终通过两个全连接层和最终的sigmoid激活函数得到预测为自然图像的概率。SRGAN的损失函数为:

img

其中内容损失可以是基于均方误差的损失的损失函数:

img

也可以是基于训练好的以ReLU为激活函数的VGG模型的损失函数:

img

i和j表示VGG19网络中第i个最大池化层(maxpooling)后的第j个卷积层得到的特征。对抗损失为:

img

文章中的实验结果表明,用基于均方误差的损失函数训练的SRResNet,得到了结果具有很高的峰值信噪比,但是会丢失一些高频部分细节,图像比较平滑。而SRGAN得到的结果则有更好的视觉效果。其中,又对内容损失分别设置成基于均方误差、基于VGG模型低层特征和基于VGG模型高层特征三种情况作了比较,在基于均方误差的时候表现最差,基于VGG模型高层特征比基于VGG模型低层特征的内容损失能生成更好的纹理细节。

github(tensorflow): https://github.com/zsdonghao/SRGAN

github(tensorflow): https://github.com/buriburisuri/SRGAN

github(torch): https://github.com/junhocho/SRGAN

github(caffe): https://github.com/ShenghaiRong/caffe_srgan

github(tensorflow): https://github.com/brade31919/SRGAN-tensorflow

github(keras): https://github.com/titu1994/Super-Resolution-using-Generative-Adversarial-Networks

github(pytorch): https://github.com/ai-tor/PyTorch-SRGAN

EDSR+MDSR [NTIRE2017]

EDSR(单一尺度网络)

EDSR是NTIRE2017超分辨率挑战赛上获得冠军的方案。

如论文中所说,EDSR最有意义的模型性能提升是去除掉了SRResNet多余的模块,从而可以扩大模型的尺寸来提升结果质量。

EDSR的网络结构如下图所示。

img

EDSR在结构上与SRResNet相比,就是把批规范化处理(batch normalization, BN)操作给去掉了。文章中说**:原始的ResNet最一开始是被提出来解决高层的计算机视觉问题,比如分类和检测,直接把ResNet的结构应用到像超分辨率这样的低层计算机视觉问题,显然不是最优的。**由于批规范化层消耗了与它前面的卷积层相同大小的内存,在去掉这一步操作后,相同的计算资源下,EDSR就可以堆叠更多的网络层或者使每层提取更多的特征,从而得到更好的性能表现。EDSR用L1范数样式的损失函数来优化网络模型。在训练时先训练低倍数的上采样模型,接着用训练低倍数上采样模型得到的参数来初始化高倍数的上采样模型,这样能减少高倍数上采样模型的训练时间,同时训练结果也更好。

这篇文章还提出了一个能同时不同上采样倍数的网络结构MDSR。

img

MDSR 多尺度超分辨率架构

两种模型EDSR(单一尺度网络) 和 MDSR(多尺度超分辨率架构)。

MDSR的中间部分还是和EDSR一样,只是在网络前面添加了不同的预训练好的模型来减少不同倍数的输入图片的差异。在网络最后,不同倍数上采样的结构平行排列来获得不同倍数的输出结果。

从文章给出的结果可以看到,EDSR能够得到很好的结果。增大模型参数数量以后,结果又有了进一步的提升。因此如果能够解决训练困难的问题,网络越深,参数越多,对提升结果确实是有帮助吧。

github(torch): https://github.com/LimBee/NTIRE2017

github(tensorflow): https://github.com/jmiller656/EDSR-Tensorflow

github(pytorch): https://github.com/thstkdgus35/EDSR-PyTorch

SRMDNF

ESRGAN [ECCV2018]

《Enhanced Super-Resolution Generative Adversarial Networks》

相比于SRGAN它在三个方面进行了改进

  1. 改进了网络结构,对抗损失,感知损失
  2. 引入Residual-in-Residu Dense Block(RRDB)
  3. 使用激活前的VGG特征来改善感知损失

https://zhuanlan.zhihu.com/p/61009988

RCAN [ECCV 2018]

Image Super-Resolution Using Very Deep Residual Channel Attention Networks

PyTorch code for our ECCV 2018 paper “Image Super-Resolution Using Very Deep Residual Channel Attention Networks”

https://github.com/yulunzhang/RCAN

Code:

Torch ESGAN

TF2.0_ESGAN

综述

ref: https://blog.csdn.net/Jemary_/article/details/92091545

img

img

Refrences

论文综述 Super-Resolution via Deep Learning

A Deep Journey into Super-resolution: A Survey

SI-SR 论文代码整理

https://blog.csdn.net/u010327061/article/details/80101301

数据挖掘

[TOC]

奇异值分解SVD

1、特征值分解EVD

实对称矩阵

如果有n阶矩阵A,其矩阵的元素都为实数,且矩阵A的转置等于其本身($a_{ij}=a_{ji}$),(i,j为元素的脚标),则称A为实对称矩阵。

如果矩阵𝐴是一个$𝑚×𝑚$的实对称矩阵(即$𝐴=𝐴^T$),那么它可以被分解成如下的形式

$$
A = Q \sigma Q^T=
Q\left[
\begin{matrix}
\lambda_1 & \cdots & \cdots & \cdots\
\cdots & \lambda_2 & \cdots & \cdots\
\cdots & \cdots & \ddots & \cdots\
\cdots & \cdots & \cdots & \lambda_m\
\end{matrix}
\right]Q^T
\tag{1-1}
$$

其中𝑄为标准正交阵,即有$𝑄𝑄^𝑇=I$,$\sigma$为对角矩阵,且上面的矩阵的维度均为𝑚×𝑚。$𝜆_i$称为特征值,$𝑞_𝑖$是𝑄(特征矩阵)中的列向量,称为特征向量

一般矩阵

上面的特征值分解,对矩阵有着较高的要求,它需要被分解的矩阵𝐴

为实对称矩阵,但是现实中,我们所遇到的问题一般不是实对称矩阵。那么当我们碰到一般性的矩阵,即有一个𝑚×𝑛的矩阵𝐴,它是否能被分解成上面的式(1-1)的形式呢?当然是可以的,这就是我们下面要讨论的内容。

2、 奇异值分解

定义

有一个𝑚×𝑛的实数矩阵𝐴,我们想要把它分解成如下的形式
$$
A = U \sigma V^T
\tag{2-1}
$$
其中𝑈𝑉均为单位正交阵,即有$𝑈𝑈^𝑇=𝐼$和$𝑉𝑉^𝑇=𝐼$,$𝑈$称为左奇异矩阵,$𝑉$称为右奇异矩阵,$\sigma $ 仅在主对角线上有值,我们称它为奇异值,其它元素均为0。上面矩阵的维度分别为$𝑈 ∈ 𝑅^{𝑚×𝑚}$, $\sigma ∈𝑅^{𝑚×𝑛}$ , $𝑉∈𝑅^{𝑛×𝑛}$。

一般地$\sigma$ 有如下形式
$$
\sigma =
\left[
\begin{matrix}
\sigma_1 & 0 & 0 & 0 & 0\
0 & \sigma_2 & 0 & 0 & 0\
0 & 0 & \ddots & 0 & 0\
0 & 0 & 0 & \ddots & 0\
\end{matrix}
\right]_{m\times n}
$$
参考自:

https://www.cnblogs.com/endlesscoding/p/10033527.html

矩阵

协方差矩阵

正交矩阵

正定矩阵

https://zhuanlan.zhihu.com/p/44860862

【定义1】给定一个大小为 $n \times n$ 的实对称矩阵 $A$ ,若对于任意长度为$n$ 的非零向量 $x$,有$x^TAx > 0$ 恒成立,则矩阵 $A$ 是一个正定矩阵。

负定矩阵

对角矩阵

相似矩阵

可逆矩阵

初等矩阵

等价矩阵

施密特正交化

实对称矩阵

斜对称矩阵

反对称矩阵

旋转矩阵

黑塞矩阵

稀疏矩阵分解

[TOC]

sparse

kron

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> from scipy import sparse
>>> A = sparse.csr_matrix(np.array([[0, 2], [5, 0]]))
>>> B = sparse.csr_matrix(np.array([[1, 2], [3, 4]]))
>>> sparse.kron(A, B).toarray()
array([[ 0, 0, 2, 4],
[ 0, 0, 6, 8],
[ 5, 10, 0, 0],
[15, 20, 0, 0]])

>>> sparse.kron(A, [[1, 2], [3, 4]]).toarray()
array([[ 0, 0, 2, 4],
[ 0, 0, 6, 8],
[ 5, 10, 0, 0],
[15, 20, 0, 0]])

$A \times B$ 如果A是一个m×n的矩阵,而B是一个p×q的矩阵,克罗内克积则是一个mp×nq分块矩阵

image-20200828144756226 image-20200828144810260

csr_matrix

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from scipy.sparse import *

row = [0,0,0,1,1,1,2,2,2]#行指标
col = [0,1,2,0,1,2,0,1,2]#列指标
data = [1,0,1,0,1,1,1,1,0]#在行指标列指标下的数字
team = csr_matrix((data,(row,col)),shape=(3,3))
print(team)
print(team.todense())

输出结果:
(0, 0) 1
(0, 1) 0
(0, 2) 1
(1, 0) 0
(1, 1) 1
(1, 2) 1
(2, 0) 1
(2, 1) 1
(2, 2) 0
[[1 0 1]
[0 1 1]
[1 1 0]]

lil_matrix

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> a = np.arange(100).reshape(10, 10)
>>> a
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

>>> lilm = scipy.sparse.lil_matrix(a)

>>> lilm[[1, 2, 3], :].toarray() # extract the rows first...
array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]])

>>> lilm[[1, 2, 3], :][:, [4, 5, 6]].toarray() # ...then the columns
array([[14, 15, 16],
[24, 25, 26],
[34, 35, 36]])

安装

1
2
sudo apt-get install python-scipy libsuitesparse-dev
pip install --user scikit-sparse

cholesky

[TOC]

点云数据处理方法概述

ICP点云配准就是我们非常熟悉的点云处理算法之一。实际上点云数据在形状检测和分类、立体视觉、运动恢复结构、多视图重建中都有广泛的使用。点云的存储、压缩、渲染等问题也是研究的热点。随着点云采集设备的普及、双目立体视觉技术、VR和AR的发展,点云数据处理技术正成为最有前景的技术之一。PCL是三维点云数据处理领域必备的工具和基本技能,这篇文章也将粗略介绍。

三维点云数据处理技术(刚性变换篇)

1. 点云滤波(数据预处理)

点云滤波,顾名思义,就是滤掉噪声。原始采集的点云数据往往包含大量散列点、孤立点,比如下图为滤波前后的点云效果对比。

点云滤波的主要方法有:双边滤波、高斯滤波、条件滤波、直通滤波、随机采样一致滤波、VoxelGrid滤波等,这些算法都被封装在了PCL点云库中。

image-20200817104906916 image-20200817104915747

2. 点云关键点(提取算法)

我们都知道在二维图像上,有Harris、SIFT、SURF、KAZE这样的关键点提取算法,这种特征点的思想可以推广到三维空间。从技术上来说,关键点的数量相比于原始点云或图像的数据量减小很多,与局部特征描述子结合在一起,组成关键点描述子常用来形成原始数据的表示,而且不失代表性和描述性,从而加快了后续的识别,追踪等对数据的处理了速度,故而,关键点技术成为在2D和3D 信息处理中非常关键的技术。

常见的三维点云关键点提取算法有一下几种:ISS3D、Harris3D、NARF、SIFT3D

这些算法在PCL库中都有实现,其中NARF算法是博主见过用的比较多的。

3. 特征和特征描述

如果要对一个三维点云进行描述,光有点云的位置是不够的,常常需要计算一些额外的参数,比如法线方向、曲率、文理特征等等。如同图像的特征一样,我们需要使用类似的方式来描述三维点云的特征。

常用的特征描述算法有:法线和曲率计算、特征值分析、PFH、FPFH、3D Shape Context、Spin Image等。

PFH:点特征直方图描述子,FPFH:跨苏点特征直方图描述子,FPFH是PFH的简化形式。这里不提供具体描述了,具体细节去谷歌吧。

image-20200817104947276

4. 点云配准

点云配准的概念也可以类比于二维图像中的配准,只不过二维图像配准获取得到的是x,y,alpha,beta等放射变化参数,二三维点云配准可以模拟三维点云的移动和对其,也就是会获得一个旋转矩阵和一个平移向量,通常表达为一个4×3的矩阵,其中3×3是旋转矩阵,13是平移向量。严格说来是6个参数,因为旋转矩阵也可以通过罗格里德斯变换转变成13的旋转向量。

常用的点云配准算法有两种:正太分布变换和著名的ICP点云配准,此外还有许多其它算法,列举如下:

ICP:稳健ICP、point to plane ICP、point to line ICP、MBICP、GICP

NDT 3D、Multil-Layer NDT

FPCS、KFPSC、SAC-IA

Line Segment Matching、ICL

image-20200817105700692

5. 点云分割与分类

点云的分割与分类也算是一个大Topic了,这里因为多了一维就和二维图像比多了许多问题,点云分割又分为区域提取、线面提取、语义分割与聚类等。同样是分割问题,点云分割涉及面太广,确实是三言两语说不清楚的。只有从字面意思去理解了,遇到具体问题再具体归类。一般说来,点云分割是目标识别的基础。

分割:区域声场、Ransac线面提取、NDT-RANSAC、K-Means、Normalize Cut、3D Hough Transform(线面提取)、连通分析

分类:基于点的分类,基于分割的分类,监督分类与非监督分类

6. SLAM图优化

SLAM又是大Topic,SLAM技术中,在图像前端主要获取点云数据,而在后端优化主要就是依靠图优化工具。而SLAM技术近年来的发展也已经改变了这种技术策略。在过去的经典策略中,为了求解LandMark和Location,将它转化为一个稀疏图的优化,常常使用g2o工具来进行图优化。下面是一些常用的工具和方法。

g2o、LUM、ELCH、Toro、SPA

SLAM方法:ICP、MBICP、IDC、likehood Field、 Cross Correlation、NDT

7. 目标识别检索

这是点云数据处理中一个偏应用层面的问题,简单说来就是Hausdorff距离常被用来进行深度图的目标识别和检索,现在很多三维人脸识别都是用这种技术来做的。

8. 变化检测

当无序点云在连续变化中,八叉树算法常常被用于检测变化,这种算法需要和关键点提取技术结合起来,八叉树算法也算是经典中的经典了。

9. 三维重建

我们获取到的点云数据都是一个个孤立的点,如何从一个个孤立的点得到整个曲面呢,这就是三维重建的topic。

在玩kinectFusion时候,如果我们不懂,会发现曲面渐渐变平缓,这就是重建算法不断迭代的效果。我们采集到的点云是充满噪声和孤立点的,三维重建算法为了重构出曲面,常常要应对这种噪声,获得看上去很舒服的曲面。

常用的三维重建算法和技术有:

泊松重建、Delauary triangulatoins

表面重建,人体重建,建筑物重建,输入重建

实时重建:重建纸杯或者龙作物4D生长台式,人体姿势识别,表情识别

image-20200817105738655

10. 点云数据管理

点云压缩,点云索引(KDtree、Octree),点云LOD(金字塔),海量点云的渲染

PCL库简介

点云数据处理中,不仅涉及前段数据的输入,中间数据和处理,还涉及到后端点云的渲染显示,如果这些函数都要我们亲自来实现,那么开发效率必然受到极大影响。在点云数据处理领域,有一个不可或缺的助手:PCL (Point Cloud Library)。PCL在点云数据处理中的地位犹如OpenCV在图像处理领域的地位,如果你接触三维点云数据处理,那么PCL将大大简化你的开发。

Ref:

https://cloud.tencent.com/developer/article/1462320

3D visiual

[TOC]

Pyrender

1
2
3
4
5
6
7
8
9
import trimesh
import pyrender

fuze_trimesh = trimesh.load('hello.obj')
mesh = pyrender.Mesh.from_trimesh(fuze_trimesh)
scene = pyrender.Scene()
scene.add(mesh)

pyrender.Viewer(scene, use_raymond_lighting=True)

psbody

Mesh

1
2
3
4
from psbody.mesh import Mesh, MeshViewer
scan = Mesh(filename=join(path, 'smpl_registered.obj'))
scan.set_texture_image(tex_file='***.jpg')
scan.show()

MeshViewer

1
2
3
4
5
6
7
8
from psbody.mesh import Mesh, MeshViewer
scan = Mesh(filename='non-vn.obj')

mvs = MeshViewers(shape=(1,3), keepalive=True) #, window_width=1024, window_height=576)
mvs[0][0].set_background_color(np.array([1, 1, 1]))
mvs[0][0].set_static_meshes([scan])
mvs[0][1].set_static_meshes([scan1])
mvs[0][2].set_static_meshes([scan2])