├── 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 | Sample 51 |

52 | 53 | -------------------------------------------------------------------------------- /Yundun_chinese/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /Yundun_chinese/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 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 | 11 | -------------------------------------------------------------------------------- /Yundun_chinese/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 48 | 49 | 50 | 51 | 52 | 71 | 72 | 73 | 92 | 93 | 94 | 113 | 114 | 115 | 134 | 135 | 136 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 1588987877540 172 | 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 --------------------------------------------------------------------------------