0%

OPENCV车流量计数

某鱼上的课设

代码来自 github 博客,原本的代码用的是背景估计的检测方法,效果不太好。将检测方法改成 opencv级联检测后,效果好了许多

计数的原理

  1. 轨迹维护的思路:就是最近邻,将当前帧所有检测的点 和 历史轨迹去比较,将距离最近 且 满足距离阈值的点和轨迹匹配。距离的计算方法,根据前两次的历史位置预测当前的位置,然后和检测的位置计算距离。
  2. 计数的方法:遍历所有轨迹,取最近两次的位置,上一次位置在检测区外,这一次的位置在检测区内,就计数。

环境

解决 opencv VideoCapture 返回为 None 的问题 参考链接

1
2
conda install -c menpo opencv3
pip install opencv-contrib-python

github

SVM舰船识别

环境配置

win系统下 qt crator + opencv2.4.9配置

https://src.fedoraproject.org/rpms/opencv/blob/f24/f/opencv-2.4.12.3-gcc6.patch

策略

为了快速准确地提取舰船目标, 目前的舰船检 测方法通常采取由粗到精的策略, 首先从大幅图像 中快速提取出候选区域, 利用反映舰船目标的最为 明显且计算量小的一些特征, 确定出舰船目标可能 存在的区域; 然后再利用精细特征对候选区域进一 步确认分析, 去除虚警, 找出真实的舰船目标

对于小于 3m的低分辨率遥感图像 ,由于分辨率较低 ,图 像中无法反映出舰船目标的细节特征 ,所以仅计算每个候选 目标进行一阶灰度特征。包括平均灰度、方差、一阶能量、 一阶熵等 )来构建目标向量。

对于高于 3m的高分辨遥感图像 ,由于图像可以反应目 标细节特征信息 ,因而在构建目标向量过程中 ,可以通过增加 灰度共生矩阵的二阶纹理特征 [16] (包括相关性、局部平稳、惯 性矩、二阶熵等 )和形状不变矩 来提高识别的精确度。

特征

灰度分块特征

论文:基于SVM 的高分辨率SAR图像舰船目标检测算法

亮度特征

  1. 块灰度均值,像素均值
  2. 方差系数,是个值 就是方差
  3. 质量,即灰度总和

纹理特征:

  1. 方差,和上面的方差不太一样
  2. 加权填充比 分块区域中 n 个最亮的像素值占分块区域内总能量的比例
  3. 离散系数 ?
  4. 灰度最大起伏量 最大 - 均值

峰值特征:

  1. 峰度 ?
  2. 偏差系数:表示分块内灰度值分布不对成程度的统计

其他灰度特征:

  1. 灰度能量比函数,反应对分块图像能量的一种度量 ?
  2. 标准差。和前面的有点相似

1,2,4特征组合成特征向量效果最好

image-20210922202537280

Hog特征

Gabor 滤波

几何形状特征

论文:高分辨率光学遥感图像舰船目标检测关键技术研究

基于船头检测与船体轮廓定位的舰船检测算法。该算法根据船头区域在极坐标变换域上可近似为梯形形状的特点,首先在极坐标变换域上对Harris角点区域提取一系列船头形状特征,使用SVM分类器筛选出疑似船头区域,同时给出船身的初始方向;然后,借助于周围与船身方向一致的直线段对船身初始方向进行修正,可得到更加准确的船身方向,并利用船体轮廓的对称性与船体轮廓处直线段定位出船身;最后通过海洋上下文信息剔除掉虚假船只

紧致度,凸度,矩形度,偏心率,矩不变量

不变矩特征

论文:基于支持向量机的遥感图像舰船目标识别方法_李毅

不随图像位移 旋转 尺度 变化。但是不变矩特征仅能构表现物体的整体形状特征, 而不能提 取细节分量上的特征信息 + 共生矩阵求纹理特征(这个需要尺寸较大 例如 3m分辨率以上)

纹理特征

简单为例特征:均值,方差,矩,熵

灰度共生矩阵

实验

降分辨率测试

首先按照不变长宽比的原则将 扣出用于训练的图像块

在训练时将 图像块 resize 为 train_size,训练svm

测试的时候,输入为一张完整的图片,已知图中的目标的大小,按照 设置的 obj_size,将原图缩小,使得目标的尺寸 = obj_size

测试一:

hrsc数据集 crop 出来的原图

测试发现 obj_size = 10 检测率和虚警率都不会降低。难道是因为 这样缩放虽然目标的信息丢失了,但是背景的信息也丢失了,所以实际信噪比不变 ,检测率就不怎么改变

