├── QQ群.png
├── README.md
├── Yundun_chinese
├── .idea
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── postion_predict.iml
│ └── workspace.xml
├── Yidun_Chinese_Captcha.py
├── __pycache__
│ ├── Yidun_Chinese_Captcha.cpython-36.pyc
│ └── settings.cpython-36.pyc
├── api_requests.py
├── captcha_server.py
├── detect
│ ├── __pycache__
│ │ └── models.cpython-36.pyc
│ ├── models.py
│ └── utils
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ ├── augmentations.cpython-36.pyc
│ │ ├── datasets.cpython-36.pyc
│ │ ├── google_utils.cpython-36.pyc
│ │ ├── layers.cpython-36.pyc
│ │ ├── parse_config.cpython-36.pyc
│ │ ├── torch_utils.cpython-36.pyc
│ │ └── utils.cpython-36.pyc
│ │ ├── adabound.py
│ │ ├── augmentations.py
│ │ ├── datasets.py
│ │ ├── evolve.sh
│ │ ├── gcp.sh
│ │ ├── google_utils.py
│ │ ├── layers.py
│ │ ├── parse_config.py
│ │ ├── torch_utils.py
│ │ └── utils.py
├── output
│ ├── 00000.jpg
│ ├── 00001.jpg
│ ├── 00002.jpg
│ ├── 00003.jpg
│ ├── 00004.jpg
│ ├── 0010fe2902d64b31817a479e72e40acd_0.jpg
│ ├── 0010fe2902d64b31817a479e72e40acd_1.jpg
│ ├── 0010fe2902d64b31817a479e72e40acd_2.jpg
│ ├── 0010fe2902d64b31817a479e72e40acd_3.jpg
│ ├── 0010fe2902d64b31817a479e72e40acd_4.jpg
│ ├── 0014f3dda06d472e8775a1dc31accee5_0.jpg
│ ├── 0014f3dda06d472e8775a1dc31accee5_1.jpg
│ ├── 0014f3dda06d472e8775a1dc31accee5_2.jpg
│ ├── 0014f3dda06d472e8775a1dc31accee5_3.jpg
│ ├── 0021b0a361ff4fa08629c70632f3d70d_0.jpg
│ ├── 0021b0a361ff4fa08629c70632f3d70d_1.jpg
│ ├── 0021b0a361ff4fa08629c70632f3d70d_2.jpg
│ ├── 0021b0a361ff4fa08629c70632f3d70d_3.jpg
│ ├── 0117d4b28cac47808cce3f5d18e2a2bb_0.jpg
│ ├── 0117d4b28cac47808cce3f5d18e2a2bb_1.jpg
│ ├── 0117d4b28cac47808cce3f5d18e2a2bb_2.jpg
│ ├── 0117d4b28cac47808cce3f5d18e2a2bb_3.jpg
│ ├── 0191c48ace5c4dbb9835e2bfe452dd62_0.jpg
│ ├── 0191c48ace5c4dbb9835e2bfe452dd62_1.jpg
│ ├── 0191c48ace5c4dbb9835e2bfe452dd62_2.jpg
│ ├── 0191c48ace5c4dbb9835e2bfe452dd62_3.jpg
│ ├── 0194cc953cdb4db1a29ba4e2b53fa378_0.jpg
│ ├── 0194cc953cdb4db1a29ba4e2b53fa378_1.jpg
│ ├── 0194cc953cdb4db1a29ba4e2b53fa378_2.jpg
│ ├── 0194cc953cdb4db1a29ba4e2b53fa378_3.jpg
│ ├── 02052f20b4cf43c7a3138bb583404c69_0.jpg
│ ├── 02052f20b4cf43c7a3138bb583404c69_1.jpg
│ ├── 02052f20b4cf43c7a3138bb583404c69_2.jpg
│ ├── 02052f20b4cf43c7a3138bb583404c69_3.jpg
│ ├── 02052f20b4cf43c7a3138bb583404c69_4.jpg
│ ├── 02513359cf4b4d83a18b779a80538dec_0.jpg
│ ├── 02513359cf4b4d83a18b779a80538dec_1.jpg
│ ├── 02513359cf4b4d83a18b779a80538dec_2.jpg
│ ├── 02513359cf4b4d83a18b779a80538dec_3.jpg
│ ├── 02513359cf4b4d83a18b779a80538dec_4.jpg
│ ├── 0259cf81304f45609641dc11c7855237_0.jpg
│ ├── 0259cf81304f45609641dc11c7855237_1.jpg
│ ├── 0259cf81304f45609641dc11c7855237_2.jpg
│ ├── 0259cf81304f45609641dc11c7855237_3.jpg
│ ├── 02923304f2ff445e838954a9f7d307ea_0.jpg
│ ├── 02923304f2ff445e838954a9f7d307ea_1.jpg
│ ├── 02923304f2ff445e838954a9f7d307ea_2.jpg
│ ├── 02923304f2ff445e838954a9f7d307ea_3.jpg
│ ├── 02923304f2ff445e838954a9f7d307ea_4.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ └── now.jpg
├── readme.md
├── requirements.txt
└── settings.py
├── captcha
├── 1.jpg
├── 10.jpg
├── 11.png
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── 5.png
├── 6.png
├── 7.png
├── 8.png
└── 9.png
├── icon.png
├── vx.jpg
└── vxq.png
/QQ群.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/QQ群.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spider_Captcha
2 | #### About this paper:
3 | 分享一下易盾的文字点选识别,其中包括验证码模型,和7w+文字数据集,目前识别率在80左右,当作一个小练习,后续有数据集的同学愿意分享,可以接着训练该模型,由于模型和数据集比较大,就分享在QQ群文件里了,有需要的可以进群一起交流!
4 | #### About Captcha:
5 | 网易易盾、极验等主流验证码破解。
6 | 已更新极验文字点选、极验滑动、极验语序、极验九宫格、易盾文字、易盾图标、易盾滑动、易盾乱序还原拼图、税务红黄蓝验证码、梦幻西游8位汉字识别、多种字符验证码。
7 | #### 其验证码类型图片已经放到captcha目录下!
8 | #### Warning!
9 | 请保证本项目仅用于研究学习!感谢配合支持!
10 |
11 | ---
12 |
13 | #### About javascript:
14 | To learn about basic! learn it with us! QQ群:1126310403
15 | #### About updated:
16 | #### 关于解决类似极验文字点选验证码的两套框架!
17 | ##### 一、目标检测框架
18 | ##### 二、文字识别框架
19 |
20 | #### 易盾文字点选验证码模型训练思路!
21 | ##### 一、label_image 标注文字位置约400左右
22 | ##### 二、pytorch + yolov3 训练位置预测模型
23 | ##### 三、根据预测位置裁剪汉字图片
24 | ##### 四、pytorch + CNN 深层神经网络识别汉字
25 | ##### 总结:大部分的验证码用目标检测+卷积神经网络是都能搞定的!如果类似易盾图标点选就可以减少数据集,使用孪生网络进行预测!
26 |
27 | #### About share:
28 | 验证码的识别的关键影响因素跟数据集的质量和数量影响很大!所以我也希望大家能共同一起分享数据集!
29 | ##### 如果觉得我分享的质量觉着满意,麻烦给个star! ^_^ thank you!也是每个创作者继续分享的动力!fighting!
30 |
31 | #### About install:
32 | ##### 很多github上都有讲Linux系统的,本文以windows为例!
33 | - [x] pip install torch==1.4.0+cpu torchvision==0.5.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
34 | - [x] pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
35 | - [x] pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI
36 | - [x] 本套识别系统环境搭建十分简单!如果使用cuda的高级玩家,把pytorch的版本换下就ok
37 | - [x] [pytorch环境安装链接](https://pytorch.org/)
38 | - [x] 安装完毕!启动captcha_server.py 就ok!调用实例在api_requests.py里面!
39 | - [x] 由于方便大家下载模型和数据集!已经上传至QQ群:1126310403 群文件!
40 |
41 |
42 | #### About contact:
43 | #### 欢迎技术交流:`923185571@qq.com`
44 | #### QQ群:1126310403
45 | #### QQ:923185571
46 | #### 商务合作VX:`spider_captcha`
47 |
48 | #### 或者扫一扫加我微信,请备注GitHub,谢谢!。
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Yundun_chinese/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Yundun_chinese/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Yundun_chinese/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Yundun_chinese/.idea/postion_predict.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Yundun_chinese/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
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 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | 1588987877540
172 |
173 |
174 | 1588987877540
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/Yundun_chinese/Yidun_Chinese_Captcha.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 | from detect.models import Darknet
3 | from detect.utils.utils import *
4 | from detect.utils.datasets import *
5 | from settings import *
6 | from io import BytesIO
7 | import cv2
8 | import base64
9 | import torch
10 | import torchvision
11 | from PIL import Image
12 | from efficientnet_pytorch import EfficientNet
13 | from albumentations.pytorch import ToTensor
14 | import torchvision.transforms as transforms
15 | from albumentations import (Compose, Resize)
16 |
17 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
18 | img_size = 192
19 | img_save = 'output/now.jpg'
20 |
21 | class Predict_chinese():
22 | def __init__(self):
23 | self.net = EfficientNet.from_name('efficientnet-b4')
24 | state_dict = torch.load(efficientnet_b4_pth)
25 | self.net.load_state_dict(state_dict)
26 | in_fea = self.net._fc.in_features
27 | self.net._fc = nn.Linear(in_features=in_fea, out_features=2181, bias=True)
28 | # self.net = EfficientNet.from_pretrained('efficientnet-b1', num_classes=2181)
29 | self.net.load_state_dict(torch.load(model_chinese_crop_b4, map_location=device))
30 | self.net.eval()
31 | self.transform = transforms.Compose([
32 | transforms.Lambda(self.albumentations_transform),
33 | ])
34 | self.trainset = torchvision.datasets.ImageFolder(root=data_train_path,
35 | transform=self.transform)
36 |
37 | def strong_aug(self,p=1):
38 | return Compose([
39 | Resize(100, 100),
40 | ToTensor()
41 | ], p=p)
42 |
43 | def albumentations_transform(self,image, transform=strong_aug(1)):
44 | if transform:
45 | image_np = np.array(image)
46 | augmented = transform(image=image_np)
47 | image = augmented['image']
48 | return image
49 |
50 | def predict_chinese(self,img):
51 | # img = Image.open('data1/test/kk/00b1d9958bf34b9f8ba7d1070cb28c9d_醉.jpg')
52 | img = self.transform(img).unsqueeze(0)
53 | pred = self.net(img)
54 | _, predicted = pred.max(1)
55 | result = self.trainset.classes[predicted[0]]
56 | return result
57 |
58 |
59 | class Captcha_yidun_chinese_position(object):
60 | def __init__(self, config_path, model_path,names='model/predict_position/target.names',conf_thres=0.3, nms_thres=0.6,save_image=False):
61 | self.model = self.load_position_model(config_path, model_path) #加载目标检测模型
62 | self.chinese_model = Predict_chinese()
63 | self.conf_thres = conf_thres #object confidence threshold
64 | self.nms_thres = nms_thres #IOU threshold for NMS
65 | self.save_image = save_image #是否保存检测标注后的图片
66 | self.names = load_classes(names) #加载目标检测类别名字
67 | self.colors = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(self.names))]
68 | if os.path.exists('output'):
69 | pass # delete output folder
70 | else:
71 | os.makedirs('output') # make new output folder
72 |
73 | def load_position_model(self,config_path, model_path):
74 | model = Darknet(config_path, img_size=img_size).to(device)
75 | if model_path.endswith(".weights"):
76 | model.load_darknet_weights(model_path)
77 | else:
78 | model.load_state_dict(torch.load(model_path, map_location=device)['model'])
79 | model.to(device).eval()
80 | return model
81 |
82 | def crop_chinese_and_recognize(self,data):
83 | im = Image.open(img_save)
84 | region = im.crop((data['x'], data['y'], data['x'] + data['width'], data['y'] + data['height'])).resize((60, 60))
85 | base64_chinese_single = self.chinese_model.predict_chinese(region)
86 | return base64_chinese_single
87 |
88 | def image_resize_and_convert(self,image_base64): # 图片重置大小与二值化处理
89 | image = base64.b64decode(image_base64)
90 | img_f = BytesIO(image)
91 | image = Image.open(img_f)
92 | out = image.resize((320, 160), Image.ANTIALIAS) # resize image with high-quality
93 | out.save(img_save)
94 |
95 | def predict_postion(self, image_path):
96 | try:
97 | self.image_resize_and_convert(image_path)
98 | image_path = 'output/now.jpg'
99 | except:
100 | print(f'当前输入:{image_path}')
101 |
102 | im0s = cv2.imread(image_path)
103 | img = letterbox(im0s, new_shape=img_size)[0]
104 | img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
105 | img = np.ascontiguousarray(img)
106 | img = torch.from_numpy(img).to(device)
107 | img = img.float() # uint8 to fp16/32
108 | img /= 255.0 # 0 - 255 to 0.0 - 1.0
109 | if img.ndimension() == 3:
110 | img = img.unsqueeze(0)
111 | with torch.no_grad():
112 | pred = self.model(img)[0]
113 | det = non_max_suppression(pred, self.conf_thres, self.nms_thres)[0]
114 | det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0s.shape).round()
115 | item_list = {}
116 | for *xyxy, conf, cls in det:
117 | item = {}
118 | item_center = {}
119 | position_one = ('%g ' * 6 ) % (*xyxy, cls, conf)
120 | position_one_list = [float(i) for i in position_one.split(' ')[:-1]]
121 | item["x"] = position_one_list[0]
122 | item["y"] = position_one_list[1]
123 | item["width"] = position_one_list[2] - position_one_list[0]
124 | item["height"] = position_one_list[3] - position_one_list[1]
125 | item["x_center"] = item["x"] + item["width"]/2
126 | item_center['x'] = int(item["x_center"])
127 | item["y_center"] = item["y"] + item["height"]/2
128 | item_center['y'] = int(item["y_center"])
129 | item["class"] = '0'
130 | item["acc"] = position_one_list[-1]
131 | chinese = self.crop_chinese_and_recognize(data=item)
132 | item_list[chinese] = item_center
133 | if self.save_image:
134 | label = '%s %.2f' % (self.names[int(cls)], conf)
135 | plot_one_box(xyxy, im0s, label=label, color=self.colors[int(cls)])
136 | cv2.imwrite(f'output/{image_path}', im0s)
137 | print(f"【易盾:文字点选】识别结果:{item_list}")
138 | return item_list
139 |
140 |
141 | if __name__ == '__main__':
142 | image_path = 'output/now.jpg'
143 | detector = Captcha_yidun_chinese_position(model_path_chinese_cfg, model_path_chinese,save_image=False)
144 | position = detector.predict_postion(image_path)
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/Yundun_chinese/__pycache__/Yidun_Chinese_Captcha.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/__pycache__/Yidun_Chinese_Captcha.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/__pycache__/settings.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/__pycache__/settings.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/api_requests.py:
--------------------------------------------------------------------------------
1 | # 请求方式
2 | import base64,json
3 | import requests,os
4 |
5 | path = r'C:\Users\spyder\Desktop\yidun_chinese\images'
6 | for i in os.listdir(path):
7 | f = open(os.path.join(path,i), 'rb')
8 | s = base64.b64encode(f.read()).decode()
9 | res=requests.post(url='http://127.0.0.1:8000/captcha',data=json.dumps({'img':s}))
10 | data = res.json()
11 | print(data)
12 | data = data.get('data')
13 | f.close()
14 | item = {}
15 | for k,v in data.items():
16 | x = v.get('x')
17 | item[x] = k
18 | k = sorted(item.keys())
19 | k = ''.join([item.get(i) for i in k])
20 | os.rename(os.path.join(path,i),path+'\\'+k+'.jpg')
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Yundun_chinese/captcha_server.py:
--------------------------------------------------------------------------------
1 | #-*-coding:utf-8-*-
2 | from flask import request
3 | from flask import Flask
4 | from settings import *
5 | import logging
6 | import json
7 | from Yidun_Chinese_Captcha import Captcha_yidun_chinese_position
8 |
9 | app = Flask(__name__)
10 |
11 | logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
12 | LOGGER = logging.getLogger(__name__)
13 |
14 | CH = Captcha_yidun_chinese_position(model_path_chinese_cfg, model_path_chinese,save_image=False)
15 |
16 | @app.route('/captcha', methods=['POST'])
17 | def parse_server():
18 | data = request.data
19 | data = json.loads(data.decode())
20 | img_s = data.get('img', None)
21 | front = data.get('front', None)
22 | res = {}
23 | if img_s is None:
24 | msg = "need img param"
25 | code = 400
26 | res['msg'] = msg
27 | res['code'] = code
28 | res['data'] = []
29 | elif front:
30 | result = CH.predict_postion(img_s)
31 | points = []
32 | for i in front:
33 | points.append(result.get(i))
34 | msg = f'已自动根据 “{front}” 排序按顺序返回汉字位置,None表示该汉字识别失败'
35 | code = 200
36 | res['msg'] = msg
37 | res['code'] = code
38 | res['data'] = points
39 | else:
40 | result = CH.predict_postion(img_s)
41 | res['msg'] = 'success'
42 | res['code'] = 200
43 | res['data'] = result
44 | return json.dumps(res)
45 |
46 |
47 | if __name__ == '__main__':
48 | app.run(port=8000, host="127.0.0.1")
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/__pycache__/models.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/__pycache__/models.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/models.py:
--------------------------------------------------------------------------------
1 | from detect.utils.google_utils import *
2 | from detect.utils.layers import *
3 | from detect.utils.parse_config import *
4 |
5 | ONNX_EXPORT = False
6 |
7 |
8 | def create_modules(module_defs, img_size, cfg):
9 | # Constructs module list of layer blocks from module configuration in module_defs
10 |
11 | img_size = [img_size] * 2 if isinstance(img_size, int) else img_size # expand if necessary
12 | _ = module_defs.pop(0) # cfg training hyperparams (unused)
13 | output_filters = [3] # input channels
14 | module_list = nn.ModuleList()
15 | routs = [] # list of layers which rout to deeper layers
16 | yolo_index = -1
17 |
18 | for i, mdef in enumerate(module_defs):
19 | modules = nn.Sequential()
20 |
21 | if mdef['type'] == 'convolutional':
22 | bn = mdef['batch_normalize']
23 | filters = mdef['filters']
24 | k = mdef['size'] # kernel size
25 | stride = mdef['stride'] if 'stride' in mdef else (mdef['stride_y'], mdef['stride_x'])
26 | if isinstance(k, int): # single-size conv
27 | modules.add_module('Conv2d', nn.Conv2d(in_channels=output_filters[-1],
28 | out_channels=filters,
29 | kernel_size=k,
30 | stride=stride,
31 | padding=k // 2 if mdef['pad'] else 0,
32 | groups=mdef['groups'] if 'groups' in mdef else 1,
33 | bias=not bn))
34 | else: # multiple-size conv
35 | modules.add_module('MixConv2d', MixConv2d(in_ch=output_filters[-1],
36 | out_ch=filters,
37 | k=k,
38 | stride=stride,
39 | bias=not bn))
40 |
41 | if bn:
42 | modules.add_module('BatchNorm2d', nn.BatchNorm2d(filters, momentum=0.03, eps=1E-4))
43 | else:
44 | routs.append(i) # detection output (goes into yolo layer)
45 |
46 | if mdef['activation'] == 'leaky': # activation study https://github.com/ultralytics/yolov3/issues/441
47 | modules.add_module('activation', nn.LeakyReLU(0.1, inplace=True))
48 | elif mdef['activation'] == 'swish':
49 | modules.add_module('activation', Swish())
50 | elif mdef['activation'] == 'mish':
51 | modules.add_module('activation', Mish())
52 |
53 | elif mdef['type'] == 'BatchNorm2d':
54 | filters = output_filters[-1]
55 | modules = nn.BatchNorm2d(filters, momentum=0.03, eps=1E-4)
56 | if i == 0 and filters == 3: # normalize RGB image
57 | # imagenet mean and var https://pytorch.org/docs/stable/torchvision/models.html#classification
58 | modules.running_mean = torch.tensor([0.485, 0.456, 0.406])
59 | modules.running_var = torch.tensor([0.0524, 0.0502, 0.0506])
60 |
61 | elif mdef['type'] == 'maxpool':
62 | k = mdef['size'] # kernel size
63 | stride = mdef['stride']
64 | maxpool = nn.MaxPool2d(kernel_size=k, stride=stride, padding=(k - 1) // 2)
65 | if k == 2 and stride == 1: # yolov3-tiny
66 | modules.add_module('ZeroPad2d', nn.ZeroPad2d((0, 1, 0, 1)))
67 | modules.add_module('MaxPool2d', maxpool)
68 | else:
69 | modules = maxpool
70 |
71 | elif mdef['type'] == 'upsample':
72 | if ONNX_EXPORT: # explicitly state size, avoid scale_factor
73 | g = (yolo_index + 1) * 2 / 32 # gain
74 | modules = nn.Upsample(size=tuple(int(x * g) for x in img_size)) # img_size = (320, 192)
75 | else:
76 | modules = nn.Upsample(scale_factor=mdef['stride'])
77 |
78 | elif mdef['type'] == 'route': # nn.Sequential() placeholder for 'route' layer
79 | layers = mdef['layers']
80 | filters = sum([output_filters[l + 1 if l > 0 else l] for l in layers])
81 | routs.extend([i + l if l < 0 else l for l in layers])
82 | modules = FeatureConcat(layers=layers)
83 |
84 | elif mdef['type'] == 'shortcut': # nn.Sequential() placeholder for 'shortcut' layer
85 | layers = mdef['from']
86 | filters = output_filters[-1]
87 | routs.extend([i + l if l < 0 else l for l in layers])
88 | modules = WeightedFeatureFusion(layers=layers, weight='weights_type' in mdef)
89 |
90 | elif mdef['type'] == 'reorg3d': # yolov3-spp-pan-scale
91 | pass
92 |
93 | elif mdef['type'] == 'yolo':
94 | yolo_index += 1
95 | stride = [32, 16, 8] # P5, P4, P3 strides
96 | if 'panet' in cfg or 'yolov4' in cfg: # stride order reversed
97 | stride = list(reversed(stride))
98 | layers = mdef['from'] if 'from' in mdef else []
99 | modules = YOLOLayer(anchors=mdef['anchors'][mdef['mask']], # anchor list
100 | nc=mdef['classes'], # number of classes
101 | img_size=img_size, # (416, 416)
102 | yolo_index=yolo_index, # 0, 1, 2...
103 | layers=layers, # output layers
104 | stride=stride[yolo_index])
105 |
106 | # Initialize preceding Conv2d() bias (https://arxiv.org/pdf/1708.02002.pdf section 3.3)
107 | try:
108 | j = layers[yolo_index] if 'from' in mdef else -1
109 | bias_ = module_list[j][0].bias # shape(255,)
110 | bias = bias_[:modules.no * modules.na].view(modules.na, -1) # shape(3,85)
111 | bias[:, 4] += -4.5 # obj
112 | bias[:, 5:] += math.log(0.6 / (modules.nc - 0.99)) # cls (sigmoid(p) = 1/nc)
113 | module_list[j][0].bias = torch.nn.Parameter(bias_, requires_grad=bias_.requires_grad)
114 | except:
115 | print('WARNING: smart bias initialization failure.')
116 |
117 | else:
118 | print('Warning: Unrecognized Layer Type: ' + mdef['type'])
119 |
120 | # Register module list and number of output filters
121 | module_list.append(modules)
122 | output_filters.append(filters)
123 |
124 | routs_binary = [False] * (i + 1)
125 | for i in routs:
126 | routs_binary[i] = True
127 | return module_list, routs_binary
128 |
129 |
130 | class YOLOLayer(nn.Module):
131 | def __init__(self, anchors, nc, img_size, yolo_index, layers, stride):
132 | super(YOLOLayer, self).__init__()
133 | self.anchors = torch.Tensor(anchors)
134 | self.index = yolo_index # index of this layer in layers
135 | self.layers = layers # model output layer indices
136 | self.stride = stride # layer stride
137 | self.nl = len(layers) # number of output layers (3)
138 | self.na = len(anchors) # number of anchors (3)
139 | self.nc = nc # number of classes (80)
140 | self.no = nc + 5 # number of outputs (85)
141 | self.nx, self.ny, self.ng = 0, 0, 0 # initialize number of x, y gridpoints
142 | self.anchor_vec = self.anchors / self.stride
143 | self.anchor_wh = self.anchor_vec.view(1, self.na, 1, 1, 2)
144 |
145 | if ONNX_EXPORT:
146 | self.training = False
147 | self.create_grids((img_size[1] // stride, img_size[0] // stride)) # number x, y grid points
148 |
149 | def create_grids(self, ng=(13, 13), device='cpu'):
150 | self.nx, self.ny = ng # x and y grid size
151 | self.ng = torch.tensor(ng, dtype=torch.float)
152 |
153 | # build xy offsets
154 | if not self.training:
155 | yv, xv = torch.meshgrid([torch.arange(self.ny, device=device), torch.arange(self.nx, device=device)])
156 | self.grid = torch.stack((xv, yv), 2).view((1, 1, self.ny, self.nx, 2)).float()
157 |
158 | if self.anchor_vec.device != device:
159 | self.anchor_vec = self.anchor_vec.to(device)
160 | self.anchor_wh = self.anchor_wh.to(device)
161 |
162 | def forward(self, p, out):
163 | ASFF = False # https://arxiv.org/abs/1911.09516
164 | if ASFF:
165 | i, n = self.index, self.nl # index in layers, number of layers
166 | p = out[self.layers[i]]
167 | bs, _, ny, nx = p.shape # bs, 255, 13, 13
168 | if (self.nx, self.ny) != (nx, ny):
169 | self.create_grids((nx, ny), p.device)
170 |
171 | # outputs and weights
172 | # w = F.softmax(p[:, -n:], 1) # normalized weights
173 | w = torch.sigmoid(p[:, -n:]) * (2 / n) # sigmoid weights (faster)
174 | # w = w / w.sum(1).unsqueeze(1) # normalize across layer dimension
175 |
176 | # weighted ASFF sum
177 | p = out[self.layers[i]][:, :-n] * w[:, i:i + 1]
178 | for j in range(n):
179 | if j != i:
180 | p += w[:, j:j + 1] * \
181 | F.interpolate(out[self.layers[j]][:, :-n], size=[ny, nx], mode='bilinear', align_corners=False)
182 |
183 | elif ONNX_EXPORT:
184 | bs = 1 # batch size
185 | else:
186 | bs, _, ny, nx = p.shape # bs, 255, 13, 13
187 | if (self.nx, self.ny) != (nx, ny):
188 | self.create_grids((nx, ny), p.device)
189 |
190 | # p.view(bs, 255, 13, 13) -- > (bs, 3, 13, 13, 85) # (bs, anchors, grid, grid, classes + xywh)
191 | p = p.view(bs, self.na, self.no, self.ny, self.nx).permute(0, 1, 3, 4, 2).contiguous() # prediction
192 |
193 | if self.training:
194 | return p
195 |
196 | elif ONNX_EXPORT:
197 | # Avoid broadcasting for ANE operations
198 | m = self.na * self.nx * self.ny
199 | ng = 1. / self.ng.repeat(m, 1)
200 | grid = self.grid.repeat(1, self.na, 1, 1, 1).view(m, 2)
201 | anchor_wh = self.anchor_wh.repeat(1, 1, self.nx, self.ny, 1).view(m, 2) * ng
202 |
203 | p = p.view(m, self.no)
204 | xy = torch.sigmoid(p[:, 0:2]) + grid # x, y
205 | wh = torch.exp(p[:, 2:4]) * anchor_wh # width, height
206 | p_cls = torch.sigmoid(p[:, 4:5]) if self.nc == 1 else \
207 | torch.sigmoid(p[:, 5:self.no]) * torch.sigmoid(p[:, 4:5]) # conf
208 | return p_cls, xy * ng, wh
209 |
210 | else: # inference
211 | io = p.clone() # inference output
212 | io[..., :2] = torch.sigmoid(io[..., :2]) + self.grid # xy
213 | io[..., 2:4] = torch.exp(io[..., 2:4]) * self.anchor_wh # wh yolo method
214 | io[..., :4] *= self.stride
215 | torch.sigmoid_(io[..., 4:])
216 | return io.view(bs, -1, self.no), p # view [1, 3, 13, 13, 85] as [1, 507, 85]
217 |
218 |
219 | class Darknet(nn.Module):
220 | # YOLOv3 object detection model
221 |
222 | def __init__(self, cfg, img_size=(416, 416), verbose=False):
223 | super(Darknet, self).__init__()
224 |
225 | self.module_defs = parse_model_cfg(cfg)
226 | self.module_list, self.routs = create_modules(self.module_defs, img_size, cfg)
227 | self.yolo_layers = get_yolo_layers(self)
228 | # torch_utils.initialize_weights(self)
229 |
230 | # Darknet Header https://github.com/AlexeyAB/darknet/issues/2914#issuecomment-496675346
231 | self.version = np.array([0, 2, 5], dtype=np.int32) # (int32) version info: major, minor, revision
232 | self.seen = np.array([0], dtype=np.int64) # (int64) number of images seen during training
233 | self.info(verbose) if not ONNX_EXPORT else None # print model description
234 |
235 | def forward(self, x, augment=False, verbose=False):
236 |
237 | if not augment:
238 | return self.forward_once(x)
239 | else: # Augment images (inference and test only) https://github.com/ultralytics/yolov3/issues/931
240 | img_size = x.shape[-2:] # height, width
241 | s = [0.83, 0.67] # scales
242 | y = []
243 | for i, xi in enumerate((x,
244 | torch_utils.scale_img(x.flip(3), s[0], same_shape=False), # flip-lr and scale
245 | torch_utils.scale_img(x, s[1], same_shape=False), # scale
246 | )):
247 | # cv2.imwrite('img%g.jpg' % i, 255 * xi[0].numpy().transpose((1, 2, 0))[:, :, ::-1])
248 | y.append(self.forward_once(xi)[0])
249 |
250 | y[1][..., :4] /= s[0] # scale
251 | y[1][..., 0] = img_size[1] - y[1][..., 0] # flip lr
252 | y[2][..., :4] /= s[1] # scale
253 |
254 | # for i, yi in enumerate(y): # coco small, medium, large = < 32**2 < 96**2 <
255 | # area = yi[..., 2:4].prod(2)[:, :, None]
256 | # if i == 1:
257 | # yi *= (area < 96. ** 2).float()
258 | # elif i == 2:
259 | # yi *= (area > 32. ** 2).float()
260 | # y[i] = yi
261 |
262 | y = torch.cat(y, 1)
263 | return y, None
264 |
265 | def forward_once(self, x, augment=False, verbose=False):
266 | img_size = x.shape[-2:] # height, width
267 | yolo_out, out = [], []
268 | if verbose:
269 | print('0', x.shape)
270 | str = ''
271 |
272 | # Augment images (inference and test only)
273 | if augment: # https://github.com/ultralytics/yolov3/issues/931
274 | nb = x.shape[0] # batch size
275 | s = [0.83, 0.67] # scales
276 | x = torch.cat((x,
277 | torch_utils.scale_img(x.flip(3), s[0]), # flip-lr and scale
278 | torch_utils.scale_img(x, s[1]), # scale
279 | ), 0)
280 |
281 | for i, module in enumerate(self.module_list):
282 | name = module.__class__.__name__
283 | if name in ['WeightedFeatureFusion', 'FeatureConcat']: # sum, concat
284 | if verbose:
285 | l = [i - 1] + module.layers # layers
286 | sh = [list(x.shape)] + [list(out[i].shape) for i in module.layers] # shapes
287 | str = ' >> ' + ' + '.join(['layer %g %s' % x for x in zip(l, sh)])
288 | x = module(x, out) # WeightedFeatureFusion(), FeatureConcat()
289 | elif name == 'YOLOLayer':
290 | yolo_out.append(module(x, out))
291 | else: # run module directly, i.e. mtype = 'convolutional', 'upsample', 'maxpool', 'batchnorm2d' etc.
292 | x = module(x)
293 |
294 | out.append(x if self.routs[i] else [])
295 | if verbose:
296 | print('%g/%g %s -' % (i, len(self.module_list), name), list(x.shape), str)
297 | str = ''
298 |
299 | if self.training: # train
300 | return yolo_out
301 | elif ONNX_EXPORT: # export
302 | x = [torch.cat(x, 0) for x in zip(*yolo_out)]
303 | return x[0], torch.cat(x[1:3], 1) # scores, boxes: 3780x80, 3780x4
304 | else: # inference or test
305 | x, p = zip(*yolo_out) # inference output, training output
306 | x = torch.cat(x, 1) # cat yolo outputs
307 | if augment: # de-augment results
308 | x = torch.split(x, nb, dim=0)
309 | x[1][..., :4] /= s[0] # scale
310 | x[1][..., 0] = img_size[1] - x[1][..., 0] # flip lr
311 | x[2][..., :4] /= s[1] # scale
312 | x = torch.cat(x, 1)
313 | return x, p
314 |
315 | def fuse(self):
316 | # Fuse Conv2d + BatchNorm2d layers throughout model
317 | print('Fusing layers...')
318 | fused_list = nn.ModuleList()
319 | for a in list(self.children())[0]:
320 | if isinstance(a, nn.Sequential):
321 | for i, b in enumerate(a):
322 | if isinstance(b, nn.modules.batchnorm.BatchNorm2d):
323 | # fuse this bn layer with the previous conv2d layer
324 | conv = a[i - 1]
325 | fused = torch_utils.fuse_conv_and_bn(conv, b)
326 | a = nn.Sequential(fused, *list(a.children())[i + 1:])
327 | break
328 | fused_list.append(a)
329 | self.module_list = fused_list
330 | self.info() if not ONNX_EXPORT else None # yolov3-spp reduced from 225 to 152 layers
331 |
332 | def info(self, verbose=False):
333 | torch_utils.model_info(self, verbose)
334 |
335 |
336 | def get_yolo_layers(model):
337 | return [i for i, m in enumerate(model.module_list) if m.__class__.__name__ == 'YOLOLayer'] # [89, 101, 113]
338 |
339 |
340 | def load_darknet_weights(self, weights, cutoff=-1):
341 | # Parses and loads the weights stored in 'weights'
342 |
343 | # Establish cutoffs (load layers between 0 and cutoff. if cutoff = -1 all are loaded)
344 | file = Path(weights).name
345 | if file == 'darknet53.conv.74':
346 | cutoff = 75
347 | elif file == 'yolov3-tiny.conv.15':
348 | cutoff = 15
349 |
350 | # Read weights file
351 | with open(weights, 'rb') as f:
352 | # Read Header https://github.com/AlexeyAB/darknet/issues/2914#issuecomment-496675346
353 | self.version = np.fromfile(f, dtype=np.int32, count=3) # (int32) version info: major, minor, revision
354 | self.seen = np.fromfile(f, dtype=np.int64, count=1) # (int64) number of images seen during training
355 |
356 | weights = np.fromfile(f, dtype=np.float32) # the rest are weights
357 |
358 | ptr = 0
359 | for i, (mdef, module) in enumerate(zip(self.module_defs[:cutoff], self.module_list[:cutoff])):
360 | if mdef['type'] == 'convolutional':
361 | conv = module[0]
362 | if mdef['batch_normalize']:
363 | # Load BN bias, weights, running mean and running variance
364 | bn = module[1]
365 | nb = bn.bias.numel() # number of biases
366 | # Bias
367 | bn.bias.data.copy_(torch.from_numpy(weights[ptr:ptr + nb]).view_as(bn.bias))
368 | ptr += nb
369 | # Weight
370 | bn.weight.data.copy_(torch.from_numpy(weights[ptr:ptr + nb]).view_as(bn.weight))
371 | ptr += nb
372 | # Running Mean
373 | bn.running_mean.data.copy_(torch.from_numpy(weights[ptr:ptr + nb]).view_as(bn.running_mean))
374 | ptr += nb
375 | # Running Var
376 | bn.running_var.data.copy_(torch.from_numpy(weights[ptr:ptr + nb]).view_as(bn.running_var))
377 | ptr += nb
378 | else:
379 | # Load conv. bias
380 | nb = conv.bias.numel()
381 | conv_b = torch.from_numpy(weights[ptr:ptr + nb]).view_as(conv.bias)
382 | conv.bias.data.copy_(conv_b)
383 | ptr += nb
384 | # Load conv. weights
385 | nw = conv.weight.numel() # number of weights
386 | conv.weight.data.copy_(torch.from_numpy(weights[ptr:ptr + nw]).view_as(conv.weight))
387 | ptr += nw
388 |
389 |
390 | def save_weights(self, path='model.weights', cutoff=-1):
391 | # Converts a PyTorch model to Darket format (*.pt to *.weights)
392 | # Note: Does not work if model.fuse() is applied
393 | with open(path, 'wb') as f:
394 | # Write Header https://github.com/AlexeyAB/darknet/issues/2914#issuecomment-496675346
395 | self.version.tofile(f) # (int32) version info: major, minor, revision
396 | self.seen.tofile(f) # (int64) number of images seen during training
397 |
398 | # Iterate through layers
399 | for i, (mdef, module) in enumerate(zip(self.module_defs[:cutoff], self.module_list[:cutoff])):
400 | if mdef['type'] == 'convolutional':
401 | conv_layer = module[0]
402 | # If batch norm, load bn first
403 | if mdef['batch_normalize']:
404 | bn_layer = module[1]
405 | bn_layer.bias.data.cpu().numpy().tofile(f)
406 | bn_layer.weight.data.cpu().numpy().tofile(f)
407 | bn_layer.running_mean.data.cpu().numpy().tofile(f)
408 | bn_layer.running_var.data.cpu().numpy().tofile(f)
409 | # Load conv bias
410 | else:
411 | conv_layer.bias.data.cpu().numpy().tofile(f)
412 | # Load conv weights
413 | conv_layer.weight.data.cpu().numpy().tofile(f)
414 |
415 |
416 | def convert(cfg='cfg/yolov3-spp.cfg', weights='weights/yolov3-spp.weights'):
417 | # Converts between PyTorch and Darknet format per extension (i.e. *.weights convert to *.pt and vice versa)
418 | # from models import *; convert('cfg/yolov3-spp.cfg', 'weights/yolov3-spp.weights')
419 |
420 | # Initialize model
421 | model = Darknet(cfg)
422 |
423 | # Load weights and save
424 | if weights.endswith('.pt'): # if PyTorch format
425 | model.load_state_dict(torch.load(weights, map_location='cpu')['model'])
426 | save_weights(model, path='converted.weights', cutoff=-1)
427 | print("Success: converted '%s' to 'converted.weights'" % weights)
428 |
429 | elif weights.endswith('.weights'): # darknet format
430 | _ = load_darknet_weights(model, weights)
431 |
432 | chkpt = {'epoch': -1,
433 | 'best_fitness': None,
434 | 'training_results': None,
435 | 'model': model.state_dict(),
436 | 'optimizer': None}
437 |
438 | torch.save(chkpt, 'converted.pt')
439 | print("Success: converted '%s' to 'converted.pt'" % weights)
440 |
441 | else:
442 | print('Error: extension not supported.')
443 |
444 |
445 | def attempt_download(weights):
446 | # Attempt to download pretrained weights if not found locally
447 | weights = weights.strip()
448 | msg = weights + ' missing, try downloading from https://drive.google.com/open?id=1LezFG5g3BCW6iYaV89B2i64cqEUZD7e0'
449 |
450 | if len(weights) > 0 and not os.path.isfile(weights):
451 | d = {'yolov3-spp.weights': '16lYS4bcIdM2HdmyJBVDOvt3Trx6N3W2R',
452 | 'yolov3.weights': '1uTlyDWlnaqXcsKOktP5aH_zRDbfcDp-y',
453 | 'yolov3-tiny.weights': '1CCF-iNIIkYesIDzaPvdwlcf7H9zSsKZQ',
454 | 'yolov3-spp.pt': '1f6Ovy3BSq2wYq4UfvFUpxJFNDFfrIDcR',
455 | 'yolov3.pt': '1SHNFyoe5Ni8DajDNEqgB2oVKBb_NoEad',
456 | 'yolov3-tiny.pt': '10m_3MlpQwRtZetQxtksm9jqHrPTHZ6vo',
457 | 'darknet53.conv.74': '1WUVBid-XuoUBmvzBVUCBl_ELrzqwA8dJ',
458 | 'yolov3-tiny.conv.15': '1Bw0kCpplxUqyRYAJr9RY9SGnOJbo9nEj',
459 | 'yolov3-spp-ultralytics.pt': '1UcR-zVoMs7DH5dj3N1bswkiQTA4dmKF4'}
460 |
461 | file = Path(weights).name
462 | if file in d:
463 | r = gdrive_download(id=d[file], name=weights)
464 | else: # download from pjreddie.com
465 | url = 'https://pjreddie.com/media/files/' + file
466 | print('Downloading ' + url)
467 | r = os.system('curl -f ' + url + ' -o ' + weights)
468 |
469 | # Error check
470 | if not (r == 0 and os.path.exists(weights) and os.path.getsize(weights) > 1E6): # weights exist and > 1MB
471 | os.system('rm ' + weights) # remove partial downloads
472 | raise Exception(msg)
473 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__init__.py
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/__init__.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/__init__.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/augmentations.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/augmentations.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/datasets.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/datasets.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/google_utils.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/google_utils.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/layers.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/layers.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/parse_config.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/parse_config.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/torch_utils.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/torch_utils.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/__pycache__/utils.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/detect/utils/__pycache__/utils.cpython-36.pyc
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/adabound.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | import torch
4 | from torch.optim.optimizer import Optimizer
5 |
6 |
7 | class AdaBound(Optimizer):
8 | """Implements AdaBound algorithm.
9 | It has been proposed in `Adaptive Gradient Methods with Dynamic Bound of Learning Rate`_.
10 | Arguments:
11 | params (iterable): iterable of parameters to optimize or dicts defining
12 | parameter groups
13 | lr (float, optional): Adam learning rate (default: 1e-3)
14 | betas (Tuple[float, float], optional): coefficients used for computing
15 | running averages of gradient and its square (default: (0.9, 0.999))
16 | final_lr (float, optional): final (SGD) learning rate (default: 0.1)
17 | gamma (float, optional): convergence speed of the bound functions (default: 1e-3)
18 | eps (float, optional): term added to the denominator to improve
19 | numerical stability (default: 1e-8)
20 | weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
21 | amsbound (boolean, optional): whether to use the AMSBound variant of this algorithm
22 | .. Adaptive Gradient Methods with Dynamic Bound of Learning Rate:
23 | https://openreview.net/forum?id=Bkg3g2R9FX
24 | """
25 |
26 | def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), final_lr=0.1, gamma=1e-3,
27 | eps=1e-8, weight_decay=0, amsbound=False):
28 | if not 0.0 <= lr:
29 | raise ValueError("Invalid learning rate: {}".format(lr))
30 | if not 0.0 <= eps:
31 | raise ValueError("Invalid epsilon value: {}".format(eps))
32 | if not 0.0 <= betas[0] < 1.0:
33 | raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
34 | if not 0.0 <= betas[1] < 1.0:
35 | raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
36 | if not 0.0 <= final_lr:
37 | raise ValueError("Invalid final learning rate: {}".format(final_lr))
38 | if not 0.0 <= gamma < 1.0:
39 | raise ValueError("Invalid gamma parameter: {}".format(gamma))
40 | defaults = dict(lr=lr, betas=betas, final_lr=final_lr, gamma=gamma, eps=eps,
41 | weight_decay=weight_decay, amsbound=amsbound)
42 | super(AdaBound, self).__init__(params, defaults)
43 |
44 | self.base_lrs = list(map(lambda group: group['lr'], self.param_groups))
45 |
46 | def __setstate__(self, state):
47 | super(AdaBound, self).__setstate__(state)
48 | for group in self.param_groups:
49 | group.setdefault('amsbound', False)
50 |
51 | def step(self, closure=None):
52 | """Performs a single optimization step.
53 | Arguments:
54 | closure (callable, optional): A closure that reevaluates the model
55 | and returns the loss.
56 | """
57 | loss = None
58 | if closure is not None:
59 | loss = closure()
60 |
61 | for group, base_lr in zip(self.param_groups, self.base_lrs):
62 | for p in group['params']:
63 | if p.grad is None:
64 | continue
65 | grad = p.grad.data
66 | if grad.is_sparse:
67 | raise RuntimeError(
68 | 'Adam does not support sparse gradients, please consider SparseAdam instead')
69 | amsbound = group['amsbound']
70 |
71 | state = self.state[p]
72 |
73 | # State initialization
74 | if len(state) == 0:
75 | state['step'] = 0
76 | # Exponential moving average of gradient values
77 | state['exp_avg'] = torch.zeros_like(p.data)
78 | # Exponential moving average of squared gradient values
79 | state['exp_avg_sq'] = torch.zeros_like(p.data)
80 | if amsbound:
81 | # Maintains max of all exp. moving avg. of sq. grad. values
82 | state['max_exp_avg_sq'] = torch.zeros_like(p.data)
83 |
84 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
85 | if amsbound:
86 | max_exp_avg_sq = state['max_exp_avg_sq']
87 | beta1, beta2 = group['betas']
88 |
89 | state['step'] += 1
90 |
91 | if group['weight_decay'] != 0:
92 | grad = grad.add(group['weight_decay'], p.data)
93 |
94 | # Decay the first and second moment running average coefficient
95 | exp_avg.mul_(beta1).add_(1 - beta1, grad)
96 | exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
97 | if amsbound:
98 | # Maintains the maximum of all 2nd moment running avg. till now
99 | torch.max(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
100 | # Use the max. for normalizing running avg. of gradient
101 | denom = max_exp_avg_sq.sqrt().add_(group['eps'])
102 | else:
103 | denom = exp_avg_sq.sqrt().add_(group['eps'])
104 |
105 | bias_correction1 = 1 - beta1 ** state['step']
106 | bias_correction2 = 1 - beta2 ** state['step']
107 | step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1
108 |
109 | # Applies bounds on actual learning rate
110 | # lr_scheduler cannot affect final_lr, this is a workaround to apply lr decay
111 | final_lr = group['final_lr'] * group['lr'] / base_lr
112 | lower_bound = final_lr * (1 - 1 / (group['gamma'] * state['step'] + 1))
113 | upper_bound = final_lr * (1 + 1 / (group['gamma'] * state['step']))
114 | step_size = torch.full_like(denom, step_size)
115 | step_size.div_(denom).clamp_(lower_bound, upper_bound).mul_(exp_avg)
116 |
117 | p.data.add_(-step_size)
118 |
119 | return loss
120 |
121 |
122 | class AdaBoundW(Optimizer):
123 | """Implements AdaBound algorithm with Decoupled Weight Decay (arxiv.org/abs/1711.05101)
124 | It has been proposed in `Adaptive Gradient Methods with Dynamic Bound of Learning Rate`_.
125 | Arguments:
126 | params (iterable): iterable of parameters to optimize or dicts defining
127 | parameter groups
128 | lr (float, optional): Adam learning rate (default: 1e-3)
129 | betas (Tuple[float, float], optional): coefficients used for computing
130 | running averages of gradient and its square (default: (0.9, 0.999))
131 | final_lr (float, optional): final (SGD) learning rate (default: 0.1)
132 | gamma (float, optional): convergence speed of the bound functions (default: 1e-3)
133 | eps (float, optional): term added to the denominator to improve
134 | numerical stability (default: 1e-8)
135 | weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
136 | amsbound (boolean, optional): whether to use the AMSBound variant of this algorithm
137 | .. Adaptive Gradient Methods with Dynamic Bound of Learning Rate:
138 | https://openreview.net/forum?id=Bkg3g2R9FX
139 | """
140 |
141 | def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), final_lr=0.1, gamma=1e-3,
142 | eps=1e-8, weight_decay=0, amsbound=False):
143 | if not 0.0 <= lr:
144 | raise ValueError("Invalid learning rate: {}".format(lr))
145 | if not 0.0 <= eps:
146 | raise ValueError("Invalid epsilon value: {}".format(eps))
147 | if not 0.0 <= betas[0] < 1.0:
148 | raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
149 | if not 0.0 <= betas[1] < 1.0:
150 | raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
151 | if not 0.0 <= final_lr:
152 | raise ValueError("Invalid final learning rate: {}".format(final_lr))
153 | if not 0.0 <= gamma < 1.0:
154 | raise ValueError("Invalid gamma parameter: {}".format(gamma))
155 | defaults = dict(lr=lr, betas=betas, final_lr=final_lr, gamma=gamma, eps=eps,
156 | weight_decay=weight_decay, amsbound=amsbound)
157 | super(AdaBoundW, self).__init__(params, defaults)
158 |
159 | self.base_lrs = list(map(lambda group: group['lr'], self.param_groups))
160 |
161 | def __setstate__(self, state):
162 | super(AdaBoundW, self).__setstate__(state)
163 | for group in self.param_groups:
164 | group.setdefault('amsbound', False)
165 |
166 | def step(self, closure=None):
167 | """Performs a single optimization step.
168 | Arguments:
169 | closure (callable, optional): A closure that reevaluates the model
170 | and returns the loss.
171 | """
172 | loss = None
173 | if closure is not None:
174 | loss = closure()
175 |
176 | for group, base_lr in zip(self.param_groups, self.base_lrs):
177 | for p in group['params']:
178 | if p.grad is None:
179 | continue
180 | grad = p.grad.data
181 | if grad.is_sparse:
182 | raise RuntimeError(
183 | 'Adam does not support sparse gradients, please consider SparseAdam instead')
184 | amsbound = group['amsbound']
185 |
186 | state = self.state[p]
187 |
188 | # State initialization
189 | if len(state) == 0:
190 | state['step'] = 0
191 | # Exponential moving average of gradient values
192 | state['exp_avg'] = torch.zeros_like(p.data)
193 | # Exponential moving average of squared gradient values
194 | state['exp_avg_sq'] = torch.zeros_like(p.data)
195 | if amsbound:
196 | # Maintains max of all exp. moving avg. of sq. grad. values
197 | state['max_exp_avg_sq'] = torch.zeros_like(p.data)
198 |
199 | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
200 | if amsbound:
201 | max_exp_avg_sq = state['max_exp_avg_sq']
202 | beta1, beta2 = group['betas']
203 |
204 | state['step'] += 1
205 |
206 | # Decay the first and second moment running average coefficient
207 | exp_avg.mul_(beta1).add_(1 - beta1, grad)
208 | exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
209 | if amsbound:
210 | # Maintains the maximum of all 2nd moment running avg. till now
211 | torch.max(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
212 | # Use the max. for normalizing running avg. of gradient
213 | denom = max_exp_avg_sq.sqrt().add_(group['eps'])
214 | else:
215 | denom = exp_avg_sq.sqrt().add_(group['eps'])
216 |
217 | bias_correction1 = 1 - beta1 ** state['step']
218 | bias_correction2 = 1 - beta2 ** state['step']
219 | step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1
220 |
221 | # Applies bounds on actual learning rate
222 | # lr_scheduler cannot affect final_lr, this is a workaround to apply lr decay
223 | final_lr = group['final_lr'] * group['lr'] / base_lr
224 | lower_bound = final_lr * (1 - 1 / (group['gamma'] * state['step'] + 1))
225 | upper_bound = final_lr * (1 + 1 / (group['gamma'] * state['step']))
226 | step_size = torch.full_like(denom, step_size)
227 | step_size.div_(denom).clamp_(lower_bound, upper_bound).mul_(exp_avg)
228 |
229 | if group['weight_decay'] != 0:
230 | decayed_weights = torch.mul(p.data, group['weight_decay'])
231 | p.data.add_(-step_size)
232 | p.data.sub_(decayed_weights)
233 | else:
234 | p.data.add_(-step_size)
235 |
236 | return loss
237 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/augmentations.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 |
4 | def horisontal_flip(images, targets):
5 | images = torch.flip(images, [-1])
6 | targets[:, 2] = 1 - targets[:, 2]
7 | return images, targets
8 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/datasets.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import random
3 | import os
4 | import numpy as np
5 | from PIL import Image
6 | import torch
7 | import torch.nn.functional as F
8 | import cv2
9 | from detect.utils.augmentations import horisontal_flip
10 | from torch.utils.data import Dataset
11 | import torchvision.transforms as transforms
12 |
13 |
14 | def letterbox(img, new_shape=(416, 416), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True):
15 | # Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232
16 | shape = img.shape[:2] # current shape [height, width]
17 | if isinstance(new_shape, int):
18 | new_shape = (new_shape, new_shape)
19 |
20 | # Scale ratio (new / old)
21 | r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
22 | if not scaleup: # only scale down, do not scale up (for better test mAP)
23 | r = min(r, 1.0)
24 |
25 | # Compute padding
26 | ratio = r, r # width, height ratios
27 | new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
28 | dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
29 | if auto: # minimum rectangle
30 | dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding
31 | elif scaleFill: # stretch
32 | dw, dh = 0.0, 0.0
33 | new_unpad = new_shape
34 | ratio = new_shape[0] / shape[1], new_shape[1] / shape[0] # width, height ratios
35 |
36 | dw /= 2 # divide padding into 2 sides
37 | dh /= 2
38 |
39 | if shape[::-1] != new_unpad: # resize
40 | img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
41 | top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
42 | left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
43 | img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
44 | return img, ratio, (dw, dh)
45 |
46 |
47 | def pad_to_square(img, pad_value):
48 | c, h, w = img.shape
49 | dim_diff = np.abs(h - w)
50 | # (upper / left) padding and (lower / right) padding
51 | pad1, pad2 = dim_diff // 2, dim_diff - dim_diff // 2
52 | # Determine padding
53 | pad = (0, 0, pad1, pad2) if h <= w else (pad1, pad2, 0, 0)
54 | # Add padding
55 | img = F.pad(img, pad, "constant", value=pad_value)
56 |
57 | return img, pad
58 |
59 |
60 | def resize(image, size):
61 | image = F.interpolate(image.unsqueeze(0), size=size, mode="nearest").squeeze(0)
62 | return image
63 |
64 |
65 | def random_resize(images, min_size=288, max_size=448):
66 | new_size = random.sample(list(range(min_size, max_size + 1, 32)), 1)[0]
67 | images = F.interpolate(images, size=new_size, mode="nearest")
68 | return images
69 |
70 |
71 | class ImageFolder(Dataset):
72 | def __init__(self, folder_path, img_size=416):
73 | self.files = sorted(glob.glob("%s/*.*" % folder_path))
74 | self.img_size = img_size
75 |
76 | def __getitem__(self, index):
77 | img_path = self.files[index % len(self.files)]
78 | # Extract image as PyTorch tensor
79 | img = transforms.ToTensor()(Image.open(img_path))
80 | # Pad to square resolution
81 | img, _ = pad_to_square(img, 0)
82 | # Resize
83 | img = resize(img, self.img_size)
84 |
85 | return img_path, img
86 |
87 | def __len__(self):
88 | return len(self.files)
89 |
90 |
91 | class ListDataset(Dataset):
92 | def __init__(self, list_path, img_size=416, augment=True, multiscale=True, normalized_labels=True):
93 | with open(list_path, "r") as file:
94 | self.img_files = file.readlines()
95 |
96 | self.label_files = [
97 | path.replace("images", "labels").replace(".png", ".txt").replace(".jpg", ".txt")
98 | for path in self.img_files
99 | ]
100 | self.img_size = img_size
101 | self.max_objects = 100
102 | self.augment = augment
103 | self.multiscale = multiscale
104 | self.normalized_labels = normalized_labels
105 | self.min_size = self.img_size - 3 * 32
106 | self.max_size = self.img_size + 3 * 32
107 | self.batch_count = 0
108 |
109 | def __getitem__(self, index):
110 |
111 | # ---------
112 | # Image
113 | # ---------
114 |
115 | img_path = self.img_files[index % len(self.img_files)].rstrip()
116 |
117 | # Extract image as PyTorch tensor
118 | img = transforms.ToTensor()(Image.open(img_path).convert('RGB'))
119 |
120 | # Handle images with less than three channels
121 | if len(img.shape) != 3:
122 | img = img.unsqueeze(0)
123 | img = img.expand((3, img.shape[1:]))
124 |
125 | _, h, w = img.shape
126 | h_factor, w_factor = (h, w) if self.normalized_labels else (1, 1)
127 | # Pad to square resolution
128 | img, pad = pad_to_square(img, 0)
129 | _, padded_h, padded_w = img.shape
130 |
131 | # ---------
132 | # Label
133 | # ---------
134 |
135 | label_path = self.label_files[index % len(self.img_files)].rstrip()
136 |
137 | targets = None
138 | if os.path.exists(label_path):
139 | boxes = torch.from_numpy(np.loadtxt(label_path).reshape(-1, 5))
140 | # Extract coordinates for unpadded + unscaled image
141 | x1 = w_factor * (boxes[:, 1] - boxes[:, 3] / 2)
142 | y1 = h_factor * (boxes[:, 2] - boxes[:, 4] / 2)
143 | x2 = w_factor * (boxes[:, 1] + boxes[:, 3] / 2)
144 | y2 = h_factor * (boxes[:, 2] + boxes[:, 4] / 2)
145 | # Adjust for added padding
146 | x1 += pad[0]
147 | y1 += pad[2]
148 | x2 += pad[1]
149 | y2 += pad[3]
150 | # Returns (x, y, w, h)
151 | boxes[:, 1] = ((x1 + x2) / 2) / padded_w
152 | boxes[:, 2] = ((y1 + y2) / 2) / padded_h
153 | boxes[:, 3] *= w_factor / padded_w
154 | boxes[:, 4] *= h_factor / padded_h
155 |
156 | targets = torch.zeros((len(boxes), 6))
157 | targets[:, 1:] = boxes
158 |
159 | # Apply augmentations
160 | if self.augment:
161 | if np.random.random() < 0.5:
162 | img, targets = horisontal_flip(img, targets)
163 |
164 | return img_path, img, targets
165 |
166 | def collate_fn(self, batch):
167 | paths, imgs, targets = list(zip(*batch))
168 | # Remove empty placeholder targets
169 | targets = [boxes for boxes in targets if boxes is not None]
170 | # Add sample index to targets
171 | for i, boxes in enumerate(targets):
172 | boxes[:, 0] = i
173 | targets = torch.cat(targets, 0)
174 | # Selects new image size every tenth batch
175 | if self.multiscale and self.batch_count % 10 == 0:
176 | self.img_size = random.choice(range(self.min_size, self.max_size + 1, 32))
177 | # Resize images to input shape
178 | imgs = torch.stack([resize(img, self.img_size) for img in imgs])
179 | self.batch_count += 1
180 | return paths, imgs, targets
181 |
182 | def __len__(self):
183 | return len(self.img_files)
184 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/evolve.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #for i in 0 1 2 3
3 | #do
4 | # t=ultralytics/yolov3:v139 && sudo docker pull $t && sudo nvidia-docker run -d --ipc=host -v "$(pwd)"/coco:/usr/src/coco $t utils/evolve.sh $i
5 | # sleep 30
6 | #done
7 |
8 | while true; do
9 | # python3 train.py --data ../data/sm4/out.data --img-size 320 --epochs 100 --batch 64 --accum 1 --weights yolov3-tiny.conv.15 --multi --bucket ult/wer --evolve --cache --device $1 --cfg yolov3-tiny3-1cls.cfg --single --adam
10 | # python3 train.py --data ../out/data.data --img-size 608 --epochs 10 --batch 8 --accum 8 --weights ultralytics68.pt --multi --bucket ult/athena --evolve --device $1 --cfg yolov3-spp-1cls.cfg
11 |
12 | python3 train.py --data coco2014.data --img-size 512 608 --epochs 27 --batch 8 --accum 8 --evolve --weights '' --bucket ult/coco/sppa_512 --device $1 --cfg yolov3-sppa.cfg --multi
13 | done
14 |
15 |
16 | # coco epoch times --img-size 416 608 --epochs 27 --batch 16 --accum 4
17 | # 36:34 2080ti
18 | # 21:58 V100
19 | # 63:00 T4
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/gcp.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # New VM
4 | rm -rf sample_data yolov3
5 | git clone https://github.com/ultralytics/yolov3
6 | # git clone -b test --depth 1 https://github.com/ultralytics/yolov3 test # branch
7 | # sudo apt-get install zip
8 | #git clone https://github.com/NVIDIA/apex && cd apex && pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" . --user && cd .. && rm -rf apex
9 | sudo conda install -yc conda-forge scikit-image pycocotools
10 | # python3 -c "from yolov3.utils.google_utils import gdrive_download; gdrive_download('193Zp_ye-3qXMonR1nZj3YyxMtQkMy50k','coco2014.zip')"
11 | python3 -c "from yolov3.utils.google_utils import gdrive_download; gdrive_download('1WQT6SOktSe8Uw6r10-2JhbEhMY5DJaph','coco2017.zip')"
12 | python3 -c "from yolov3.utils.google_utils import gdrive_download; gdrive_download('1C3HewOG9akA3y456SZLBJZfNDPkBwAto','knife.zip')"
13 | python3 -c "from yolov3.utils.google_utils import gdrive_download; gdrive_download('13g3LqdpkNE8sPosVJT6KFXlfoMypzRP4','sm4.zip')"
14 | sudo shutdown
15 |
16 | # Mount local SSD
17 | lsblk
18 | sudo mkfs.ext4 -F /dev/nvme0n1
19 | sudo mkdir -p /mnt/disks/nvme0n1
20 | sudo mount /dev/nvme0n1 /mnt/disks/nvme0n1
21 | sudo chmod a+w /mnt/disks/nvme0n1
22 | cp -r coco /mnt/disks/nvme0n1
23 |
24 | # Kill All
25 | t=ultralytics/yolov3:v1
26 | docker kill $(docker ps -a -q --filter ancestor=$t)
27 |
28 | # Evolve coco
29 | sudo -s
30 | t=ultralytics/yolov3:evolve
31 | # docker kill $(docker ps -a -q --filter ancestor=$t)
32 | for i in 0 1 6 7
33 | do
34 | docker pull $t && docker run --gpus all -d --ipc=host -v "$(pwd)"/coco:/usr/src/coco $t bash utils/evolve.sh $i
35 | sleep 30
36 | done
37 |
38 | #COCO training
39 | n=131 && t=ultralytics/coco:v131 && sudo docker pull $t && sudo docker run -it --gpus all --ipc=host -v "$(pwd)"/coco:/usr/src/coco $t python3 train.py --data coco2014.data --img-size 320 640 --epochs 300 --batch 16 --weights '' --device 0 --cfg yolov3-spp.cfg --bucket ult/coco --name $n && sudo shutdown
40 | n=132 && t=ultralytics/coco:v131 && sudo docker pull $t && sudo docker run -it --gpus all --ipc=host -v "$(pwd)"/coco:/usr/src/coco $t python3 train.py --data coco2014.data --img-size 320 640 --epochs 300 --batch 64 --weights '' --device 0 --cfg yolov3-tiny.cfg --bucket ult/coco --name $n && sudo shutdown
41 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/google_utils.py:
--------------------------------------------------------------------------------
1 | # This file contains google utils: https://cloud.google.com/storage/docs/reference/libraries
2 | # pip install --upgrade google-cloud-storage
3 |
4 | import os
5 | import time
6 |
7 |
8 | # from google.cloud import storage
9 |
10 |
11 | def gdrive_download(id='1HaXkef9z6y5l4vUnCYgdmEAj61c6bfWO', name='coco.zip'):
12 | # https://gist.github.com/tanaikech/f0f2d122e05bf5f971611258c22c110f
13 | # Downloads a file from Google Drive, accepting presented query
14 | # from utils.google_utils import *; gdrive_download()
15 | t = time.time()
16 |
17 | print('Downloading https://drive.google.com/uc?export=download&id=%s as %s... ' % (id, name), end='')
18 | os.remove(name) if os.path.exists(name) else None # remove existing
19 | os.remove('cookie') if os.path.exists('cookie') else None
20 |
21 | # Attempt file download
22 | os.system("curl -c ./cookie -s -L \"https://drive.google.com/uc?export=download&id=%s\" > /dev/null" % id)
23 | if os.path.exists('cookie'): # large file
24 | s = "curl -Lb ./cookie \"https://drive.google.com/uc?export=download&confirm=`awk '/download/ {print $NF}' ./cookie`&id=%s\" -o %s" % (
25 | id, name)
26 | else: # small file
27 | s = "curl -s -L -o %s 'https://drive.google.com/uc?export=download&id=%s'" % (name, id)
28 | r = os.system(s) # execute, capture return values
29 | os.remove('cookie') if os.path.exists('cookie') else None
30 |
31 | # Error check
32 | if r != 0:
33 | os.remove(name) if os.path.exists(name) else None # remove partial
34 | print('Download error ') # raise Exception('Download error')
35 | return r
36 |
37 | # Unzip if archive
38 | if name.endswith('.zip'):
39 | print('unzipping... ', end='')
40 | os.system('unzip -q %s' % name) # unzip
41 | os.remove(name) # remove zip to free space
42 |
43 | print('Done (%.1fs)' % (time.time() - t))
44 | return r
45 |
46 |
47 | def upload_blob(bucket_name, source_file_name, destination_blob_name):
48 | # Uploads a file to a bucket
49 | # https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-python
50 |
51 | storage_client = storage.Client()
52 | bucket = storage_client.get_bucket(bucket_name)
53 | blob = bucket.blob(destination_blob_name)
54 |
55 | blob.upload_from_filename(source_file_name)
56 |
57 | print('File {} uploaded to {}.'.format(
58 | source_file_name,
59 | destination_blob_name))
60 |
61 |
62 | def download_blob(bucket_name, source_blob_name, destination_file_name):
63 | # Uploads a blob from a bucket
64 | storage_client = storage.Client()
65 | bucket = storage_client.get_bucket(bucket_name)
66 | blob = bucket.blob(source_blob_name)
67 |
68 | blob.download_to_filename(destination_file_name)
69 |
70 | print('Blob {} downloaded to {}.'.format(
71 | source_blob_name,
72 | destination_file_name))
73 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/layers.py:
--------------------------------------------------------------------------------
1 | import torch.nn.functional as F
2 |
3 | from detect.utils.utils import *
4 |
5 |
6 | def make_divisible(v, divisor):
7 | # Function ensures all layers have a channel number that is divisible by 8
8 | # https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
9 | return math.ceil(v / divisor) * divisor
10 |
11 |
12 | class Flatten(nn.Module):
13 | # Use after nn.AdaptiveAvgPool2d(1) to remove last 2 dimensions
14 | def forward(self, x):
15 | return x.view(x.size(0), -1)
16 |
17 |
18 | class Concat(nn.Module):
19 | # Concatenate a list of tensors along dimension
20 | def __init__(self, dimension=1):
21 | super(Concat, self).__init__()
22 | self.d = dimension
23 |
24 | def forward(self, x):
25 | return torch.cat(x, self.d)
26 |
27 |
28 | class FeatureConcat(nn.Module):
29 | def __init__(self, layers):
30 | super(FeatureConcat, self).__init__()
31 | self.layers = layers # layer indices
32 | self.multiple = len(layers) > 1 # multiple layers flag
33 |
34 | def forward(self, x, outputs):
35 | return torch.cat([outputs[i] for i in self.layers], 1) if self.multiple else outputs[self.layers[0]]
36 |
37 |
38 | class WeightedFeatureFusion(nn.Module): # weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070
39 | def __init__(self, layers, weight=False):
40 | super(WeightedFeatureFusion, self).__init__()
41 | self.layers = layers # layer indices
42 | self.weight = weight # apply weights boolean
43 | self.n = len(layers) + 1 # number of layers
44 | if weight:
45 | self.w = nn.Parameter(torch.zeros(self.n), requires_grad=True) # layer weights
46 |
47 | def forward(self, x, outputs):
48 | # Weights
49 | if self.weight:
50 | w = torch.sigmoid(self.w) * (2 / self.n) # sigmoid weights (0-1)
51 | x = x * w[0]
52 |
53 | # Fusion
54 | nx = x.shape[1] # input channels
55 | for i in range(self.n - 1):
56 | a = outputs[self.layers[i]] * w[i + 1] if self.weight else outputs[self.layers[i]] # feature to add
57 | na = a.shape[1] # feature channels
58 |
59 | # Adjust channels
60 | if nx == na: # same shape
61 | x = x + a
62 | elif nx > na: # slice input
63 | x[:, :na] = x[:, :na] + a # or a = nn.ZeroPad2d((0, 0, 0, 0, 0, dc))(a); x = x + a
64 | else: # slice feature
65 | x = x + a[:, :nx]
66 |
67 | return x
68 |
69 |
70 | class MixConv2d(nn.Module): # MixConv: Mixed Depthwise Convolutional Kernels https://arxiv.org/abs/1907.09595
71 | def __init__(self, in_ch, out_ch, k=(3, 5, 7), stride=1, dilation=1, bias=True, method='equal_params'):
72 | super(MixConv2d, self).__init__()
73 |
74 | groups = len(k)
75 | if method == 'equal_ch': # equal channels per group
76 | i = torch.linspace(0, groups - 1E-6, out_ch).floor() # out_ch indices
77 | ch = [(i == g).sum() for g in range(groups)]
78 | else: # 'equal_params': equal parameter count per group
79 | b = [out_ch] + [0] * groups
80 | a = np.eye(groups + 1, groups, k=-1)
81 | a -= np.roll(a, 1, axis=1)
82 | a *= np.array(k) ** 2
83 | a[0] = 1
84 | ch = np.linalg.lstsq(a, b, rcond=None)[0].round().astype(int) # solve for equal weight indices, ax = b
85 |
86 | self.m = nn.ModuleList([nn.Conv2d(in_channels=in_ch,
87 | out_channels=ch[g],
88 | kernel_size=k[g],
89 | stride=stride,
90 | padding=k[g] // 2, # 'same' pad
91 | dilation=dilation,
92 | bias=bias) for g in range(groups)])
93 |
94 | def forward(self, x):
95 | return torch.cat([m(x) for m in self.m], 1)
96 |
97 |
98 | # Activation functions below -------------------------------------------------------------------------------------------
99 | class SwishImplementation(torch.autograd.Function):
100 | @staticmethod
101 | def forward(ctx, x):
102 | ctx.save_for_backward(x)
103 | return x * torch.sigmoid(x)
104 |
105 | @staticmethod
106 | def backward(ctx, grad_output):
107 | x = ctx.saved_tensors[0]
108 | sx = torch.sigmoid(x) # sigmoid(ctx)
109 | return grad_output * (sx * (1 + x * (1 - sx)))
110 |
111 |
112 | class MishImplementation(torch.autograd.Function):
113 | @staticmethod
114 | def forward(ctx, x):
115 | ctx.save_for_backward(x)
116 | return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x)))
117 |
118 | @staticmethod
119 | def backward(ctx, grad_output):
120 | x = ctx.saved_tensors[0]
121 | sx = torch.sigmoid(x)
122 | fx = F.softplus(x).tanh()
123 | return grad_output * (fx + x * sx * (1 - fx * fx))
124 |
125 |
126 | class MemoryEfficientSwish(nn.Module):
127 | def forward(self, x):
128 | return SwishImplementation.apply(x)
129 |
130 |
131 | class MemoryEfficientMish(nn.Module):
132 | def forward(self, x):
133 | return MishImplementation.apply(x)
134 |
135 |
136 | class Swish(nn.Module):
137 | def forward(self, x):
138 | return x * torch.sigmoid(x)
139 |
140 |
141 | class HardSwish(nn.Module): # https://arxiv.org/pdf/1905.02244.pdf
142 | def forward(self, x):
143 | return x * F.hardtanh(x + 3, 0., 6., True) / 6.
144 |
145 |
146 | class Mish(nn.Module): # https://github.com/digantamisra98/Mish
147 | def forward(self, x):
148 | return x * F.softplus(x).tanh()
149 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/parse_config.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import numpy as np
4 |
5 |
6 | def parse_model_cfg(path):
7 | # Parse the yolo *.cfg file and return module definitions path may be 'cfg/yolov3.cfg', 'yolov3.cfg', or 'yolov3'
8 | if not path.endswith('.cfg'): # add .cfg suffix if omitted
9 | path += '.cfg'
10 | if not os.path.exists(path) and os.path.exists('cfg' + os.sep + path): # add cfg/ prefix if omitted
11 | path = 'cfg' + os.sep + path
12 |
13 | with open(path, 'r') as f:
14 | lines = f.read().split('\n')
15 | lines = [x for x in lines if x and not x.startswith('#')]
16 | lines = [x.rstrip().lstrip() for x in lines] # get rid of fringe whitespaces
17 | mdefs = [] # module definitions
18 | for line in lines:
19 | if line.startswith('['): # This marks the start of a new block
20 | mdefs.append({})
21 | mdefs[-1]['type'] = line[1:-1].rstrip()
22 | if mdefs[-1]['type'] == 'convolutional':
23 | mdefs[-1]['batch_normalize'] = 0 # pre-populate with zeros (may be overwritten later)
24 | else:
25 | key, val = line.split("=")
26 | key = key.rstrip()
27 |
28 | if key == 'anchors': # return nparray
29 | mdefs[-1][key] = np.array([float(x) for x in val.split(',')]).reshape((-1, 2)) # np anchors
30 | elif (key in ['from', 'layers', 'mask']) or (key == 'size' and ',' in val): # return array
31 | mdefs[-1][key] = [int(x) for x in val.split(',')]
32 | else:
33 | val = val.strip()
34 | if val.isnumeric(): # return int or float
35 | mdefs[-1][key] = int(val) if (int(val) - float(val)) == 0 else float(val)
36 | else:
37 | mdefs[-1][key] = val # return string
38 |
39 | # Check all fields are supported
40 | supported = ['type', 'batch_normalize', 'filters', 'size', 'stride', 'pad', 'activation', 'layers', 'groups',
41 | 'from', 'mask', 'anchors', 'classes', 'num', 'jitter', 'ignore_thresh', 'truth_thresh', 'random',
42 | 'stride_x', 'stride_y', 'weights_type', 'weights_normalization', 'scale_x_y', 'beta_nms', 'nms_kind',
43 | 'iou_loss', 'iou_normalizer', 'cls_normalizer', 'iou_thresh']
44 |
45 | f = [] # fields
46 | for x in mdefs[1:]:
47 | [f.append(k) for k in x if k not in f]
48 | u = [x for x in f if x not in supported] # unsupported fields
49 | assert not any(u), "Unsupported fields %s in %s. See https://github.com/ultralytics/yolov3/issues/631" % (u, path)
50 |
51 | return mdefs
52 |
53 |
54 | def parse_data_cfg(path):
55 | # Parses the data configuration file
56 | if not os.path.exists(path) and os.path.exists('data' + os.sep + path): # add data/ prefix if omitted
57 | path = 'data' + os.sep + path
58 |
59 | with open(path, 'r') as f:
60 | lines = f.readlines()
61 |
62 | options = dict()
63 | for line in lines:
64 | line = line.strip()
65 | if line == '' or line.startswith('#'):
66 | continue
67 | key, val = line.split('=')
68 | options[key.strip()] = val.strip()
69 |
70 | return options
71 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/torch_utils.py:
--------------------------------------------------------------------------------
1 | import math
2 | import os
3 | import time
4 | from copy import deepcopy
5 |
6 | import torch
7 | import torch.backends.cudnn as cudnn
8 | import torch.nn as nn
9 | import torch.nn.functional as F
10 |
11 |
12 | def init_seeds(seed=0):
13 | torch.manual_seed(seed)
14 |
15 | # Remove randomness (may be slower on Tesla GPUs) # https://pytorch.org/docs/stable/notes/randomness.html
16 | if seed == 0:
17 | cudnn.deterministic = True
18 | cudnn.benchmark = False
19 |
20 |
21 | def select_device(device='', apex=False, batch_size=None):
22 | # device = 'cpu' or '0' or '0,1,2,3'
23 | cpu_request = device.lower() == 'cpu'
24 | if device and not cpu_request: # if device requested other than 'cpu'
25 | os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable
26 | assert torch.cuda.is_available(), 'CUDA unavailable, invalid device %s requested' % device # check availablity
27 |
28 | cuda = False if cpu_request else torch.cuda.is_available()
29 | if cuda:
30 | c = 1024 ** 2 # bytes to MB
31 | ng = torch.cuda.device_count()
32 | if ng > 1 and batch_size: # check that batch_size is compatible with device_count
33 | assert batch_size % ng == 0, 'batch-size %g not multiple of GPU count %g' % (batch_size, ng)
34 | x = [torch.cuda.get_device_properties(i) for i in range(ng)]
35 | s = 'Using CUDA ' + ('Apex ' if apex else '') # apex for mixed precision https://github.com/NVIDIA/apex
36 | for i in range(0, ng):
37 | if i == 1:
38 | s = ' ' * len(s)
39 | print("%sdevice%g _CudaDeviceProperties(name='%s', total_memory=%dMB)" %
40 | (s, i, x[i].name, x[i].total_memory / c))
41 | else:
42 | print('Using CPU')
43 |
44 | print('') # skip a line
45 | return torch.device('cuda:0' if cuda else 'cpu')
46 |
47 |
48 | def time_synchronized():
49 | torch.cuda.synchronize() if torch.cuda.is_available() else None
50 | return time.time()
51 |
52 |
53 | def initialize_weights(model):
54 | for m in model.modules():
55 | t = type(m)
56 | if t is nn.Conv2d:
57 | pass # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
58 | elif t is nn.BatchNorm2d:
59 | m.eps = 1e-4
60 | m.momentum = 0.03
61 | elif t in [nn.LeakyReLU, nn.ReLU, nn.ReLU6]:
62 | m.inplace = True
63 |
64 |
65 | def find_modules(model, mclass=nn.Conv2d):
66 | # finds layer indices matching module class 'mclass'
67 | return [i for i, m in enumerate(model.module_list) if isinstance(m, mclass)]
68 |
69 |
70 | def fuse_conv_and_bn(conv, bn):
71 | # https://tehnokv.com/posts/fusing-batchnorm-and-conv/
72 | with torch.no_grad():
73 | # init
74 | fusedconv = torch.nn.Conv2d(conv.in_channels,
75 | conv.out_channels,
76 | kernel_size=conv.kernel_size,
77 | stride=conv.stride,
78 | padding=conv.padding,
79 | bias=True)
80 |
81 | # prepare filters
82 | w_conv = conv.weight.clone().view(conv.out_channels, -1)
83 | w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var)))
84 | fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.size()))
85 |
86 | # prepare spatial bias
87 | if conv.bias is not None:
88 | b_conv = conv.bias
89 | else:
90 | b_conv = torch.zeros(conv.weight.size(0))
91 | b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps))
92 | fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn)
93 |
94 | return fusedconv
95 |
96 |
97 | def model_info(model, verbose=False):
98 | # Plots a line-by-line description of a PyTorch model
99 | n_p = sum(x.numel() for x in model.parameters()) # number parameters
100 | n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients
101 | if verbose:
102 | print('%5s %40s %9s %12s %20s %10s %10s' % ('layer', 'name', 'gradient', 'parameters', 'shape', 'mu', 'sigma'))
103 | for i, (name, p) in enumerate(model.named_parameters()):
104 | name = name.replace('module_list.', '')
105 | print('%5g %40s %9s %12g %20s %10.3g %10.3g' %
106 | (i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std()))
107 |
108 | try: # FLOPS
109 | from thop import profile
110 | macs, _ = profile(model, inputs=(torch.zeros(1, 3, 480, 640),), verbose=False)
111 | fs = ', %.1f GFLOPS' % (macs / 1E9 * 2)
112 | except:
113 | fs = ''
114 |
115 | print('Model Summary: %g layers, %g parameters, %g gradients%s' % (len(list(model.parameters())), n_p, n_g, fs))
116 |
117 |
118 | def load_classifier(name='resnet101', n=2):
119 | # Loads a pretrained model reshaped to n-class output
120 | import pretrainedmodels # https://github.com/Cadene/pretrained-models.pytorch#torchvision
121 | model = pretrainedmodels.__dict__[name](num_classes=1000, pretrained='imagenet')
122 |
123 | # Display model properties
124 | for x in ['model.input_size', 'model.input_space', 'model.input_range', 'model.mean', 'model.std']:
125 | print(x + ' =', eval(x))
126 |
127 | # Reshape output to n classes
128 | filters = model.last_linear.weight.shape[1]
129 | model.last_linear.bias = torch.nn.Parameter(torch.zeros(n))
130 | model.last_linear.weight = torch.nn.Parameter(torch.zeros(n, filters))
131 | model.last_linear.out_features = n
132 | return model
133 |
134 |
135 | def scale_img(img, ratio=1.0, same_shape=True): # img(16,3,256,416), r=ratio
136 | # scales img(bs,3,y,x) by ratio
137 | h, w = img.shape[2:]
138 | s = (int(h * ratio), int(w * ratio)) # new size
139 | img = F.interpolate(img, size=s, mode='bilinear', align_corners=False) # resize
140 | if not same_shape: # pad/crop img
141 | gs = 64 # (pixels) grid size
142 | h, w = [math.ceil(x * ratio / gs) * gs for x in (h, w)]
143 | return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447) # value = imagenet mean
144 |
145 |
146 | class ModelEMA:
147 | """ Model Exponential Moving Average from https://github.com/rwightman/pytorch-image-models
148 | Keep a moving average of everything in the model state_dict (parameters and buffers).
149 | This is intended to allow functionality like
150 | https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage
151 | A smoothed version of the weights is necessary for some training schemes to perform well.
152 | E.g. Google's hyper-params for training MNASNet, MobileNet-V3, EfficientNet, etc that use
153 | RMSprop with a short 2.4-3 epoch decay period and slow LR decay rate of .96-.99 requires EMA
154 | smoothing of weights to match results. Pay attention to the decay constant you are using
155 | relative to your update count per epoch.
156 | To keep EMA from using GPU resources, set device='cpu'. This will save a bit of memory but
157 | disable validation of the EMA weights. Validation will have to be done manually in a separate
158 | process, or after the training stops converging.
159 | This class is sensitive where it is initialized in the sequence of model init,
160 | GPU assignment and distributed training wrappers.
161 | I've tested with the sequence in my own train.py for torch.DataParallel, apex.DDP, and single-GPU.
162 | """
163 |
164 | def __init__(self, model, decay=0.9999, device=''):
165 | # make a copy of the model for accumulating moving average of weights
166 | self.ema = deepcopy(model)
167 | self.ema.eval()
168 | self.updates = 0 # number of EMA updates
169 | self.decay = lambda x: decay * (1 - math.exp(-x / 2000)) # decay exponential ramp (to help early epochs)
170 | self.device = device # perform ema on different device from model if set
171 | if device:
172 | self.ema.to(device=device)
173 | for p in self.ema.parameters():
174 | p.requires_grad_(False)
175 |
176 | def update(self, model):
177 | self.updates += 1
178 | d = self.decay(self.updates)
179 | with torch.no_grad():
180 | if type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel):
181 | msd, esd = model.module.state_dict(), self.ema.module.state_dict()
182 | else:
183 | msd, esd = model.state_dict(), self.ema.state_dict()
184 |
185 | for k, v in esd.items():
186 | if v.dtype.is_floating_point:
187 | v *= d
188 | v += (1. - d) * msd[k].detach()
189 |
190 | def update_attr(self, model):
191 | # Assign attributes (which may change during training)
192 | for k in model.__dict__.keys():
193 | if not k.startswith('_'):
194 | setattr(self.ema, k, getattr(model, k))
195 |
--------------------------------------------------------------------------------
/Yundun_chinese/detect/utils/utils.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import math
3 | import os
4 | import random
5 | import shutil
6 | import subprocess
7 | from pathlib import Path
8 |
9 | import cv2
10 | import matplotlib
11 | import matplotlib.pyplot as plt
12 | import numpy as np
13 | import torch
14 | import torch.nn as nn
15 | import torchvision
16 | from tqdm import tqdm
17 |
18 | from . import torch_utils # , google_utils
19 |
20 | # Set printoptions
21 | torch.set_printoptions(linewidth=320, precision=5, profile='long')
22 | np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
23 | matplotlib.rc('font', **{'size': 11})
24 |
25 | # Prevent OpenCV from multithreading (to use PyTorch DataLoader)
26 | cv2.setNumThreads(0)
27 |
28 |
29 | def init_seeds(seed=0):
30 | random.seed(seed)
31 | np.random.seed(seed)
32 | torch_utils.init_seeds(seed=seed)
33 |
34 |
35 | def rescale_boxes(boxes, current_dim, original_shape):
36 | """ Rescales bounding boxes to the original shape """
37 | orig_h, orig_w = original_shape
38 | # The amount of padding that was added
39 | pad_x = max(orig_h - orig_w, 0) * (current_dim / max(original_shape))
40 | pad_y = max(orig_w - orig_h, 0) * (current_dim / max(original_shape))
41 | # Image height and width after padding is removed
42 | unpad_h = current_dim - pad_y
43 | unpad_w = current_dim - pad_x
44 | # Rescale bounding boxes to dimension of original image
45 | boxes[:, 0] = ((boxes[:, 0] - pad_x // 2) / unpad_w) * orig_w
46 | boxes[:, 1] = ((boxes[:, 1] - pad_y // 2) / unpad_h) * orig_h
47 | boxes[:, 2] = ((boxes[:, 2] - pad_x // 2) / unpad_w) * orig_w
48 | boxes[:, 3] = ((boxes[:, 3] - pad_y // 2) / unpad_h) * orig_h
49 | return boxes
50 |
51 | def check_git_status():
52 | # Suggest 'git pull' if repo is out of date
53 | # s = subprocess.check_output('if [ -d .git ]; then git fetch && git status -uno; fi', shell=True).decode('utf-8')
54 | # if 'Your branch is behind' in s:
55 | # print(s[s.find('Your branch is behind'):s.find('\n\n')] + '\n')
56 | pass
57 |
58 | def load_classes(path):
59 | # Loads *.names file at 'path'
60 | with open(path, 'r') as f:
61 | names = f.read().split('\n')
62 | return list(filter(None, names)) # filter removes empty strings (such as last line)
63 |
64 |
65 | def labels_to_class_weights(labels, nc=80):
66 | # Get class weights (inverse frequency) from training labels
67 | if labels[0] is None: # no labels loaded
68 | return torch.Tensor()
69 |
70 | labels = np.concatenate(labels, 0) # labels.shape = (866643, 5) for COCO
71 | classes = labels[:, 0].astype(np.int) # labels = [class xywh]
72 | weights = np.bincount(classes, minlength=nc) # occurences per class
73 |
74 | # Prepend gridpoint count (for uCE trianing)
75 | # gpi = ((320 / 32 * np.array([1, 2, 4])) ** 2 * 3).sum() # gridpoints per image
76 | # weights = np.hstack([gpi * len(labels) - weights.sum() * 9, weights * 9]) ** 0.5 # prepend gridpoints to start
77 |
78 | weights[weights == 0] = 1 # replace empty bins with 1
79 | weights = 1 / weights # number of targets per class
80 | weights /= weights.sum() # normalize
81 | return torch.from_numpy(weights)
82 |
83 |
84 | def labels_to_image_weights(labels, nc=80, class_weights=np.ones(80)):
85 | # Produces image weights based on class mAPs
86 | n = len(labels)
87 | class_counts = np.array([np.bincount(labels[i][:, 0].astype(np.int), minlength=nc) for i in range(n)])
88 | image_weights = (class_weights.reshape(1, nc) * class_counts).sum(1)
89 | # index = random.choices(range(n), weights=image_weights, k=1) # weight image sample
90 | return image_weights
91 |
92 |
93 | def coco_class_weights(): # frequency of each class in coco train2014
94 | n = [187437, 4955, 30920, 6033, 3838, 4332, 3160, 7051, 7677, 9167, 1316, 1372, 833, 6757, 7355, 3302, 3776, 4671,
95 | 6769, 5706, 3908, 903, 3686, 3596, 6200, 7920, 8779, 4505, 4272, 1862, 4698, 1962, 4403, 6659, 2402, 2689,
96 | 4012, 4175, 3411, 17048, 5637, 14553, 3923, 5539, 4289, 10084, 7018, 4314, 3099, 4638, 4939, 5543, 2038, 4004,
97 | 5053, 4578, 27292, 4113, 5931, 2905, 11174, 2873, 4036, 3415, 1517, 4122, 1980, 4464, 1190, 2302, 156, 3933,
98 | 1877, 17630, 4337, 4624, 1075, 3468, 135, 1380]
99 | weights = 1 / torch.Tensor(n)
100 | weights /= weights.sum()
101 | # with open('data/coco.names', 'r') as f:
102 | # for k, v in zip(f.read().splitlines(), n):
103 | # print('%20s: %g' % (k, v))
104 | return weights
105 |
106 |
107 | def coco80_to_coco91_class(): # converts 80-index (val2014) to 91-index (paper)
108 | # https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/
109 | # a = np.loadtxt('data/coco.names', dtype='str', delimiter='\n')
110 | # b = np.loadtxt('data/coco_paper.names', dtype='str', delimiter='\n')
111 | # x1 = [list(a[i] == b).index(True) + 1 for i in range(80)] # darknet to coco
112 | # x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)] # coco to darknet
113 | x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 31, 32, 33, 34,
114 | 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
115 | 64, 65, 67, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90]
116 | return x
117 |
118 |
119 | def xyxy2xywh(x):
120 | # Transform box coordinates from [x1, y1, x2, y2] (where xy1=top-left, xy2=bottom-right) to [x, y, w, h]
121 | y = torch.zeros_like(x) if isinstance(x, torch.Tensor) else np.zeros_like(x)
122 | y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center
123 | y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center
124 | y[:, 2] = x[:, 2] - x[:, 0] # width
125 | y[:, 3] = x[:, 3] - x[:, 1] # height
126 | return y
127 |
128 |
129 | def xywh2xyxy(x):
130 | # Transform box coordinates from [x, y, w, h] to [x1, y1, x2, y2] (where xy1=top-left, xy2=bottom-right)
131 | y = torch.zeros_like(x) if isinstance(x, torch.Tensor) else np.zeros_like(x)
132 | y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
133 | y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
134 | y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
135 | y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
136 | return y
137 |
138 |
139 | # def xywh2xyxy(box):
140 | # # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2]
141 | # if isinstance(box, torch.Tensor):
142 | # x, y, w, h = box.t()
143 | # return torch.stack((x - w / 2, y - h / 2, x + w / 2, y + h / 2)).t()
144 | # else: # numpy
145 | # x, y, w, h = box.T
146 | # return np.stack((x - w / 2, y - h / 2, x + w / 2, y + h / 2)).T
147 | #
148 | #
149 | # def xyxy2xywh(box):
150 | # # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h]
151 | # if isinstance(box, torch.Tensor):
152 | # x1, y1, x2, y2 = box.t()
153 | # return torch.stack(((x1 + x2) / 2, (y1 + y2) / 2, x2 - x1, y2 - y1)).t()
154 | # else: # numpy
155 | # x1, y1, x2, y2 = box.T
156 | # return np.stack(((x1 + x2) / 2, (y1 + y2) / 2, x2 - x1, y2 - y1)).T
157 |
158 |
159 | def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):
160 | # Rescale coords (xyxy) from img1_shape to img0_shape
161 | if ratio_pad is None: # calculate from img0_shape
162 | gain = max(img1_shape) / max(img0_shape) # gain = old / new
163 | pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding
164 | else:
165 | gain = ratio_pad[0][0]
166 | pad = ratio_pad[1]
167 |
168 | coords[:, [0, 2]] -= pad[0] # x padding
169 | coords[:, [1, 3]] -= pad[1] # y padding
170 | coords[:, :4] /= gain
171 | clip_coords(coords, img0_shape)
172 | return coords
173 |
174 |
175 | def clip_coords(boxes, img_shape):
176 | # Clip bounding xyxy bounding boxes to image shape (height, width)
177 | boxes[:, 0].clamp_(0, img_shape[1]) # x1
178 | boxes[:, 1].clamp_(0, img_shape[0]) # y1
179 | boxes[:, 2].clamp_(0, img_shape[1]) # x2
180 | boxes[:, 3].clamp_(0, img_shape[0]) # y2
181 |
182 |
183 | def ap_per_class(tp, conf, pred_cls, target_cls):
184 | """ Compute the average precision, given the recall and precision curves.
185 | Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.
186 | # Arguments
187 | tp: True positives (nparray, nx1 or nx10).
188 | conf: Objectness value from 0-1 (nparray).
189 | pred_cls: Predicted object classes (nparray).
190 | target_cls: True object classes (nparray).
191 | # Returns
192 | The average precision as computed in py-faster-rcnn.
193 | """
194 |
195 | # Sort by objectness
196 | i = np.argsort(-conf)
197 | tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]
198 |
199 | # Find unique classes
200 | unique_classes = np.unique(target_cls)
201 |
202 | # Create Precision-Recall curve and compute AP for each class
203 | pr_score = 0.1 # score to evaluate P and R https://github.com/ultralytics/yolov3/issues/898
204 | s = [len(unique_classes), tp.shape[1]] # number class, number iou thresholds (i.e. 10 for mAP0.5...0.95)
205 | ap, p, r = np.zeros(s), np.zeros(s), np.zeros(s)
206 | for ci, c in enumerate(unique_classes):
207 | i = pred_cls == c
208 | n_gt = (target_cls == c).sum() # Number of ground truth objects
209 | n_p = i.sum() # Number of predicted objects
210 |
211 | if n_p == 0 or n_gt == 0:
212 | continue
213 | else:
214 | # Accumulate FPs and TPs
215 | fpc = (1 - tp[i]).cumsum(0)
216 | tpc = tp[i].cumsum(0)
217 |
218 | # Recall
219 | recall = tpc / (n_gt + 1e-16) # recall curve
220 | r[ci] = np.interp(-pr_score, -conf[i], recall[:, 0]) # r at pr_score, negative x, xp because xp decreases
221 |
222 | # Precision
223 | precision = tpc / (tpc + fpc) # precision curve
224 | p[ci] = np.interp(-pr_score, -conf[i], precision[:, 0]) # p at pr_score
225 |
226 | # AP from recall-precision curve
227 | for j in range(tp.shape[1]):
228 | ap[ci, j] = compute_ap(recall[:, j], precision[:, j])
229 |
230 | # Plot
231 | # fig, ax = plt.subplots(1, 1, figsize=(5, 5))
232 | # ax.plot(recall, precision)
233 | # ax.set_xlabel('Recall')
234 | # ax.set_ylabel('Precision')
235 | # ax.set_xlim(0, 1.01)
236 | # ax.set_ylim(0, 1.01)
237 | # fig.tight_layout()
238 | # fig.savefig('PR_curve.png', dpi=300)
239 |
240 | # Compute F1 score (harmonic mean of precision and recall)
241 | f1 = 2 * p * r / (p + r + 1e-16)
242 |
243 | return p, r, ap, f1, unique_classes.astype('int32')
244 |
245 |
246 | def compute_ap(recall, precision):
247 | """ Compute the average precision, given the recall and precision curves.
248 | Source: https://github.com/rbgirshick/py-faster-rcnn.
249 | # Arguments
250 | recall: The recall curve (list).
251 | precision: The precision curve (list).
252 | # Returns
253 | The average precision as computed in py-faster-rcnn.
254 | """
255 |
256 | # Append sentinel values to beginning and end
257 | mrec = np.concatenate(([0.], recall, [min(recall[-1] + 1E-3, 1.)]))
258 | mpre = np.concatenate(([0.], precision, [0.]))
259 |
260 | # Compute the precision envelope
261 | mpre = np.flip(np.maximum.accumulate(np.flip(mpre)))
262 |
263 | # Integrate area under curve
264 | method = 'interp' # methods: 'continuous', 'interp'
265 | if method == 'interp':
266 | x = np.linspace(0, 1, 101) # 101-point interp (COCO)
267 | ap = np.trapz(np.interp(x, mrec, mpre), x) # integrate
268 | else: # 'continuous'
269 | i = np.where(mrec[1:] != mrec[:-1])[0] # points where x axis (recall) changes
270 | ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) # area under curve
271 |
272 | return ap
273 |
274 |
275 | def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False):
276 | # Returns the IoU of box1 to box2. box1 is 4, box2 is nx4
277 | box2 = box2.t()
278 |
279 | # Get the coordinates of bounding boxes
280 | if x1y1x2y2: # x1, y1, x2, y2 = box1
281 | b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3]
282 | b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3]
283 | else: # transform from xywh to xyxy
284 | b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2
285 | b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2
286 | b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2
287 | b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2
288 |
289 | # Intersection area
290 | inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
291 | (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)
292 |
293 | # Union Area
294 | w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1
295 | w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1
296 | union = (w1 * h1 + 1e-16) + w2 * h2 - inter
297 |
298 | iou = inter / union # iou
299 | if GIoU or DIoU or CIoU:
300 | cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex (smallest enclosing box) width
301 | ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex height
302 | if GIoU: # Generalized IoU https://arxiv.org/pdf/1902.09630.pdf
303 | c_area = cw * ch + 1e-16 # convex area
304 | return iou - (c_area - union) / c_area # GIoU
305 | if DIoU or CIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
306 | # convex diagonal squared
307 | c2 = cw ** 2 + ch ** 2 + 1e-16
308 | # centerpoint distance squared
309 | rho2 = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2)) ** 2 / 4 + ((b2_y1 + b2_y2) - (b1_y1 + b1_y2)) ** 2 / 4
310 | if DIoU:
311 | return iou - rho2 / c2 # DIoU
312 | elif CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
313 | v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
314 | with torch.no_grad():
315 | alpha = v / (1 - iou + v)
316 | return iou - (rho2 / c2 + v * alpha) # CIoU
317 |
318 | return iou
319 |
320 |
321 | def box_iou(box1, box2):
322 | # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py
323 | """
324 | Return intersection-over-union (Jaccard index) of boxes.
325 | Both sets of boxes are expected to be in (x1, y1, x2, y2) format.
326 | Arguments:
327 | box1 (Tensor[N, 4])
328 | box2 (Tensor[M, 4])
329 | Returns:
330 | iou (Tensor[N, M]): the NxM matrix containing the pairwise
331 | IoU values for every element in boxes1 and boxes2
332 | """
333 |
334 | def box_area(box):
335 | # box = 4xn
336 | return (box[2] - box[0]) * (box[3] - box[1])
337 |
338 | area1 = box_area(box1.t())
339 | area2 = box_area(box2.t())
340 |
341 | # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)
342 | inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2)
343 | return inter / (area1[:, None] + area2 - inter) # iou = inter / (area1 + area2 - inter)
344 |
345 |
346 | def wh_iou(wh1, wh2):
347 | # Returns the nxm IoU matrix. wh1 is nx2, wh2 is mx2
348 | wh1 = wh1[:, None] # [N,1,2]
349 | wh2 = wh2[None] # [1,M,2]
350 | inter = torch.min(wh1, wh2).prod(2) # [N,M]
351 | return inter / (wh1.prod(2) + wh2.prod(2) - inter) # iou = inter / (area1 + area2 - inter)
352 |
353 |
354 | class FocalLoss(nn.Module):
355 | # Wraps focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5)
356 | def __init__(self, loss_fcn, gamma=1.5, alpha=0.25):
357 | super(FocalLoss, self).__init__()
358 | self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss()
359 | self.gamma = gamma
360 | self.alpha = alpha
361 | self.reduction = loss_fcn.reduction
362 | self.loss_fcn.reduction = 'none' # required to apply FL to each element
363 |
364 | def forward(self, pred, true):
365 | loss = self.loss_fcn(pred, true)
366 | # p_t = torch.exp(-loss)
367 | # loss *= self.alpha * (1.000001 - p_t) ** self.gamma # non-zero power for gradient stability
368 |
369 | # TF implementation https://github.com/tensorflow/addons/blob/v0.7.1/tensorflow_addons/losses/focal_loss.py
370 | pred_prob = torch.sigmoid(pred) # prob from logits
371 | p_t = true * pred_prob + (1 - true) * (1 - pred_prob)
372 | alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha)
373 | modulating_factor = (1.0 - p_t) ** self.gamma
374 | loss *= alpha_factor * modulating_factor
375 |
376 | if self.reduction == 'mean':
377 | return loss.mean()
378 | elif self.reduction == 'sum':
379 | return loss.sum()
380 | else: # 'none'
381 | return loss
382 |
383 |
384 | def smooth_BCE(eps=0.1): # https://github.com/ultralytics/yolov3/issues/238#issuecomment-598028441
385 | # return positive, negative label smoothing BCE targets
386 | return 1.0 - 0.5 * eps, 0.5 * eps
387 |
388 |
389 | def compute_loss(p, targets, model): # predictions, targets, model
390 | ft = torch.cuda.FloatTensor if p[0].is_cuda else torch.Tensor
391 | lcls, lbox, lobj = ft([0]), ft([0]), ft([0])
392 | tcls, tbox, indices, anchor_vec = build_targets(p, targets, model)
393 | h = model.hyp # hyperparameters
394 | red = 'mean' # Loss reduction (sum or mean)
395 |
396 | # Define criteria
397 | BCEcls = nn.BCEWithLogitsLoss(pos_weight=ft([h['cls_pw']]), reduction=red)
398 | BCEobj = nn.BCEWithLogitsLoss(pos_weight=ft([h['obj_pw']]), reduction=red)
399 |
400 | # class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3
401 | cp, cn = smooth_BCE(eps=0.0)
402 |
403 | # focal loss
404 | g = h['fl_gamma'] # focal loss gamma
405 | if g > 0:
406 | BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)
407 |
408 | # Compute losses
409 | np, ng = 0, 0 # number grid points, targets
410 | for i, pi in enumerate(p): # layer index, layer predictions
411 | b, a, gj, gi = indices[i] # image, anchor, gridy, gridx
412 | tobj = torch.zeros_like(pi[..., 0]) # target obj
413 | np += tobj.numel()
414 |
415 | # Compute losses
416 | nb = len(b)
417 | if nb: # number of targets
418 | ng += nb
419 | ps = pi[b, a, gj, gi] # prediction subset corresponding to targets
420 | # ps[:, 2:4] = torch.sigmoid(ps[:, 2:4]) # wh power loss (uncomment)
421 |
422 | # GIoU
423 | pxy = torch.sigmoid(ps[:, 0:2]) # pxy = pxy * s - (s - 1) / 2, s = 1.5 (scale_xy)
424 | pwh = torch.exp(ps[:, 2:4]).clamp(max=1E3) * anchor_vec[i]
425 | pbox = torch.cat((pxy, pwh), 1) # predicted box
426 | giou = bbox_iou(pbox.t(), tbox[i], x1y1x2y2=False, GIoU=True) # giou computation
427 | lbox += (1.0 - giou).sum() if red == 'sum' else (1.0 - giou).mean() # giou loss
428 | tobj[b, a, gj, gi] = (1.0 - model.gr) + model.gr * giou.detach().clamp(0).type(tobj.dtype) # giou ratio
429 |
430 | if model.nc > 1: # cls loss (only if multiple classes)
431 | t = torch.full_like(ps[:, 5:], cn) # targets
432 | t[range(nb), tcls[i]] = cp
433 | lcls += BCEcls(ps[:, 5:], t) # BCE
434 | # lcls += CE(ps[:, 5:], tcls[i]) # CE
435 |
436 | # Append targets to text file
437 | # with open('targets.txt', 'a') as file:
438 | # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)]
439 |
440 | lobj += BCEobj(pi[..., 4], tobj) # obj loss
441 |
442 | lbox *= h['giou']
443 | lobj *= h['obj']
444 | lcls *= h['cls']
445 | if red == 'sum':
446 | bs = tobj.shape[0] # batch size
447 | lobj *= 3 / (6300 * bs) * 2 # 3 / np * 2
448 | if ng:
449 | lcls *= 3 / ng / model.nc
450 | lbox *= 3 / ng
451 |
452 | loss = lbox + lobj + lcls
453 | return loss, torch.cat((lbox, lobj, lcls, loss)).detach()
454 |
455 |
456 | def build_targets(p, targets, model):
457 | # targets = [image, class, x, y, w, h]
458 |
459 | nt = targets.shape[0]
460 | tcls, tbox, indices, av = [], [], [], []
461 | reject, use_all_anchors = True, True
462 | gain = torch.ones(6, device=targets.device) # normalized to gridspace gain
463 |
464 | # m = list(model.modules())[-1]
465 | # for i in range(m.nl):
466 | # anchors = m.anchors[i]
467 | multi_gpu = type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel)
468 | for i, j in enumerate(model.yolo_layers):
469 | # get number of grid points and anchor vec for this yolo layer
470 | anchors = model.module.module_list[j].anchor_vec if multi_gpu else model.module_list[j].anchor_vec
471 |
472 | # iou of targets-anchors
473 | gain[2:] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain
474 | t, a = targets * gain, []
475 | gwh = t[:, 4:6]
476 | if nt:
477 | iou = wh_iou(anchors, gwh) # iou(3,n) = wh_iou(anchors(3,2), gwh(n,2))
478 |
479 | if use_all_anchors:
480 | na = anchors.shape[0] # number of anchors
481 | a = torch.arange(na).view(-1, 1).repeat(1, nt).view(-1)
482 | t = t.repeat(na, 1)
483 | else: # use best anchor only
484 | iou, a = iou.max(0) # best iou and anchor
485 |
486 | # reject anchors below iou_thres (OPTIONAL, increases P, lowers R)
487 | if reject:
488 | j = iou.view(-1) > model.hyp['iou_t'] # iou threshold hyperparameter
489 | t, a = t[j], a[j]
490 |
491 | # Indices
492 | b, c = t[:, :2].long().t() # target image, class
493 | gxy = t[:, 2:4] # grid x, y
494 | gwh = t[:, 4:6] # grid w, h
495 | gi, gj = gxy.long().t() # grid x, y indices
496 | indices.append((b, a, gj, gi))
497 |
498 | # Box
499 | gxy -= gxy.floor() # xy
500 | tbox.append(torch.cat((gxy, gwh), 1)) # xywh (grids)
501 | av.append(anchors[a]) # anchor vec
502 |
503 | # Class
504 | tcls.append(c)
505 | if c.shape[0]: # if any targets
506 | assert c.max() < model.nc, 'Model accepts %g classes labeled from 0-%g, however you labelled a class %g. ' \
507 | 'See https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data' % (
508 | model.nc, model.nc - 1, c.max())
509 |
510 | return tcls, tbox, indices, av
511 |
512 |
513 | def non_max_suppression(prediction, conf_thres=0.1, iou_thres=0.6, multi_label=True, classes=None, agnostic=False):
514 | """
515 | Performs Non-Maximum Suppression on inference results
516 | Returns detections with shape:
517 | nx6 (x1, y1, x2, y2, conf, cls)
518 | """
519 |
520 | # Box constraints
521 | min_wh, max_wh = 2, 4096 # (pixels) minimum and maximum box width and height
522 |
523 | method = 'merge'
524 | nc = prediction[0].shape[1] - 5 # number of classes
525 | multi_label &= nc > 1 # multiple labels per box
526 | output = [None] * len(prediction)
527 |
528 | for xi, x in enumerate(prediction): # image index, image inference
529 | # Apply conf constraint
530 | x = x[x[:, 4] > conf_thres]
531 |
532 | # Apply width-height constraint
533 | x = x[((x[:, 2:4] > min_wh) & (x[:, 2:4] < max_wh)).all(1)]
534 |
535 | # If none remain process next image
536 | if not x.shape[0]:
537 | continue
538 |
539 | # Compute conf
540 | x[..., 5:] *= x[..., 4:5] # conf = obj_conf * cls_conf
541 |
542 | # Box (center x, center y, width, height) to (x1, y1, x2, y2)
543 | box = xywh2xyxy(x[:, :4])
544 |
545 | # Detections matrix nx6 (xyxy, conf, cls)
546 | if multi_label:
547 | i, j = (x[:, 5:] > conf_thres).nonzero().t()
548 | x = torch.cat((box[i], x[i, j + 5].unsqueeze(1), j.float().unsqueeze(1)), 1)
549 | else: # best class only
550 | conf, j = x[:, 5:].max(1)
551 | x = torch.cat((box, conf.unsqueeze(1), j.float().unsqueeze(1)), 1)
552 |
553 | # Filter by class
554 | if classes:
555 | x = x[(j.view(-1, 1) == torch.tensor(classes, device=j.device)).any(1)]
556 |
557 | # Apply finite constraint
558 | if not torch.isfinite(x).all():
559 | x = x[torch.isfinite(x).all(1)]
560 |
561 | # If none remain process next image
562 | n = x.shape[0] # number of boxes
563 | if not n:
564 | continue
565 |
566 | # Sort by confidence
567 | # if method == 'fast_batch':
568 | # x = x[x[:, 4].argsort(descending=True)]
569 |
570 | # Batched NMS
571 | c = x[:, 5] * 0 if agnostic else x[:, 5] # classes
572 | boxes, scores = x[:, :4].clone() + c.view(-1, 1) * max_wh, x[:, 4] # boxes (offset by class), scores
573 | if method == 'merge': # Merge NMS (boxes merged using weighted mean)
574 | i = torchvision.ops.boxes.nms(boxes, scores, iou_thres)
575 | if 1 < n < 3E3: # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4)
576 | try:
577 | # weights = (box_iou(boxes, boxes).tril_() > iou_thres) * scores.view(-1, 1) # box weights
578 | # weights /= weights.sum(0) # normalize
579 | # x[:, :4] = torch.mm(weights.T, x[:, :4])
580 | weights = (box_iou(boxes[i], boxes) > iou_thres) * scores[None] # box weights
581 | x[i, :4] = torch.mm(weights / weights.sum(1, keepdim=True), x[:, :4]).float() # merged boxes
582 | except: # possible CUDA error https://github.com/ultralytics/yolov3/issues/1139
583 | pass
584 | elif method == 'vision':
585 | i = torchvision.ops.boxes.nms(boxes, scores, iou_thres)
586 | elif method == 'fast': # FastNMS from https://github.com/dbolya/yolact
587 | iou = box_iou(boxes, boxes).triu_(diagonal=1) # upper triangular iou matrix
588 | i = iou.max(0)[0] < iou_thres
589 |
590 | output[xi] = x[i]
591 | return output
592 |
593 |
594 | def get_yolo_layers(model):
595 | bool_vec = [x['type'] == 'yolo' for x in model.module_defs]
596 | return [i for i, x in enumerate(bool_vec) if x] # [82, 94, 106] for yolov3
597 |
598 |
599 | def print_model_biases(model):
600 | # prints the bias neurons preceding each yolo layer
601 | print('\nModel Bias Summary: %8s%18s%18s%18s' % ('layer', 'regression', 'objectness', 'classification'))
602 | try:
603 | multi_gpu = type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel)
604 | for l in model.yolo_layers: # print pretrained biases
605 | if multi_gpu:
606 | na = model.module.module_list[l].na # number of anchors
607 | b = model.module.module_list[l - 1][0].bias.view(na, -1) # bias 3x85
608 | else:
609 | na = model.module_list[l].na
610 | b = model.module_list[l - 1][0].bias.view(na, -1) # bias 3x85
611 | print(' ' * 20 + '%8g %18s%18s%18s' % (l, '%5.2f+/-%-5.2f' % (b[:, :4].mean(), b[:, :4].std()),
612 | '%5.2f+/-%-5.2f' % (b[:, 4].mean(), b[:, 4].std()),
613 | '%5.2f+/-%-5.2f' % (b[:, 5:].mean(), b[:, 5:].std())))
614 | except:
615 | pass
616 |
617 |
618 | def strip_optimizer(f='weights/last.pt'): # from utils.utils import *; strip_optimizer()
619 | # Strip optimizer from *.pt files for lighter files (reduced by 2/3 size)
620 | x = torch.load(f, map_location=torch.device('cpu'))
621 | x['optimizer'] = None
622 | torch.save(x, f)
623 |
624 |
625 | def create_backbone(f='weights/last.pt'): # from utils.utils import *; create_backbone()
626 | # create a backbone from a *.pt file
627 | x = torch.load(f, map_location=torch.device('cpu'))
628 | x['optimizer'] = None
629 | x['training_results'] = None
630 | x['epoch'] = -1
631 | for p in x['model'].values():
632 | try:
633 | p.requires_grad = True
634 | except:
635 | pass
636 | torch.save(x, 'weights/backbone.pt')
637 |
638 |
639 | def coco_class_count(path='../coco/labels/train2014/'):
640 | # Histogram of occurrences per class
641 | nc = 80 # number classes
642 | x = np.zeros(nc, dtype='int32')
643 | files = sorted(glob.glob('%s/*.*' % path))
644 | for i, file in enumerate(files):
645 | labels = np.loadtxt(file, dtype=np.float32).reshape(-1, 5)
646 | x += np.bincount(labels[:, 0].astype('int32'), minlength=nc)
647 | print(i, len(files))
648 |
649 |
650 | def coco_only_people(path='../coco/labels/train2017/'): # from utils.utils import *; coco_only_people()
651 | # Find images with only people
652 | files = sorted(glob.glob('%s/*.*' % path))
653 | for i, file in enumerate(files):
654 | labels = np.loadtxt(file, dtype=np.float32).reshape(-1, 5)
655 | if all(labels[:, 0] == 0):
656 | print(labels.shape[0], file)
657 |
658 |
659 | def select_best_evolve(path='evolve*.txt'): # from utils.utils import *; select_best_evolve()
660 | # Find best evolved mutation
661 | for file in sorted(glob.glob(path)):
662 | x = np.loadtxt(file, dtype=np.float32, ndmin=2)
663 | print(file, x[fitness(x).argmax()])
664 |
665 |
666 | def crop_images_random(path='../images/', scale=0.50): # from utils.utils import *; crop_images_random()
667 | # crops images into random squares up to scale fraction
668 | # WARNING: overwrites images!
669 | for file in tqdm(sorted(glob.glob('%s/*.*' % path))):
670 | img = cv2.imread(file) # BGR
671 | if img is not None:
672 | h, w = img.shape[:2]
673 |
674 | # create random mask
675 | a = 30 # minimum size (pixels)
676 | mask_h = random.randint(a, int(max(a, h * scale))) # mask height
677 | mask_w = mask_h # mask width
678 |
679 | # box
680 | xmin = max(0, random.randint(0, w) - mask_w // 2)
681 | ymin = max(0, random.randint(0, h) - mask_h // 2)
682 | xmax = min(w, xmin + mask_w)
683 | ymax = min(h, ymin + mask_h)
684 |
685 | # apply random color mask
686 | cv2.imwrite(file, img[ymin:ymax, xmin:xmax])
687 |
688 |
689 | def coco_single_class_labels(path='../coco/labels/train2014/', label_class=43):
690 | # Makes single-class coco datasets. from utils.utils import *; coco_single_class_labels()
691 | if os.path.exists('new/'):
692 | shutil.rmtree('new/') # delete output folder
693 | os.makedirs('new/') # make new output folder
694 | os.makedirs('new/labels/')
695 | os.makedirs('new/images/')
696 | for file in tqdm(sorted(glob.glob('%s/*.*' % path))):
697 | with open(file, 'r') as f:
698 | labels = np.array([x.split() for x in f.read().splitlines()], dtype=np.float32)
699 | i = labels[:, 0] == label_class
700 | if any(i):
701 | img_file = file.replace('labels', 'images').replace('txt', 'jpg')
702 | labels[:, 0] = 0 # reset class to 0
703 | with open('new/images.txt', 'a') as f: # add image to dataset list
704 | f.write(img_file + '\n')
705 | with open('new/labels/' + Path(file).name, 'a') as f: # write label
706 | for l in labels[i]:
707 | f.write('%g %.6f %.6f %.6f %.6f\n' % tuple(l))
708 | shutil.copyfile(src=img_file, dst='new/images/' + Path(file).name.replace('txt', 'jpg')) # copy images
709 |
710 |
711 | def kmean_anchors(path='./data/coco64.txt', n=9, img_size=(320, 1024), thr=0.20, gen=1000):
712 | # Creates kmeans anchors for use in *.cfg files: from utils.utils import *; _ = kmean_anchors()
713 | # n: number of anchors
714 | # img_size: (min, max) image size used for multi-scale training (can be same values)
715 | # thr: IoU threshold hyperparameter used for training (0.0 - 1.0)
716 | # gen: generations to evolve anchors using genetic algorithm
717 | from utils.datasets import LoadImagesAndLabels
718 |
719 | def print_results(k):
720 | k = k[np.argsort(k.prod(1))] # sort small to large
721 | iou = wh_iou(wh, torch.Tensor(k))
722 | max_iou = iou.max(1)[0]
723 | bpr, aat = (max_iou > thr).float().mean(), (iou > thr).float().mean() * n # best possible recall, anch > thr
724 | print('%.2f iou_thr: %.3f best possible recall, %.2f anchors > thr' % (thr, bpr, aat))
725 | print('n=%g, img_size=%s, IoU_all=%.3f/%.3f-mean/best, IoU>thr=%.3f-mean: ' %
726 | (n, img_size, iou.mean(), max_iou.mean(), iou[iou > thr].mean()), end='')
727 | for i, x in enumerate(k):
728 | print('%i,%i' % (round(x[0]), round(x[1])), end=', ' if i < len(k) - 1 else '\n') # use in *.cfg
729 | return k
730 |
731 | def fitness(k): # mutation fitness
732 | iou = wh_iou(wh, torch.Tensor(k)) # iou
733 | max_iou = iou.max(1)[0]
734 | return (max_iou * (max_iou > thr).float()).mean() # product
735 |
736 | # Get label wh
737 | wh = []
738 | dataset = LoadImagesAndLabels(path, augment=True, rect=True)
739 | nr = 1 if img_size[0] == img_size[1] else 10 # number augmentation repetitions
740 | for s, l in zip(dataset.shapes, dataset.labels):
741 | wh.append(l[:, 3:5] * (s / s.max())) # image normalized to letterbox normalized wh
742 | wh = np.concatenate(wh, 0).repeat(nr, axis=0) # augment 10x
743 | wh *= np.random.uniform(img_size[0], img_size[1], size=(wh.shape[0], 1)) # normalized to pixels (multi-scale)
744 | wh = wh[(wh > 2.0).all(1)] # remove below threshold boxes (< 2 pixels wh)
745 |
746 | # Darknet yolov3.cfg anchors
747 | use_darknet = False
748 | if use_darknet and n == 9:
749 | k = np.array([[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]])
750 | else:
751 | # Kmeans calculation
752 | from scipy.cluster.vq import kmeans
753 | print('Running kmeans for %g anchors on %g points...' % (n, len(wh)))
754 | s = wh.std(0) # sigmas for whitening
755 | k, dist = kmeans(wh / s, n, iter=30) # points, mean distance
756 | k *= s
757 | wh = torch.Tensor(wh)
758 | k = print_results(k)
759 |
760 | # # Plot
761 | # k, d = [None] * 20, [None] * 20
762 | # for i in tqdm(range(1, 21)):
763 | # k[i-1], d[i-1] = kmeans(wh / s, i) # points, mean distance
764 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7))
765 | # ax = ax.ravel()
766 | # ax[0].plot(np.arange(1, 21), np.array(d) ** 2, marker='.')
767 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7)) # plot wh
768 | # ax[0].hist(wh[wh[:, 0]<100, 0],400)
769 | # ax[1].hist(wh[wh[:, 1]<100, 1],400)
770 | # fig.tight_layout()
771 | # fig.savefig('wh.png', dpi=200)
772 |
773 | # Evolve
774 | npr = np.random
775 | f, sh, mp, s = fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma
776 | for _ in tqdm(range(gen), desc='Evolving anchors'):
777 | v = np.ones(sh)
778 | while (v == 1).all(): # mutate until a change occurs (prevent duplicates)
779 | v = ((npr.random(sh) < mp) * npr.random() * npr.randn(*sh) * s + 1).clip(0.3, 3.0) # 98.6, 61.6
780 | kg = (k.copy() * v).clip(min=2.0)
781 | fg = fitness(kg)
782 | if fg > f:
783 | f, k = fg, kg.copy()
784 | print_results(k)
785 | k = print_results(k)
786 |
787 | return k
788 |
789 |
790 | def print_mutation(hyp, results, bucket=''):
791 | # Print mutation results to evolve.txt (for use with train.py --evolve)
792 | a = '%10s' * len(hyp) % tuple(hyp.keys()) # hyperparam keys
793 | b = '%10.3g' * len(hyp) % tuple(hyp.values()) # hyperparam values
794 | c = '%10.4g' * len(results) % results # results (P, R, mAP, F1, test_loss)
795 | print('\n%s\n%s\nEvolved fitness: %s\n' % (a, b, c))
796 |
797 | if bucket:
798 | os.system('gsutil cp gs://%s/evolve.txt .' % bucket) # download evolve.txt
799 |
800 | with open('evolve.txt', 'a') as f: # append result
801 | f.write(c + b + '\n')
802 | x = np.unique(np.loadtxt('evolve.txt', ndmin=2), axis=0) # load unique rows
803 | np.savetxt('evolve.txt', x[np.argsort(-fitness(x))], '%10.3g') # save sort by fitness
804 |
805 | if bucket:
806 | os.system('gsutil cp evolve.txt gs://%s' % bucket) # upload evolve.txt
807 |
808 |
809 | def apply_classifier(x, model, img, im0):
810 | # applies a second stage classifier to yolo outputs
811 | im0 = [im0] if isinstance(im0, np.ndarray) else im0
812 | for i, d in enumerate(x): # per image
813 | if d is not None and len(d):
814 | d = d.clone()
815 |
816 | # Reshape and pad cutouts
817 | b = xyxy2xywh(d[:, :4]) # boxes
818 | b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # rectangle to square
819 | b[:, 2:] = b[:, 2:] * 1.3 + 30 # pad
820 | d[:, :4] = xywh2xyxy(b).long()
821 |
822 | # Rescale boxes from img_size to im0 size
823 | scale_coords(img.shape[2:], d[:, :4], im0[i].shape)
824 |
825 | # Classes
826 | pred_cls1 = d[:, 5].long()
827 | ims = []
828 | for j, a in enumerate(d): # per item
829 | cutout = im0[i][int(a[1]):int(a[3]), int(a[0]):int(a[2])]
830 | im = cv2.resize(cutout, (224, 224)) # BGR
831 | # cv2.imwrite('test%i.jpg' % j, cutout)
832 |
833 | im = im[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
834 | im = np.ascontiguousarray(im, dtype=np.float32) # uint8 to float32
835 | im /= 255.0 # 0 - 255 to 0.0 - 1.0
836 | ims.append(im)
837 |
838 | pred_cls2 = model(torch.Tensor(ims).to(d.device)).argmax(1) # classifier prediction
839 | x[i] = x[i][pred_cls1 == pred_cls2] # retain matching class detections
840 |
841 | return x
842 |
843 |
844 | def fitness(x):
845 | # Returns fitness (for use with results.txt or evolve.txt)
846 | w = [0.0, 0.01, 0.99, 0.00] # weights for [P, R, mAP, F1]@0.5 or [P, R, mAP@0.5, mAP@0.5:0.95]
847 | return (x[:, :4] * w).sum(1)
848 |
849 |
850 | def output_to_target(output, width, height):
851 | """
852 | Convert a YOLO model output to target format
853 |
854 | [batch_id, class_id, x, y, w, h, conf]
855 |
856 | """
857 |
858 | if isinstance(output, torch.Tensor):
859 | output = output.cpu().numpy()
860 |
861 | targets = []
862 | for i, o in enumerate(output):
863 |
864 | if o is not None:
865 | for pred in o:
866 | box = pred[:4]
867 | w = (box[2] - box[0]) / width
868 | h = (box[3] - box[1]) / height
869 | x = box[0] / width + w / 2
870 | y = box[1] / height + h / 2
871 | conf = pred[4]
872 | cls = int(pred[5])
873 |
874 | targets.append([i, cls, x, y, w, h, conf])
875 |
876 | return np.array(targets)
877 |
878 |
879 | # Plotting functions ---------------------------------------------------------------------------------------------------
880 | def plot_one_box(x, img, color=None, label=None, line_thickness=None):
881 | # Plots one bounding box on image img
882 | tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness
883 | color = color or [random.randint(0, 255) for _ in range(3)]
884 | c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
885 | cv2.rectangle(img, c1, c2, color, thickness=tl)
886 | if label:
887 | tf = max(tl - 1, 1) # font thickness
888 | t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
889 | c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
890 | cv2.rectangle(img, c1, c2, color, -1) # filled
891 | cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
892 |
893 |
894 | def plot_wh_methods(): # from utils.utils import *; plot_wh_methods()
895 | # Compares the two methods for width-height anchor multiplication
896 | # https://github.com/ultralytics/yolov3/issues/168
897 | x = np.arange(-4.0, 4.0, .1)
898 | ya = np.exp(x)
899 | yb = torch.sigmoid(torch.from_numpy(x)).numpy() * 2
900 |
901 | fig = plt.figure(figsize=(6, 3), dpi=150)
902 | plt.plot(x, ya, '.-', label='yolo method')
903 | plt.plot(x, yb ** 2, '.-', label='^2 power method')
904 | plt.plot(x, yb ** 2.5, '.-', label='^2.5 power method')
905 | plt.xlim(left=-4, right=4)
906 | plt.ylim(bottom=0, top=6)
907 | plt.xlabel('input')
908 | plt.ylabel('output')
909 | plt.legend()
910 | fig.tight_layout()
911 | fig.savefig('comparison.png', dpi=200)
912 |
913 |
914 | def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max_size=640, max_subplots=16):
915 | tl = 3 # line thickness
916 | tf = max(tl - 1, 1) # font thickness
917 |
918 | if isinstance(images, torch.Tensor):
919 | images = images.cpu().numpy()
920 |
921 | if isinstance(targets, torch.Tensor):
922 | targets = targets.cpu().numpy()
923 |
924 | # un-normalise
925 | if np.max(images[0]) <= 1:
926 | images *= 255
927 |
928 | bs, _, h, w = images.shape # batch size, _, height, width
929 | bs = min(bs, max_subplots) # limit plot images
930 | ns = np.ceil(bs ** 0.5) # number of subplots (square)
931 |
932 | # Check if we should resize
933 | scale_factor = max_size / max(h, w)
934 | if scale_factor < 1:
935 | h = math.ceil(scale_factor * h)
936 | w = math.ceil(scale_factor * w)
937 |
938 | # Empty array for output
939 | mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8)
940 |
941 | # Fix class - colour map
942 | prop_cycle = plt.rcParams['axes.prop_cycle']
943 | # https://stackoverflow.com/questions/51350872/python-from-color-name-to-rgb
944 | hex2rgb = lambda h: tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4))
945 | color_lut = [hex2rgb(h) for h in prop_cycle.by_key()['color']]
946 |
947 | for i, img in enumerate(images):
948 | if i == max_subplots: # if last batch has fewer images than we expect
949 | break
950 |
951 | block_x = int(w * (i // ns))
952 | block_y = int(h * (i % ns))
953 |
954 | img = img.transpose(1, 2, 0)
955 | if scale_factor < 1:
956 | img = cv2.resize(img, (w, h))
957 |
958 | mosaic[block_y:block_y + h, block_x:block_x + w, :] = img
959 | if len(targets) > 0:
960 | image_targets = targets[targets[:, 0] == i]
961 | boxes = xywh2xyxy(image_targets[:, 2:6]).T
962 | classes = image_targets[:, 1].astype('int')
963 | gt = image_targets.shape[1] == 6 # ground truth if no conf column
964 | conf = None if gt else image_targets[:, 6] # check for confidence presence (gt vs pred)
965 |
966 | boxes[[0, 2]] *= w
967 | boxes[[0, 2]] += block_x
968 | boxes[[1, 3]] *= h
969 | boxes[[1, 3]] += block_y
970 | for j, box in enumerate(boxes.T):
971 | cls = int(classes[j])
972 | color = color_lut[cls % len(color_lut)]
973 | cls = names[cls] if names else cls
974 | if gt or conf[j] > 0.3: # 0.3 conf thresh
975 | label = '%s' % cls if gt else '%s %.1f' % (cls, conf[j])
976 | plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)
977 |
978 | # Draw image filename labels
979 | if paths is not None:
980 | label = os.path.basename(paths[i])[:40] # trim to 40 char
981 | t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
982 | cv2.putText(mosaic, label, (block_x + 5, block_y + t_size[1] + 5), 0, tl / 3, [220, 220, 220], thickness=tf,
983 | lineType=cv2.LINE_AA)
984 |
985 | # Image border
986 | cv2.rectangle(mosaic, (block_x, block_y), (block_x + w, block_y + h), (255, 255, 255), thickness=3)
987 |
988 | if fname is not None:
989 | cv2.imwrite(fname, cv2.cvtColor(mosaic, cv2.COLOR_BGR2RGB))
990 |
991 | return mosaic
992 |
993 |
994 | def plot_test_txt(): # from utils.utils import *; plot_test()
995 | # Plot test.txt histograms
996 | x = np.loadtxt('test.txt', dtype=np.float32)
997 | box = xyxy2xywh(x[:, :4])
998 | cx, cy = box[:, 0], box[:, 1]
999 |
1000 | fig, ax = plt.subplots(1, 1, figsize=(6, 6))
1001 | ax.hist2d(cx, cy, bins=600, cmax=10, cmin=0)
1002 | ax.set_aspect('equal')
1003 | fig.tight_layout()
1004 | plt.savefig('hist2d.png', dpi=300)
1005 |
1006 | fig, ax = plt.subplots(1, 2, figsize=(12, 6))
1007 | ax[0].hist(cx, bins=600)
1008 | ax[1].hist(cy, bins=600)
1009 | fig.tight_layout()
1010 | plt.savefig('hist1d.png', dpi=200)
1011 |
1012 |
1013 | def plot_targets_txt(): # from utils.utils import *; plot_targets_txt()
1014 | # Plot targets.txt histograms
1015 | x = np.loadtxt('targets.txt', dtype=np.float32).T
1016 | s = ['x targets', 'y targets', 'width targets', 'height targets']
1017 | fig, ax = plt.subplots(2, 2, figsize=(8, 8))
1018 | ax = ax.ravel()
1019 | for i in range(4):
1020 | ax[i].hist(x[i], bins=100, label='%.3g +/- %.3g' % (x[i].mean(), x[i].std()))
1021 | ax[i].legend()
1022 | ax[i].set_title(s[i])
1023 | fig.tight_layout()
1024 | plt.savefig('targets.jpg', dpi=200)
1025 |
1026 |
1027 | def plot_evolution_results(hyp): # from utils.utils import *; plot_evolution_results(hyp)
1028 | # Plot hyperparameter evolution results in evolve.txt
1029 | x = np.loadtxt('evolve.txt', ndmin=2)
1030 | f = fitness(x)
1031 | weights = (f - f.min()) ** 2 # for weighted results
1032 | fig = plt.figure(figsize=(12, 10))
1033 | matplotlib.rc('font', **{'size': 8})
1034 | for i, (k, v) in enumerate(hyp.items()):
1035 | y = x[:, i + 7]
1036 | # mu = (y * weights).sum() / weights.sum() # best weighted result
1037 | mu = y[f.argmax()] # best single result
1038 | plt.subplot(4, 5, i + 1)
1039 | plt.plot(mu, f.max(), 'o', markersize=10)
1040 | plt.plot(y, f, '.')
1041 | plt.title('%s = %.3g' % (k, mu), fontdict={'size': 9}) # limit to 40 characters
1042 | print('%15s: %.3g' % (k, mu))
1043 | fig.tight_layout()
1044 | plt.savefig('evolve.png', dpi=200)
1045 |
1046 |
1047 | def plot_results_overlay(start=0, stop=0): # from utils.utils import *; plot_results_overlay()
1048 | # Plot training results files 'results*.txt', overlaying train and val losses
1049 | s = ['train', 'train', 'train', 'Precision', 'mAP@0.5', 'val', 'val', 'val', 'Recall', 'F1'] # legends
1050 | t = ['GIoU', 'Objectness', 'Classification', 'P-R', 'mAP-F1'] # titles
1051 | for f in sorted(glob.glob('results*.txt') + glob.glob('../../Downloads/results*.txt')):
1052 | results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T
1053 | n = results.shape[1] # number of rows
1054 | x = range(start, min(stop, n) if stop else n)
1055 | fig, ax = plt.subplots(1, 5, figsize=(14, 3.5))
1056 | ax = ax.ravel()
1057 | for i in range(5):
1058 | for j in [i, i + 5]:
1059 | y = results[j, x]
1060 | if i in [0, 1, 2]:
1061 | y[y == 0] = np.nan # dont show zero loss values
1062 | ax[i].plot(x, y, marker='.', label=s[j])
1063 | ax[i].set_title(t[i])
1064 | ax[i].legend()
1065 | ax[i].set_ylabel(f) if i == 0 else None # add filename
1066 | fig.tight_layout()
1067 | fig.savefig(f.replace('.txt', '.png'), dpi=200)
1068 |
1069 |
1070 | def plot_results(start=0, stop=0, bucket='', id=()): # from utils.utils import *; plot_results()
1071 | # Plot training 'results*.txt' as seen in https://github.com/ultralytics/yolov3#training
1072 | fig, ax = plt.subplots(2, 5, figsize=(12, 6))
1073 | ax = ax.ravel()
1074 | s = ['GIoU', 'Objectness', 'Classification', 'Precision', 'Recall',
1075 | 'val GIoU', 'val Objectness', 'val Classification', 'mAP@0.5', 'F1']
1076 | if bucket:
1077 | os.system('rm -rf storage.googleapis.com')
1078 | files = ['https://storage.googleapis.com/%s/results%g.txt' % (bucket, x) for x in id]
1079 | else:
1080 | files = glob.glob('results*.txt') + glob.glob('../../Downloads/results*.txt')
1081 | for f in sorted(files):
1082 | try:
1083 | results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T
1084 | n = results.shape[1] # number of rows
1085 | x = range(start, min(stop, n) if stop else n)
1086 | for i in range(10):
1087 | y = results[i, x]
1088 | if i in [0, 1, 2, 5, 6, 7]:
1089 | y[y == 0] = np.nan # dont show zero loss values
1090 | # y /= y[0] # normalize
1091 | ax[i].plot(x, y, marker='.', label=Path(f).stem, linewidth=2, markersize=8)
1092 | ax[i].set_title(s[i])
1093 | if i in [5, 6, 7]: # share train and val loss y axes
1094 | ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])
1095 | except:
1096 | print('Warning: Plotting error for %s, skipping file' % f)
1097 |
1098 | fig.tight_layout()
1099 | ax[1].legend()
1100 | fig.savefig('results.png', dpi=200)
1101 |
--------------------------------------------------------------------------------
/Yundun_chinese/output/00000.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/00000.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/00001.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/00001.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/00002.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/00002.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/00003.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/00003.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/00004.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/00004.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0010fe2902d64b31817a479e72e40acd_4.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0014f3dda06d472e8775a1dc31accee5_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0021b0a361ff4fa08629c70632f3d70d_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0117d4b28cac47808cce3f5d18e2a2bb_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0191c48ace5c4dbb9835e2bfe452dd62_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0194cc953cdb4db1a29ba4e2b53fa378_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02052f20b4cf43c7a3138bb583404c69_4.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02513359cf4b4d83a18b779a80538dec_4.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/0259cf81304f45609641dc11c7855237_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_0.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_1.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_2.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/02923304f2ff445e838954a9f7d307ea_4.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/3.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/4.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/output/now.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/Yundun_chinese/output/now.jpg
--------------------------------------------------------------------------------
/Yundun_chinese/readme.md:
--------------------------------------------------------------------------------
1 |
2 | #### pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
3 | #### pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI
4 | #### pip install torch==1.4.0+cpu torchvision==0.5.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
5 | #### 在data文件夹下建立三个子文件夹(Annotations、images与ImageSets,labels后续使用脚本生成)其中Annotations存放xml文件,images图像,ImageSets新建Main文件存放train与test文件(脚本生成),labels是标签文件
6 | #### 新建两个py文件,分别放入以下两段代码,并依次运行
7 | #### data目录下新建cat.name写上预测的类别名字,不懂的可以参考coco.name
8 | #### cat.data,文件内容如下,配置训练的数据,同理参考coco.data
9 | #### cfg文件修改
10 | #### 主要修改3*3共9处,分别为最后的通道数、类别数、随机多尺度训练开关
11 | #### python train.py --data data/cat.data --cfg cfg/yolov3-cat.cfg --weights weights/yolov3.weights --epochs 10
12 | #### python test.py --data-cfg data/cat.data --cfg cfg/yolov3-cat.cfg --weights weights/latest.pt
13 | #### python detect.py --data-cfg data/cat.data --cfg cfg/yolov3-cat.cfg --weights weights/best.pt
14 | #### python -c "from utils import utils; utils.plot_results()"
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Yundun_chinese/requirements.txt:
--------------------------------------------------------------------------------
1 | Cython
2 | absl-py==0.9.0
3 | albumentations==0.4.5
4 | bleach==1.5.0
5 | cachetools==4.1.0
6 | certifi==2020.4.5.1
7 | chardet==3.0.4
8 | click==7.1.2
9 | cycler==0.10.0
10 | Cython==0.29.17
11 | decorator==4.4.2
12 | efficientnet-pytorch==0.6.3
13 | Flask==1.1.1
14 | future==0.18.2
15 | google-auth==1.14.2
16 | google-auth-oauthlib==0.4.1
17 | grpcio==1.28.1
18 | h5py==2.10.0
19 | html5lib==0.9999999
20 | idna==2.9
21 | imagecodecs==2020.2.18
22 | imageio==2.8.0
23 | imgaug==0.2.6
24 | itsdangerous==1.1.0
25 | Jinja2==2.11.2
26 | joblib==0.15.1
27 | Keras==2.3.1
28 | Keras-Applications==1.0.8
29 | Keras-Preprocessing==1.1.2
30 | kiwisolver==1.2.0
31 | Markdown==3.2.1
32 | MarkupSafe==1.1.1
33 | matplotlib==3.2.1
34 | networkx==2.4
35 | numpy==1.16.0
36 | oauthlib==3.1.0
37 | opencv-python==4.2.0.34
38 | Pillow==7.0.0
39 | protobuf==3.11.3
40 | pyasn1==0.4.8
41 | pyasn1-modules==0.2.8
42 | pycocotools==2.0
43 | pyparsing==2.4.7
44 | python-dateutil==2.8.1
45 | PyWavelets==1.1.1
46 | PyYAML==5.3.1
47 | requests==2.23.0
48 | requests-oauthlib==1.3.0
49 | rsa==4.0
50 | scikit-image==0.17.2
51 | scikit-learn==0.23.1
52 | scipy==1.4.1
53 | six==1.14.0
54 | sklearn==0.0
55 | tensorboard==2.2.1
56 | tensorboard-plugin-wit==1.6.0.post3
57 | threadpoolctl==2.0.0
58 | tifffile==2020.5.11
59 | tqdm==4.46.0
60 | urllib3==1.25.9
61 | Werkzeug==1.0.1
62 |
--------------------------------------------------------------------------------
/Yundun_chinese/settings.py:
--------------------------------------------------------------------------------
1 | model_path_chinese = 'model/predict_position/best.pt'
2 | model_path_chinese_cfg = 'model/predict_position/yolov3.cfg'
3 | model_path_chinese_names = 'model/predict_position/target.names'
4 |
5 | efficientnet_b4_pth = 'model/chinese_recognize/efficientnet-b4-6ed6700e.pth'
6 | model_chinese_crop_b4 = r'model/chinese_recognize/model_b4.pth'
7 | data_train_path = './data/train'
8 |
9 |
10 |
--------------------------------------------------------------------------------
/captcha/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/1.jpg
--------------------------------------------------------------------------------
/captcha/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/10.jpg
--------------------------------------------------------------------------------
/captcha/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/11.png
--------------------------------------------------------------------------------
/captcha/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/2.jpg
--------------------------------------------------------------------------------
/captcha/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/3.jpg
--------------------------------------------------------------------------------
/captcha/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/4.jpg
--------------------------------------------------------------------------------
/captcha/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/5.png
--------------------------------------------------------------------------------
/captcha/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/6.png
--------------------------------------------------------------------------------
/captcha/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/7.png
--------------------------------------------------------------------------------
/captcha/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/8.png
--------------------------------------------------------------------------------
/captcha/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/captcha/9.png
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/icon.png
--------------------------------------------------------------------------------
/vx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/vx.jpg
--------------------------------------------------------------------------------
/vxq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WindSeason666/Spider_Captcha/eac4333bc686d5cc7d8d1aa3c87f78b814caeb5c/vxq.png
--------------------------------------------------------------------------------