如果是常规小目标 输入图像是1280*720的尺寸,如果hog特征的尺寸还是设置为80x80,输入测试的时候就检测不到图上存在的10x10的小目标。

YouTube爬虫

参考链接

使用谷歌api爬视频评论

使用谷歌 api ,很方便。按照谷歌的官方文档快速开始

  1. conda环境中安装相应的包
1
2
pip install --upgrade google-api-python-client
pip install --upgrade google-auth-oauthlib google-auth-httplib2
  1. 设置凭证:按照别人步骤操作就行。
    • 这个网页中先创建一个工程(如果没有的话)
    • 打开侧边的 选项,进入,选择 YouTube Data API v3启用
    • 返回刚才的界面再选择 凭据页面进入,创建 API 密钥和 OAuth 2.0 客户端ID
      • API密钥是用于 web 访问用的(具体我也不知道)
      • 在本地python代码中使用谷歌api 要创建OAuth ID
  2. 创建 OAuth ID:注意选项中要 选择 桌面客户端类型(第一次选的Web客户端就不行),然后下载 json,在代码中会用到这个json
image-20210913171539492
  1. 代码:修改json文件为刚才下载的路径。vpn模式为 规则
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import os
import numpy as np
import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
from googleapiclient.errors import HttpError
import pandas as pd
import json
import socket
import socks
import requests
## 科学上网,你也需要科学使用代理,不然科学不了外网,也许你不会需要。
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'}
socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 10080)
socket.socket = socks.socksocket
##
scopes = ["https://www.googleapis.com/auth/youtube.force-ssl"]

def main():
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

api_service_name = "youtube"
api_version = "v3"
# 这个文件需要自己注册完,自己下载
client_secrets_file = "client_secret_303198188639-ptcmpb7m0urubvl0mvoip8tp05tp8lv6.apps.googleusercontent.com.json"
# Get credentials and create an API client
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
client_secrets_file, scopes)
credentials = flow.run_console()

youtube = googleapiclient.discovery.build(
api_service_name, api_version, credentials=credentials)
videoId = '5YGc4zOqozo'
request = youtube.commentThreads().list(
part="snippet,replies",
videoId=videoId,
maxResults = 100
)
response = request.execute()
# print(response)

totalResults = 0
totalResults = int(response['pageInfo']['totalResults'])

count = 0
nextPageToken = ''
comments = []
first = True
further = True
while further:
halt = False
if first == False:
print('..')
try:
response = youtube.commentThreads().list(
part="snippet,replies",
videoId=videoId,
maxResults = 100,
textFormat='plainText',
pageToken=nextPageToken
).execute()
totalResults = int(response['pageInfo']['totalResults'])
except HttpError as e:
print("An HTTP error %d occurred:\n%s" % (e.resp.status, e.content))
halt = True

if halt == False:
count += totalResults
for item in response["items"]:
# 这只是一部分数据,你需要啥自己选就行,可以先打印下你能拿到那些数据信息,按需爬取。
comment = item["snippet"]["topLevelComment"]
author = comment["snippet"]["authorDisplayName"]
text = comment["snippet"]["textDisplay"]
likeCount = comment["snippet"]['likeCount']
publishtime = comment['snippet']['publishedAt']
comments.append([author, publishtime, likeCount, text])
if totalResults < 100:
further = False
first = False
else:
further = True
first = False
try:
nextPageToken = response["nextPageToken"]
except KeyError as e:
print("An KeyError error occurred: %s" % (e))
further = False
print('get data count: ', str(count))
### write to csv file
data = np.array(comments)
df = pd.DataFrame(data, columns=['author', 'publishtime', 'likeCount', 'comment'])
df.to_csv('google_comments.csv', index=0, encoding='utf-8')

### write to json file
result = []
for name, time, vote, comment in comments:
temp = {}
temp['author'] = name
temp['publishtime'] = time
temp['likeCount'] = vote
temp['comment'] = comment
result.append(temp)
print('result: ', len(result))

json_str = json.dumps(result, indent=4)
with open('google_comments.json', 'w', encoding='utf-8') as f:
f.write(json_str)

f.close()
if __name__ == "__main__":
main()

文本分析

英文文本挖掘预处理流程

参考链接 参考链接

  1. 去除非文本部分,例如表情符号,非英文字符等
  2. 英文分词(不一定需要 但是 像 New York 这种如果不做分词容易错)
  3. 拼写检查更正 pyenchant
  4. 词干提取 和 词型还原,即要找到词的原始形式 nltk
  5. 转换为小写,统计词频率需要
  6. 引入停用词,比如“a”,“to”,一些短词,还有一些标点符号,这些我们不想在文本分析的时候引入,因此需要去掉,这些词就是停用词
  7. 对文本做情感分析,词性标注(动词,形容词等),词频率分析,LDA主题分析 gensim,TF-IDF聚类

情感分析

使用 TextBlob 来判断单个词的情感

主题分析

参考链接 参考链接 参考链接

语义网络参考链接

git的使用

一般用法

  1. 首先在远程创建一个工程,按照工程下的提示在本地该工程的文件夹下打开git bash按顺序输入(其中有和远程库关联的步骤,必须输入)

  2. git init 在当前文件夹下创建一个git 仓库管理该文件夹下的工程

  3. git add . 表示将所有文件提交到监控区域 则有文件改动都会被记录

  4. git commit -m "inform" 提交当前改动

  5. git push 将代码提交到git hub 远程仓库 不要在浏览器上随意删除已经提交了的文件,否则本地的记录和云端的记录不一致再次提交会报错,所有对文件的修改最好只在本地修改让后上传云端

    空文件夹不会被add添加 只有里面有文件才会被add .gitignore文件可以选择push时忽略的文件

  6. 在新的机子上生成 公钥 ssh-keygen -C“ changruowang@qq.com” 然后在用户目录下 把生成的id_rsa.pub文件中的东西粘贴添加到 网页版github账户的列表中 这样本地可以访问远程github仓库了

  7. 使用 git clone https… 形式的连接,提示错误,要将 http / https 代理关闭

    fatal: unable to access 'https://github.com/hexojs/hexo-starter.git/': Failed to connect to 127.0.0.1 port 1080: Connection refused

    git config --global --unset http.proxy

    git config --global --unset https.proxy

新建一个resp

2021/9 月提交一个爬虫工程很多报错,最后使用下面的流程成功新增了一个 resp 并提交

  • 在远程 github 网站上 新建一个 resp
  • 将这个 resp clone 到本地
  • 将源代码拷贝到其中
  • git push

const 和 constexpr

const

参考链接

const 修饰变量

无论const修饰全局还是函数的局部变量,那么该变量都不能被程序员显式的修改,否则编译器会报错。但是变量本质都是地址,因此可以对他取地址给指针,然后通过指针再去修改该值。这样操作编译器不会报错。例如:

1
2
3
const int N = 1000;
int* p = const_cast<int*>(&N);
*p = 102; // 这样就将N的值修改了
  • 全局 const 变量:存储在只读存储区,使用上面的方法可以修改 会导致运行错误
  • 函数局部的 const 变量:存在栈里,使用上面的方法修改它的值,且不会产生运行错误

c++ 对 const 会对默认类型的变量(非自定义的结构体成员变量,常规的int float 等会优化)在编译时期会优化,上面的例子中 会将所有地方的 N 像宏定义一样直接替换为 1000;例如

1
2
cout << N << " " << *p;
// N: 输出1000 *P输出102

因此 推荐将变量定义为 const 类型 这样编译器会直接替换为值,后面就减少一次内存访问请求 相对宏定义的优势就是 有类型检查

const 类对象

定义 const 类对象 或者 结构体变量一样,代表他们的成员都不能被修改。并且,const 类对象只能调用它的 const 成员函数,而不能调用普通的非const成员函数。

img

const指针

  • 一种指 指向的地址的内容不可以修改 但是它可以转而指向别的地址 (结合最开始的例子可以看出 编译器是通过 “指针的类型” 来判断是否只读的,因此如果普通指针指向const的变量地址,它的值仍然可以修改) const int *p
  • 指向的地址可以修改,但是它不能再指向别人 int * const p = &a ( 就看const 后面跟的是啥 啥就不能变)

const 成员函数

代表该成员函数 不能修改类中的成员变量,同时也正是这个特性,const 类的对象只能调用 ocnst 修饰的成员函数

constexpr

参考链接

constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。

编译时常量/常量表达式

  • 必须是可以在编译阶段被识别的。比如模版的参数/数组的大小
  • 例如 const int a = 5; 此时 5 就是一个常量表达式 sizeof() 就是个常量表达式 在编译时期就能确定 可以能用来初始化数组大小 case语句的分支等
1
2
3
4
5
6
template <int N>
class fixed_size_list
{ /*...*/ };

fixed_size_list<X> mylist; // <-- X必须是字面值类型
int numbers[X]; // <-- X必须是字面值类型

特性

constexpr 在修饰函数的时候

  • 如果函数的入口参数 是使用的字符常量,在编译时期可以确定 那么这个函数就是常量表达式 返回值可以直接用来初始化数组大小
  • 如果入口参数的值编译时期无法直接确定 那么他就是个普通函数

constexpr的好处:

  1. 是一种很强的约束,更好地保证程序的正确语义不被破坏。
  2. 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
  3. 相比宏来说,没有额外的开销,但更安全可靠

子字符串查找 ——Rabin-Karp算法

求字符串的连续子串,如果传统的散列法复杂度很高。而Rabin-Karp算法的思路是 将字符串每个 字符 a~z 看作 0~25 组成的 26 进制的数。并且在计算 每个位置长度 为 n 的连续子串的散列值时 可以使用 除留余法 方式 将散列的复杂度降低为 O(1)

image-20210711114457000

例题

最长重复子串

这个题也可以用 动态规划做,dp 设置为 字符串1 前 i 个 和 字符串2 前 j 个的子串的最长后缀长度。通过 两层遍历即可完成。

但是使用 二分查找 + hash 法 复杂度会更低(前提是 hash 使用Rabin-Karp算法散列,保证散列的复杂度为O(1))

参考链接

二分匹配

匈牙利算法

参考链接

主要就是 能划分为 二分图,集合0中的元素之间不会有匹配关系,它只会和集合1中的元素有匹配关系,可能一对一一对多。同理集合2中的元素也是

常用的套路是 按照 奇偶 位置划分二分集合。

算法思路

首先 依次从左边集合中选出一个元素 和 右边集合中的元素匹配。如果右边集合中某个元素已经被占用了,那么根据它指向的左边集合中的元素号,递归的去调整它,以给当前元素腾位置。如果能腾出来,那么成功。否则返回false 算法实现的关键在于 一个记录 右边 到左边 的数组序号的映射关系,和 右边序号的访问情况。

算法时间复杂度,O(ExV) V为左边集合顶点的数目,E为图中的边数。每次去左边的一个顶点去和右边集合的顶点匹配,最坏情况就是把图中所有的边都遍历一遍才能找到可以匹配的结果。

例题

代码

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
int M, N;            //M, N分别表示左、右侧集合的元素数量
int Map[MAXM][MAXN]; //邻接矩阵存图
int p[MAXN]; //记录当前右侧元素所对应的左侧元素
bool vis[MAXN]; //记录右侧元素是否已被访问过
bool match(int i)
{
for (int j = 1; j <= N; ++j)
if (Map[i][j] && !vis[j]) //有边且未访问
{
vis[j] = true; //记录状态为访问过
if (p[j] == 0 || match(p[j])) //如果暂无匹配,或者原来匹配的左侧元素可以找到新的匹配
{
p[j] = i; //当前左侧元素成为当前右侧元素的新匹配
return true; //返回匹配成功
}
}
return false; //循环结束,仍未找到匹配,返回匹配失败
}
int Hungarian()
{
int cnt = 0;
for (int i = 1; i <= M; ++i)
{
memset(vis, 0, sizeof(vis)); //重置vis数组
if (match(i))
cnt++;
}
return cnt;
}

KM算法

KM 算法是解决带权值的 二分匹配问题

参考链接

算法实现原理

基本原理也是依靠匈牙利算法,但是每个边都带了权重,因此思想是 取当前的最大子图 然后按照匈牙利算法去寻找完美匹配,如果无法实现完备匹配 就需要扩充最大子图。

通俗的算法原理:集合A中的1个元素都可以和B中的多个元素匹配,但是由于只能择其一,所以就选和B中匹配得分最大的边 联通,同理所有A中的点都这样筛选边,这样如果 A中的所有元素都和B中的有匹配(即实现完备匹配),那么此时图的匹配权重一定是最大。此时这就可以转化为匈牙利算法去搜索有无完备匹配。

如果没有实现完备匹配,那一定是有 n 个点 n-1 个边之间出现冲突。所以直观的应该放宽选取可匹配边的权重标准 让新的权重较小的边也加入图中,再尝试是否可以完备匹配,如果此时可以完备匹配 那么起始最终匹配的权重和相对初始的最大子图的完备匹配后的权重和(但是无法实现) 是 略微缩小的。KM算法就是通过一定的策略完成 扩充子图 -> 完备匹配 这个循环的,并保证 子图的权重 一定是 从初始最大 -> 小递减的 中间没有跳跃,所以最后匹配的权重和最大

  • 寻找相等子图的完备匹配

实现步骤

  1. 初始化可行顶标的值 (设定lx,ly的初始值)
  2. 用匈牙利算法寻找相等子图的完备匹配
  3. 若未找到增广路则修改可行顶标的值
  4. 重复(2)(3)直到找到相等子图的完备匹配为止

实现子图扩充最优 要 按照两个原则来实现:

  • 可行顶标
  • 可行边,顶点的定标和 等于 边的权重 为一个可行边 可以加入相等子图中