├── DenseBox.py ├── LICENSE ├── README.md ├── demo_1.jpg ├── demo_2.jpg ├── demo_3.jpg ├── demo_4.jpg ├── pair_1_1.jpg ├── pair_1_2.jpg ├── pair_2_1.jpg ├── pair_2_2.jpg ├── pair_3_1.jpg ├── pair_3_2.jpg └── process_plate.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Even 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DenseBox 2 | Baidu's Densebox implemention with PyTorch used for multi-task learning of object detection and landmark(key-point) localization 3 | 4 | # Test result 5 | ![](https://github.com/CaptainEven/DenseBox/blob/master/demo_1.jpg) 6 | ![](https://github.com/CaptainEven/DenseBox/blob/master/demo_4.jpg)
7 | ![](https://github.com/CaptainEven/DenseBox/blob/master/demo_2.jpg)
8 | ![](https://github.com/CaptainEven/DenseBox/blob/master/demo_3.jpg)
9 | 10 | # Perspective transformation result 11 | ![](https://github.com/CaptainEven/DenseBox/blob/master/pair_1_1.jpg) 12 | ![](https://github.com/CaptainEven/DenseBox/blob/master/pair_1_2.jpg)
13 | 14 | ![](https://github.com/CaptainEven/DenseBox/blob/master/pair_2_1.jpg) 15 | ![](https://github.com/CaptainEven/DenseBox/blob/master/pair_2_2.jpg)
16 | 17 | ![](https://github.com/CaptainEven/DenseBox/blob/master/pair_3_1.jpg) 18 | ![](https://github.com/CaptainEven/DenseBox/blob/master/pair_3_2.jpg)
19 | 20 | 21 | ## A small dataset sample:
22 | [patches](https://pan.baidu.com/s/1hGtdPHhuMW9Lz0gRLMYUUw)
23 | 24 | extract code: ir6n
25 | -------------------------------------------------------------------------------- /demo_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/demo_1.jpg -------------------------------------------------------------------------------- /demo_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/demo_2.jpg -------------------------------------------------------------------------------- /demo_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/demo_3.jpg -------------------------------------------------------------------------------- /demo_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/demo_4.jpg -------------------------------------------------------------------------------- /pair_1_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/pair_1_1.jpg -------------------------------------------------------------------------------- /pair_1_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/pair_1_2.jpg -------------------------------------------------------------------------------- /pair_2_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/pair_2_1.jpg -------------------------------------------------------------------------------- /pair_2_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/pair_2_2.jpg -------------------------------------------------------------------------------- /pair_3_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/pair_3_1.jpg -------------------------------------------------------------------------------- /pair_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainEven/DenseBox/7340ed03d34f48cfc15778c5b380782cfd4e8344/pair_3_2.jpg -------------------------------------------------------------------------------- /process_plate.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import os 4 | import random 5 | import shutil 6 | import time 7 | import xml.etree.ElementTree as ET 8 | import cv2 9 | import numpy as np 10 | import torch 11 | import math 12 | 13 | from PIL import Image, ImageDraw 14 | from tqdm import tqdm 15 | 16 | from torchvision import transforms as T 17 | from models import LP_Net 18 | 19 | 20 | # 类别名称 21 | classes = ['Plate'] 22 | # classes = ['car', 'Car'] 23 | # plate_models = [u'单', u'双'] 24 | # plate_colors = [u'蓝', u'黄', u'白', u'黑', u'绿'] 25 | 26 | # 输出文件名格式 27 | # count_id = 0 28 | type_name = 'det' 29 | date_name = time.strftime('_%Y_%m_%d_', time.localtime(time.time())) 30 | print('date_name: ', date_name) 31 | # id_name = '{:0>6d}'.format(count_id) 32 | 33 | print(type_name, date_name, '{:0>6d}'.format(0)) 34 | 35 | 36 | # 将原始数据目录分割为3个子目录 37 | def move_rename(origin_dir, img_path, xml_path): 38 | """ 39 | :param origin_dir: 图像原始路径 40 | :param img_path: 图像新路径 41 | :param xml_path: xml文件新路径 42 | :return: None 43 | """ 44 | global count_id 45 | for img_name in os.listdir(origin_dir): 46 | if img_name.endswith('.jpg'): 47 | xml_src_path = os.path.join(origin_dir, img_name[:-4] + '.xml') 48 | 49 | if os.path.exists(xml_src_path): # 验证一个img对应一个xml 50 | id_name = '{:0>6d}'.format(count_id) 51 | format_name = type_name + date_name + id_name 52 | 53 | img_src_path = os.path.join(origin_dir, img_name) 54 | 55 | img_dst_path = os.path.join(img_path, format_name + '.jpg') 56 | xml_dst_path = os.path.join(xml_path, format_name + '.xml') 57 | 58 | shutil.copyfile(img_src_path, img_dst_path) 59 | shutil.copyfile(xml_src_path, xml_dst_path) 60 | 61 | count_id += 1 62 | if count_id % 100 == 0: 63 | print('=> {} files_path moved and renamed.'.format(count_id)) 64 | print('=> copy and rename total {} files_path done.'.format(count_id)) 65 | 66 | 67 | # 生成img绝对路径和列表 68 | def get_img_path_list(img_path): 69 | """ 70 | :param img_path: 图像重命名后的路径 71 | :return: None, 将图像文件绝对路径列表写入磁盘 72 | """ 73 | list_path = os.path.join(dst_root + os.path.sep, 'all.txt') 74 | if os.path.exists(list_path): 75 | os.remove(list_path) 76 | 77 | with open(list_path, 'w') as f_list: 78 | img_files = os.listdir(img_path) 79 | for f_name in img_files: 80 | the_path = os.path.join(img_path + os.path.sep, f_name) 81 | f_list.write(the_path + '\n') 82 | print('=> generating all.txt done, total {} images.'.format(len(img_files))) 83 | 84 | 85 | # 计算label, 转换成dark-net要求的形式,对原图归一化 86 | def convert(size, bbox): 87 | """ 88 | :param size: 原始图像宽高 89 | :param bbox: 车牌最小外接矩形 90 | :return: 转换后的bbox中心坐标和宽高 91 | """ 92 | dw = 1.0 / size[0] # 整张原图宽度 93 | dh = 1.0 / size[1] # 整张原图高度 94 | x = (bbox[0] + bbox[1]) / 2.0 - 1.0 95 | y = (bbox[2] + bbox[3]) / 2.0 - 1.0 96 | w = bbox[1] - bbox[0] 97 | h = bbox[3] - bbox[2] 98 | x *= dw 99 | w *= dw 100 | y *= dh 101 | h *= dh 102 | return x, y, w, h 103 | 104 | 105 | global bbox_max_width, bbox_max_height 106 | bbox_max_width, bbox_max_height = 0, 0 107 | 108 | 109 | def format_4_vertices(vertices): 110 | """ 111 | vertices: 2D list or numpy array 112 | """ 113 | assert len(vertices) == 4 114 | 115 | left_pts = sorted(vertices, key=lambda x: int(x[0]))[:2] 116 | right_pts = [pt for pt in vertices if pt not in left_pts] 117 | 118 | left_up = sorted(left_pts, key=lambda x: int(x[1]))[0] 119 | left_down = [x for x in left_pts if x != left_up][0] 120 | 121 | right_up = sorted(right_pts, key=lambda x: int(x[1]))[0] 122 | right_down = [x for x in right_pts if x != right_up][0] 123 | 124 | return [left_up, right_up, right_down, left_down] 125 | 126 | 127 | def format_lp_vertices(vertices): 128 | """ 129 | 按照left-up编号为0, 顺时针依次编号的循序排列: 130 | leftup -> 0 131 | rightup -> 1 132 | rightdown -> 2 133 | leftdown -> 3 134 | """ 135 | assert len(vertices) == 4 136 | vertices = [pt.split(',') for pt in vertices] 137 | print('=> vertices: ', vertices) 138 | 139 | left_pts = sorted(vertices, key=lambda x: int(x[0]))[:2] 140 | right_pts = [pt for pt in vertices if pt not in left_pts] 141 | 142 | left_up = sorted(left_pts, key=lambda x: int(x[1]))[0] 143 | left_down = [x for x in left_pts if x != left_up][0] 144 | right_up = sorted(right_pts, key=lambda x: int(x[1]))[0] 145 | right_down = [x for x in right_pts if x != right_up][0] 146 | 147 | # do statistics... 148 | # global bbox_max_width, bbox_max_height 149 | # bbox_width = max(right_up[0], right_down[0]) - min(left_up[0], left_down[0]) 150 | # bbox_height = max(left_down[1], right_down[1]) - min(left_up[1], right_up[1]) 151 | # if bbox_width > bbox_max_width: 152 | # bbox_max_width = bbox_width 153 | # if bbox_height > bbox_max_height: 154 | # bbox_max_height = bbox_height 155 | 156 | return [left_up, right_up, right_down, left_down] 157 | 158 | 159 | # ---------------------- generate patches 160 | interp_methods = [cv2.INTER_LINEAR, 161 | cv2.INTER_CUBIC, 162 | cv2.INTER_AREA, 163 | cv2.INTER_NEAREST, 164 | cv2.INTER_LANCZOS4] 165 | 166 | 167 | def gen_pos_patch(img, 168 | vertices, 169 | offset_radius=15, 170 | size=(240, 240)): 171 | """ 172 | 生成positive patch: 大多数bbox都满足条件, 做非等比缩放 173 | """ 174 | H, W, _ = img.shape # H×W×channels 175 | 176 | # 格式化四个顶点 177 | vertices = [[int(x) for x in pt.split(',')] 178 | for pt in vertices] 179 | vertices = format_4_vertices(vertices) 180 | 181 | # 0 1 2 3 182 | # 将四个顶点转换成bbox: x_min, x_max, y_min, y_max 183 | bbox = bbox_from_vertices(vertices) 184 | 185 | # bbox中心点坐标 186 | center_x = int(float(bbox[0] + bbox[1]) * 0.5 + 0.5) 187 | center_y = int(float(bbox[2] + bbox[3]) * 0.5 + 0.5) 188 | 189 | # bbox长宽 190 | bbox_w, bbox_h = float(bbox[1] - bbox[0]), float(bbox[3] - bbox[2]) 191 | 192 | # 计算patch中心offset 193 | offset_x = random.uniform(-1.0, 1.0) * offset_radius 194 | offset_y = random.uniform(-1.0, 1.0) * offset_radius 195 | 196 | # 计算patch的org坐标和end坐标: 这个4倍有改进的空间... 197 | patch_w, patch_h = 4.0 * bbox_w, 4.0 * bbox_h 198 | patch_org = [center_x - patch_w * 0.5 - offset_x, 199 | center_y - patch_h * 0.5 - offset_y] 200 | patch_end = [patch_org[0] + patch_w, 201 | patch_org[1] + patch_h] 202 | 203 | # 改进: 使得位于图片边界的bbox也能用来训练 204 | # if patch_org[0] < 0 or patch_org[1] < 0 \ 205 | # or patch_end[0] >= W or patch_end[1] >= H: 206 | # return None, None, None, None 207 | 208 | if patch_org[0] < 0 or patch_org[1] < 0: 209 | patch_org[0] = patch_org[0] if patch_org[0] >= 0 else 0 210 | patch_org[1] = patch_org[1] if patch_org[1] >= 0 else 0 211 | 212 | # 更新patch w, h 213 | patch_w = patch_end[0] - patch_org[0] 214 | patch_h = patch_end[1] - patch_org[1] 215 | 216 | if patch_end[0] >= W or patch_end[1] >= H: 217 | patch_end[0] = patch_end[0] if patch_end[0] < W else W - 1 218 | patch_end[1] = patch_end[1] if patch_end[1] < H else H - 1 219 | 220 | # 更新patch w, h 221 | patch_w = patch_end[0] - patch_org[0] 222 | patch_h = patch_end[1] - patch_org[1] 223 | 224 | # 计算相对坐标, 并归一化到[0, 1] 225 | bbox_leftup = [(bbox[0] - patch_org[0]) / patch_w, 226 | (bbox[2] - patch_org[1]) / patch_h] 227 | bbox_rightdown = [(bbox[1] - patch_org[0]) / patch_w, 228 | (bbox[3] - patch_org[1]) / patch_h] 229 | 230 | # 四个顶点(keypoint) 231 | leftup = [(vertices[0][0] - patch_org[0]) / patch_w, 232 | (vertices[0][1] - patch_org[1]) / patch_h] 233 | 234 | rightup = [(vertices[1][0] - patch_org[0]) / patch_w, 235 | (vertices[1][1] - patch_org[1]) / patch_h] 236 | 237 | rightdown = [(vertices[2][0] - patch_org[0]) / patch_w, 238 | (vertices[2][1] - patch_org[1]) / patch_h] 239 | 240 | leftdown = [(vertices[3][0] - patch_org[0]) / patch_w, 241 | (vertices[3][1] - patch_org[1]) / patch_h] 242 | 243 | # 截取patch: H,W,channels 244 | patch = img[int(patch_org[1] + 0.5): int(patch_end[1] + 0.5), 245 | int(patch_org[0] + 0.5): int(patch_end[0] + 0.5), :] 246 | 247 | if patch is None: 248 | return None, None, None, None 249 | 250 | # 非等比缩放patch到指定size 251 | try: 252 | patch = cv2.resize(patch, dsize=size, interpolation=cv2.INTER_CUBIC) 253 | except Exception as e: 254 | print(e) 255 | 256 | # 计算在新坐标系下的绝对坐标 257 | patch_w_new, patch_h_new = float(size[0]), float(size[1]) 258 | 259 | bbox_leftup = [int(bbox_leftup[0] * patch_w_new + 0.5), 260 | int(bbox_leftup[1] * patch_h_new + 0.5)] 261 | bbox_rightdown = [int(bbox_rightdown[0] * patch_w_new + 0.5), 262 | int(bbox_rightdown[1] * patch_h_new + 0.5)] 263 | 264 | leftup = [int(leftup[0] * patch_w_new + 0.5), 265 | int(leftup[1] * patch_h_new + 0.5)] 266 | rightup = [int(rightup[0] * patch_w_new + 0.5), 267 | int(rightup[1] * patch_h_new + 0.5)] 268 | rightdown = [int(rightdown[0] * patch_w_new + 0.5), 269 | int(rightdown[1] * patch_h_new + 0.5)] 270 | leftdown = [int(leftdown[0] * patch_w_new + 0.5), 271 | int(leftdown[1] * patch_h_new + 0.5)] 272 | 273 | # landmark计算gray zone的约束 274 | if leftup[0] < 8 or leftup[1] < 8 or \ 275 | rightup[0] > W - 8 or rightup[1] < 8 or \ 276 | rightdown[0] > W - 8 or rightdown[1] > H - 8 or \ 277 | leftdown[0] < 8 or leftdown[1] > H - 8: 278 | return None, None, None, None 279 | 280 | # 可视化中间结果 281 | 282 | """ 283 | # leftup 284 | cv2.circle(patch, tuple(leftup), 5, (0, 255, 255), -1) 285 | txt = 'LU' 286 | txt_size = cv2.getTextSize(text=txt, 287 | fontFace=cv2.FONT_HERSHEY_PLAIN, 288 | fontScale=1, 289 | thickness=1)[0] 290 | cv2.putText(img=patch, 291 | text=txt, 292 | org=(leftup[0] - txt_size[0], leftup[1]), 293 | fontFace=cv2.FONT_HERSHEY_PLAIN, 294 | fontScale=1, 295 | color=[225, 255, 255], 296 | thickness=1) 297 | 298 | # rightup 299 | cv2.circle(patch, tuple(rightup), 5, (0, 255, 255), -1) 300 | txt = 'RU' 301 | txt_size = cv2.getTextSize(text=txt, 302 | fontFace=cv2.FONT_HERSHEY_PLAIN, 303 | fontScale=1, 304 | thickness=1)[0] 305 | cv2.putText(img=patch, 306 | text=txt, 307 | org=(rightup[0], rightup[1]), 308 | fontFace=cv2.FONT_HERSHEY_PLAIN, 309 | fontScale=1, 310 | color=[225, 255, 255], 311 | thickness=1) 312 | 313 | # rightdown 314 | cv2.circle(patch, tuple(rightdown), 5, (0, 255, 255), -1) 315 | txt = 'RD' 316 | txt_size = cv2.getTextSize(text=txt, 317 | fontFace=cv2.FONT_HERSHEY_PLAIN, 318 | fontScale=1, 319 | thickness=1)[0] 320 | cv2.putText(img=patch, 321 | text=txt, 322 | org=(rightdown[0], 323 | rightdown[1] + txt_size[1]), 324 | fontFace=cv2.FONT_HERSHEY_PLAIN, 325 | fontScale=1, 326 | color=[225, 255, 255], 327 | thickness=1) 328 | 329 | # leftdown 330 | txt = 'LD' 331 | cv2.circle(patch, tuple(leftdown), 5, (0, 255, 255), -1) 332 | txt_size = cv2.getTextSize(text=txt, 333 | fontFace=cv2.FONT_HERSHEY_PLAIN, 334 | fontScale=1, 335 | thickness=1)[0] 336 | cv2.putText(img=patch, 337 | text=txt, 338 | org=(leftdown[0] - txt_size[0], 339 | leftdown[1] + txt_size[1]), 340 | fontFace=cv2.FONT_HERSHEY_PLAIN, 341 | fontScale=1, 342 | color=[225, 255, 255], 343 | thickness=1) 344 | 345 | cv2.imshow('patch', patch) 346 | cv2.waitKey() 347 | """ 348 | 349 | return patch, bbox_leftup, bbox_rightdown, [leftup, 350 | rightup, 351 | rightdown, 352 | leftdown] 353 | 354 | 355 | def gen_patch_lm(img, 356 | vertices, 357 | offset_radius=15, 358 | min_scale=0.22, 359 | max_scale=2.8): 360 | """ 361 | 生成patch的一种方法 362 | """ 363 | W, H, _ = img.shape 364 | vertices = [[int(x) for x in pt.split(',')] 365 | for pt in vertices] 366 | vertices = format_4_vertices(vertices) 367 | 368 | # 将四个顶点转换成bbox(x_min, x_max, y_min, y_max) 369 | bbox = bbox_from_vertices(vertices) 370 | 371 | # 计算bbox是否满足尺度条件 372 | center_x = int(float(bbox[0] + bbox[1]) * 0.5 + 0.5) 373 | center_y = int(float(bbox[2] + bbox[3]) * 0.5 + 0.5) 374 | 375 | area = float(bbox[1] - bbox[0]) * (bbox[3] - bbox[2]) 376 | if area > 2500.0 * max_scale or area < 2500.0 * min_scale: 377 | # print('=> incompatible scale.') 378 | return None, None, None, None 379 | else: 380 | offset_x = random.uniform(-1.0, 1.0) * offset_radius 381 | offset_y = random.uniform(-1.0, 1.0) * offset_radius 382 | offset_x = int( 383 | offset_x + 0.5) if offset_x > 0.0 else int(offset_x - 0.5) 384 | offset_y = int( 385 | offset_y + 0.5) if offset_y > 0.0 else int(offset_y - 0.5) 386 | # print(offset_x, ' ', offset_y) 387 | 388 | org = [center_x - offset_x - 120, center_y - offset_y - 120] 389 | if org[0] < 0 or org[0] > W or \ 390 | org[1] < 0 or org[1] > H: 391 | return None, None, None, None 392 | else: 393 | bbox_leftup = [bbox[0] - org[0], bbox[2] - org[1]] 394 | bbox_rightdown = [bbox[1] - org[0], bbox[3] - org[1]] 395 | 396 | leftup = vertices[0] 397 | rightup = vertices[1] 398 | rightdown = vertices[2] 399 | leftdown = vertices[3] 400 | 401 | leftup = [leftup[0] - org[0], leftup[1] - org[1]] 402 | rightup = [rightup[0] - org[0], rightup[1] - org[1]] 403 | rightdown = [rightdown[0] - org[0], rightdown[1] - org[1]] 404 | leftdown = [leftdown[0] - org[0], leftdown[1] - org[1]] 405 | 406 | patch = img[org[1]: org[1] + 240, org[0]: org[0] + 240, :] 407 | 408 | return patch, bbox_leftup, bbox_rightdown, [leftup, 409 | rightup, 410 | rightdown, 411 | leftdown] 412 | 413 | 414 | def gen_patch_1(img, 415 | vertices, 416 | offset_radius=15, 417 | min_scale=0.22, 418 | max_scale=2.8): 419 | """ 420 | 生成patch的一种方法 421 | """ 422 | W, H, _ = img.shape 423 | vertices = [[int(x) for x in pt.split(',')] 424 | for pt in vertices] 425 | 426 | # 将四个顶点转换成bbox(leftup) 427 | bbox = bbox_from_vertices(vertices) 428 | 429 | # 计算bbox是否满足尺度条件 430 | center_x = int(float(bbox[0] + bbox[1]) * 0.5 + 0.5) 431 | center_y = int(float(bbox[2] + bbox[3]) * 0.5 + 0.5) 432 | area = float(bbox[1] - bbox[0]) * (bbox[3] - bbox[2]) 433 | if area > 2500.0 * max_scale or area < 2500.0 * min_scale: 434 | # print('=> incompatible scale.') 435 | return None, None, None 436 | else: 437 | offset_x = random.uniform(-1.0, 1.0) * offset_radius 438 | offset_y = random.uniform(-1.0, 1.0) * offset_radius 439 | offset_x = int( 440 | offset_x + 0.5) if offset_x > 0.0 else int(offset_x - 0.5) 441 | offset_y = int( 442 | offset_y + 0.5) if offset_y > 0.0 else int(offset_y - 0.5) 443 | # print(offset_x, ' ', offset_y) 444 | 445 | org = [center_x - offset_x - 120, center_y - offset_y - 120] 446 | if org[0] < 0 or org[0] > W or \ 447 | org[1] < 0 or org[1] > H: 448 | return None, None, None 449 | else: 450 | leftup = [bbox[0] - org[0], bbox[2] - org[1]] 451 | rightdown = [bbox[1] - org[0], bbox[3] - org[1]] 452 | 453 | patch = img[org[1]: org[1] + 240, org[0]: org[0] + 240, :] 454 | return patch, leftup, rightdown 455 | 456 | 457 | def pad_resize_img(img, size=(240, 240)): 458 | """ 459 | :param img: RGB image 460 | :return: 461 | """ 462 | img = np.array(img) # H x W x channels 463 | H, W, channels = img.shape 464 | dim_diff = np.abs(H - W) 465 | 466 | # upper(left) and lower(right) padding 467 | pad_lu = dim_diff // 2 # integer division 468 | pad_rd = dim_diff - pad_lu 469 | 470 | # determine padding for each axis: H, W, channels 471 | pad = ((pad_lu, pad_rd), (0, 0), (0, 0)) if H <= W else \ 472 | ((0, 0), (pad_lu, pad_rd), (0, 0)) 473 | 474 | # do padding(0.5) and normalize 475 | img = np.pad(img, 476 | pad, 477 | 'constant', 478 | constant_values=128.0) # / 255.0 479 | img = cv2.resize(img, 480 | size, 481 | cv2.INTER_CUBIC) 482 | return img 483 | 484 | 485 | def gen_patch_2(img, 486 | vertices, 487 | offset_radius=8): 488 | """ 489 | 从任意一张车牌生成patch 490 | """ 491 | W, H, _ = img.shape 492 | vertices = [[int(x) for x in pt.split(',')] 493 | for pt in vertices] 494 | bbox = bbox_from_vertices(vertices) 495 | center_x = int(float(bbox[0] + bbox[1]) * 0.5 + 0.5) 496 | center_y = int(float(bbox[2] + bbox[3]) * 0.5 + 0.5) 497 | bbox_w, bbox_h = int(bbox[1] - bbox[0]), int(bbox[3] - bbox[2]) 498 | # area = float(bbox_w) * (bbox_h) 499 | 500 | # offset_x = random.uniform(-1.0, 1.0) * offset_radius 501 | # offset_y = random.uniform(-1.0, 1.0) * offset_radius 502 | # offset_x = int(offset_x + 0.5) if offset_x > 0.0 else int(offset_x - 0.5) 503 | # offset_y = int(offset_y + 0.5) if offset_y > 0.0 else int(offset_y - 0.5) 504 | offset_x, offset_y = 0, 0 505 | print(offset_x, ' ', offset_y) 506 | 507 | patch_w, patch_h = int(float(bbox_w) * 4.8 + 508 | 0.5), int(float(bbox_h) * 4.8 + 0.5) 509 | if patch_w > 0 and patch_w < W \ 510 | and patch_h > 0 and patch_h < H: 511 | org_x = center_x - offset_x - int(patch_w * 0.5 + 0.5) 512 | org_y = center_y - offset_y - int(patch_h * 0.5 + 0.5) 513 | if org_x > 0 and org_x < W \ 514 | and org_y > 0 and org_y < H: 515 | 516 | # org = [center_x - offset_x - int(patch_w * 0.5 + 0.5), 517 | # center_y - offset_y - int(patch_h * 0.5 + 0.5)] 518 | 519 | end_x, end_y = org_x + patch_w, org_y + patch_h 520 | if end_x > 0 and end_x < W \ 521 | and end_y > 0 and end_y < H: 522 | patch = img[org_x: end_y, 523 | org_y: end_x, :] 524 | 525 | # patch = cv2.resize(patch, (240, 240), cv2.INTER_CUBIC) 526 | patch = pad_resize_img(img) 527 | return patch 528 | else: 529 | return None 530 | 531 | 532 | def gen_lp_patches(root, 533 | offset_radius=10, 534 | min_scale=0.25, 535 | max_scale=2.8, 536 | is_viz=False): 537 | """ 538 | 处理成DenseBox训练数据 539 | """ 540 | if not os.path.isdir(root): 541 | print('=> [Err]: invalid root.') 542 | return 543 | 544 | jpg_dir = root + '/JPEGImages' 545 | txt_dir = root + '/labels' 546 | if not (os.path.isdir(jpg_dir) and os.path.isdir(txt_dir)): 547 | print('=> [Err]: invalid dir.') 548 | return 549 | 550 | patch_dir = root + '/patches' 551 | if not os.path.isdir(patch_dir): 552 | os.makedirs(patch_dir) 553 | 554 | # process each img 555 | cnt = 0 556 | for img_name in tqdm(os.listdir(jpg_dir)): 557 | img_path = jpg_dir + '/' + img_name 558 | txt_path = txt_dir + '/' + img_name.replace('.jpg', '.txt') 559 | if os.path.isfile(img_path) and os.path.isfile(txt_path): 560 | img = cv2.imread(img_path, cv2.IMREAD_COLOR) 561 | with open(txt_path, 'r', encoding='utf-8') as txt_h: 562 | # process each plate 563 | for line in txt_h.readlines(): 564 | # 4个顶点 565 | vertices = line.strip().split(' ') 566 | 567 | assert len(vertices) == 4 568 | 569 | patch, leftup, rightdown = gen_patch_1(img, 570 | vertices, 571 | offset_radius, 572 | min_scale=min_scale, 573 | max_scale=max_scale) 574 | # patch = gen_patch_2(img, vertices, offset_radius=8) 575 | 576 | if patch is None: 577 | continue 578 | 579 | # -------------- 可视化 580 | if is_viz: 581 | cv2.circle(patch, tuple(leftup), 3, (0, 255, 255), -1) 582 | txt = 'LU' 583 | txt_size = cv2.getTextSize(text=txt, 584 | fontFace=cv2.FONT_HERSHEY_PLAIN, 585 | fontScale=1, 586 | thickness=1)[0] 587 | cv2.putText(img=patch, 588 | text=txt, 589 | org=(leftup[0] - txt_size[0], leftup[1]), 590 | fontFace=cv2.FONT_HERSHEY_PLAIN, 591 | fontScale=1, 592 | color=[225, 255, 255], 593 | thickness=1) 594 | 595 | cv2.circle(patch, tuple(rightdown), 596 | 3, (0, 255, 255), -1) 597 | txt = 'RD' 598 | txt_size = cv2.getTextSize(text=txt, 599 | fontFace=cv2.FONT_HERSHEY_PLAIN, 600 | fontScale=1, 601 | thickness=1)[0] 602 | cv2.putText(img=patch, 603 | text=txt, 604 | org=(rightdown[0] - 605 | txt_size[0], rightdown[1]), 606 | fontFace=cv2.FONT_HERSHEY_PLAIN, 607 | fontScale=1, 608 | color=[225, 255, 255], 609 | thickness=1) 610 | 611 | window_name = 'test' 612 | cv2.moveWindow(window_name, 200, 200) 613 | cv2.imshow(window_name, patch) 614 | cv2.waitKey() 615 | # -------------- 616 | 617 | # -------------- 生成label: leftup_x, leftup_y, rightdown_x, right_down_y 618 | coord_str_arr = [str(leftup[0]), 619 | str(leftup[1]), 620 | str(rightdown[0]), 621 | str(rightdown[1])] 622 | label = '_'.join(coord_str_arr) 623 | 624 | patch_path = patch_dir + '/' \ 625 | + img_name[:-4] + '_label_' + label + '.jpg' 626 | 627 | # 保存patch 628 | cv2.imwrite(patch_path, patch) 629 | 630 | cnt += 1 631 | print('=> %d patche generated.' % cnt) 632 | 633 | # print('\n') 634 | print('=> total %d patches' % cnt) 635 | 636 | 637 | # -------------------------------- generating patch with label containing formatted 4 corners 638 | 639 | def intersect_small(bboxes, patch_center, TH): 640 | """ 641 | 计算patch与bbox的intersection 642 | """ 643 | patch_x_min = patch_center[0] - 120 644 | patch_x_max = patch_center[0] + 120 645 | patch_y_min = patch_center[1] - 120 646 | patch_y_max = patch_center[1] + 120 647 | 648 | for bbox in bboxes: 649 | # bbox: leftup, rightup, rightdown, leftdown 650 | bbox_x_min, bbox_x_max = bbox[0][0], bbox[2][0] 651 | bbox_y_min, bbox_y_max = bbox[0][1], bbox[2][1] 652 | 653 | inter_w = min(bbox_x_max, patch_x_max) \ 654 | - max(bbox_x_min, patch_x_min) 655 | inter_h = min(bbox_y_max, patch_y_max) \ 656 | - max(bbox_y_min, patch_y_min) 657 | 658 | if inter_w < 0 or inter_h < 0: 659 | return True 660 | intersection = inter_w * inter_h 661 | print('=> intersection: ', intersection) 662 | 663 | if intersection > TH: 664 | return False 665 | 666 | return True 667 | 668 | 669 | def gen_pure_neg_patches(src_root, 670 | dst_root, 671 | num=1000, 672 | TH=0): 673 | """ 674 | 生成纯负样本的patch数据 675 | """ 676 | if not os.path.isdir(src_root): 677 | print('=> [Err]: invalid dir.') 678 | return 679 | 680 | src_jpg_dir = src_root + '/JPEGImages' 681 | src_txt_dir = src_root + '/labels' 682 | if not (os.path.isdir(src_jpg_dir) and os.path.isdir(src_txt_dir)): 683 | print('=> [Err]: invalid jpg or txt dir.') 684 | return 685 | 686 | if not os.path.isdir(dst_root): 687 | os.makedirs(dst_root) 688 | # else: 689 | # for x in os.listdir(dst_root): 690 | # x_path = dst_root + '/' + x 691 | # os.remove(x_path) 692 | 693 | imgs_path = [src_jpg_dir + '/' + 694 | img_name for img_name in os.listdir(src_jpg_dir)] 695 | pic_num = len(imgs_path) 696 | print('=> total %d src jpgs.' % (pic_num)) 697 | 698 | cnt = 0 699 | for item_i in range(num): 700 | # 随机选择一张图片 701 | rand_pic_id = int(np.random.choice(pic_num, 1, replace=True)[0]) 702 | # print('=> rand pic id: ', rand_pic_id) 703 | 704 | img_path = imgs_path[int(rand_pic_id)] 705 | img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) 706 | if img is None: 707 | continue 708 | 709 | H, W, num_chans = img.shape 710 | 711 | # 生成patch中心点 712 | center_x = np.random.choice( 713 | np.arange(120, W - 120), 1, replace=True)[0] 714 | center_y = np.random.choice( 715 | np.arange(120, H - 120), 1, replace=True)[0] 716 | print('=> patch center:(%d, %d)' % (center_x, center_y)) 717 | 718 | # 生成一个patch 719 | patch = img[center_y-120: center_y + 120, # H 720 | center_x-120: center_x + 120, # W 721 | :] # num_channels 722 | if patch is None: 723 | continue 724 | 725 | # 读取bboxes 726 | txt_path = str(src_txt_dir + '/' + os.path.split(img_path) 727 | [1]).replace('.jpg', '.txt') 728 | if os.path.isfile(txt_path): 729 | bboxes = [] 730 | 731 | # 读取每一个bbox 732 | with open(txt_path, 'r', encoding='utf-8') as f_h: 733 | for line in f_h.readlines(): 734 | line = line.strip().split(' ') 735 | vertices = [[int(x) for x in pt.split(',')] 736 | for pt in line] 737 | 738 | vertices = format_4_vertices(vertices=vertices) 739 | bboxes.append(vertices) 740 | 741 | # 计算intersection是否超过阈值 742 | if intersect_small(bboxes, [center_x, center_y], TH=TH): 743 | 744 | # 将满足条件的patch写入目标目录 745 | dst_path = dst_root + '/' + \ 746 | os.path.split(img_path)[1][:-4] + \ 747 | '_label_0_0_0_0_0_0_0_0_0_0_0_0' + '.jpg' 748 | if not os.path.isfile(dst_path): 749 | cv2.imwrite(dst_path, patch) 750 | cnt += 1 751 | print('=> item %d done\n' % (item_i)) 752 | print('=> total %d negative patch sample generated.' % (cnt)) 753 | 754 | 755 | def gen_lplm_pos_patches(src_root, 756 | dst_root, 757 | offset_radius=10, 758 | is_viz=False): 759 | """ 760 | 处理成DenseBox训练数据, 生成带标签的positive patch 761 | 非等比缩放到指定尺寸 762 | """ 763 | if not os.path.isdir(src_root): 764 | print('=> [Err]: invalid root.') 765 | return 766 | 767 | jpg_dir = src_root + '/JPEGImages' 768 | txt_dir = src_root + '/labels' 769 | if not (os.path.isdir(jpg_dir) and os.path.isdir(txt_dir)): 770 | print('=> [Err]: invalid dir.') 771 | return 772 | 773 | # dst_root = src_root + '/patchesLM' 774 | # if not os.path.isdir(dst_root): 775 | # os.makedirs(dst_root) 776 | 777 | # process each img 778 | cnt = 0 # patch计数 779 | for item_i, img_name in enumerate(os.listdir(jpg_dir)): 780 | img_path = jpg_dir + '/' + img_name 781 | txt_path = txt_dir + '/' + img_name.replace('.jpg', '.txt') 782 | if os.path.isfile(img_path) and os.path.isfile(txt_path): 783 | img = cv2.imread(img_path, cv2.IMREAD_COLOR) 784 | with open(txt_path, 'r', encoding='utf-8') as txt_h: 785 | # process each plate 786 | for line in txt_h.readlines(): 787 | # 4个顶点 788 | vertices = line.strip().split(' ') 789 | 790 | assert len(vertices) == 4 791 | 792 | # 生成patch和坐标 793 | patch, bbox_leftup, bbox_rightdown, vertices = gen_pos_patch(img=img, 794 | vertices=vertices, 795 | offset_radius=15, 796 | size=(240, 240)) 797 | if patch is None: 798 | continue 799 | 800 | # -------------- 生成label: 801 | leftup = vertices[0] # 0 802 | rightup = vertices[1] # 1 803 | rightdown = vertices[2] # 2 804 | leftdown = vertices[3] # 3 805 | 806 | coords_str = [str(bbox_leftup[0]), 807 | str(bbox_leftup[1]), # bbox_leftup 808 | str(bbox_rightdown[0]), 809 | str(bbox_rightdown[1]), # bbox_rightdown 810 | 811 | str(leftup[0]), 812 | str(leftup[1]), # leftup 813 | 814 | str(rightup[0]), 815 | str(rightup[1]), # rightup 816 | 817 | str(rightdown[0]), 818 | str(rightdown[1]), # rightdown 819 | 820 | str(leftdown[0]), 821 | str(leftdown[1])] # leftdown 822 | 823 | label = '_'.join(coords_str) 824 | patch_path = dst_root + '/' \ 825 | + img_name[:-4] + '_label_' + label + '.jpg' 826 | 827 | # 保存patch 828 | cv2.imwrite(patch_path, patch) 829 | 830 | cnt += 1 831 | print('=> %d patch generated\n' % (cnt)) 832 | 833 | print('=> %s proceesed, %.3f%% completed.' 834 | % (img_name, 835 | float(item_i + 1) / float(len(os.listdir(jpg_dir))) * 100.0)) 836 | 837 | print('=> total %d patches' % cnt) 838 | 839 | 840 | def gen_lplm_patches(root, 841 | offset_radius=10, 842 | min_scale=0.25, 843 | max_scale=2.8, 844 | is_viz=False): 845 | """ 846 | 处理成DenseBox训练数据, 生成带标签的patch 847 | """ 848 | if not os.path.isdir(root): 849 | print('=> [Err]: invalid root.') 850 | return 851 | 852 | jpg_dir = root + '/JPEGImages' 853 | txt_dir = root + '/labels' 854 | if not (os.path.isdir(jpg_dir) and os.path.isdir(txt_dir)): 855 | print('=> [Err]: invalid dir.') 856 | return 857 | 858 | patch_dir = root + '/patchesLM' 859 | if not os.path.isdir(patch_dir): 860 | os.makedirs(patch_dir) 861 | 862 | # process each img 863 | cnt = 0 864 | for item_i, img_name in enumerate(os.listdir(jpg_dir)): 865 | img_path = jpg_dir + '/' + img_name 866 | txt_path = txt_dir + '/' + img_name.replace('.jpg', '.txt') 867 | if os.path.isfile(img_path) and os.path.isfile(txt_path): 868 | img = cv2.imread(img_path, cv2.IMREAD_COLOR) 869 | with open(txt_path, 'r', encoding='utf-8') as txt_h: 870 | # process each plate 871 | for line in txt_h.readlines(): 872 | # 4个顶点 873 | vertices = line.strip().split(' ') 874 | 875 | assert len(vertices) == 4 876 | 877 | patch, bbox_leftup, bbox_rightdown, vertices = gen_patch_lm(img=img, 878 | vertices=vertices, 879 | offset_radius=offset_radius, 880 | min_scale=min_scale, 881 | max_scale=max_scale) 882 | 883 | if patch is None: 884 | continue 885 | 886 | # -------------- 可视化 887 | # 绘制每个顶点 888 | if is_viz: 889 | # left-up 890 | pt = tuple(vertices[0]) 891 | txt = 'LU' 892 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 893 | txt_size = cv2.getTextSize(text=txt, 894 | fontFace=cv2.FONT_HERSHEY_PLAIN, 895 | fontScale=1, 896 | thickness=1)[0] 897 | cv2.putText(img=patch, 898 | text=txt, 899 | org=( 900 | pt[0] - txt_size[0], pt[1]), 901 | fontFace=cv2.FONT_HERSHEY_PLAIN, 902 | fontScale=1, 903 | color=[225, 255, 255], 904 | thickness=1) 905 | 906 | # right-up 907 | pt = tuple(vertices[1]) 908 | txt = 'RU' 909 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 910 | txt_size = cv2.getTextSize(text=txt, 911 | fontFace=cv2.FONT_HERSHEY_PLAIN, 912 | fontScale=1, 913 | thickness=1)[0] 914 | cv2.putText(img=patch, 915 | text=txt, 916 | org=(pt[0], pt[1]), 917 | fontFace=cv2.FONT_HERSHEY_PLAIN, 918 | fontScale=1, 919 | color=[225, 255, 255], 920 | thickness=1) 921 | 922 | # right-down 923 | pt = tuple(vertices[2]) 924 | txt = 'RD' 925 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 926 | txt_size = cv2.getTextSize(text=txt, 927 | fontFace=cv2.FONT_HERSHEY_PLAIN, 928 | fontScale=1, 929 | thickness=1)[0] 930 | cv2.putText(img=patch, 931 | text=txt, 932 | org=(pt[0], 933 | pt[1] + txt_size[1]), 934 | fontFace=cv2.FONT_HERSHEY_PLAIN, 935 | fontScale=1, 936 | color=[225, 255, 255], 937 | thickness=1) 938 | 939 | # left-down 940 | pt = tuple(vertices[3]) 941 | txt = 'LD' 942 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 943 | txt_size = cv2.getTextSize(text=txt, 944 | fontFace=cv2.FONT_HERSHEY_PLAIN, 945 | fontScale=1, 946 | thickness=1)[0] 947 | cv2.putText(img=patch, 948 | text=txt, 949 | org=(pt[0] - txt_size[0], 950 | pt[1] + txt_size[1]), 951 | fontFace=cv2.FONT_HERSHEY_PLAIN, 952 | fontScale=1, 953 | color=[225, 255, 255], 954 | thickness=1) 955 | 956 | window_name = 'test' 957 | cv2.moveWindow(window_name, 200, 200) 958 | cv2.imshow(window_name, patch) 959 | cv2.waitKey() 960 | # -------------- 961 | 962 | # -------------- 生成label: 963 | leftup = vertices[0] # 0 964 | rightup = vertices[1] # 1 965 | rightdown = vertices[2] # 2 966 | leftdown = vertices[3] # 3 967 | 968 | coords_str = [str(bbox_leftup[0]), 969 | str(bbox_leftup[1]), # bbox_leftup 970 | str(bbox_rightdown[0]), 971 | str(bbox_rightdown[1]), # bbox_rightdown 972 | 973 | str(leftup[0]), 974 | str(leftup[1]), # leftup 975 | 976 | str(rightup[0]), 977 | str(rightup[1]), # rightup 978 | 979 | str(rightdown[0]), 980 | str(rightdown[1]), # rightdown 981 | 982 | str(leftdown[0]), 983 | str(leftdown[1])] # leftdown 984 | 985 | label = '_'.join(coords_str) 986 | patch_path = patch_dir + '/' \ 987 | + img_name[:-4] + '_label_' + label + '.jpg' 988 | 989 | # 保存patch 990 | cv2.imwrite(patch_path, patch) 991 | 992 | cnt += 1 993 | print('=> %d patch generated\n' % (cnt)) 994 | 995 | # print('\n') 996 | 997 | print('=> %s proceesed, %.3f%% completed.' 998 | % (img_name, 999 | float(item_i + 1) / float(len(os.listdir(jpg_dir))) * 100.0)) 1000 | 1001 | print('=> total %d patches' % cnt) 1002 | 1003 | 1004 | def viz_patch(root): 1005 | """ 1006 | 可视化license plate patch 1007 | """ 1008 | 1009 | 1010 | def viz_vertices(root): 1011 | """ 1012 | 可视化车牌的四个顶点 1013 | """ 1014 | viz_res_dir = root + '/' + 'viz_result' 1015 | if not os.path.exists(viz_res_dir): 1016 | os.makedirs(viz_res_dir) 1017 | else: 1018 | for x in os.listdir(viz_res_dir): 1019 | x_path = viz_res_dir + '/' + x 1020 | os.remove(x_path) 1021 | 1022 | JPEG_dir = root + '/' + 'JPEGImages' 1023 | label_dir = root + '/' + 'labels' 1024 | 1025 | viz_vertex(JPEG_dir, label_dir, viz_res_dir) 1026 | 1027 | 1028 | def viz_vertex(JPEG_dir, label_dir, viz_res_dir): 1029 | for x in tqdm(os.listdir(JPEG_dir)): 1030 | if x.endswith('.jpg'): 1031 | img_path = JPEG_dir + '/' + x 1032 | if os.path.isfile(img_path): 1033 | label_path = label_dir + '/' + x[:-4] + '.txt' 1034 | if os.path.isfile(label_path): 1035 | # 读取原图 1036 | img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) 1037 | 1038 | # 读取label 1039 | with open(label_path, 'r', encoding='utf-8') as f_label: 1040 | # 绘制每一个车牌 1041 | for line in f_label.readlines(): 1042 | vertices = line.split(' ') 1043 | 1044 | assert len(vertices) == 4 # 4个顶点 1045 | 1046 | # 绘制每个顶点 1047 | for pt in vertices: 1048 | if vertices.index(pt) == 0: # test left-up 1049 | pt = tuple([int(x) for x in pt.split(',')]) 1050 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 1051 | txt = 'LU' 1052 | txt_size = cv2.getTextSize(text=txt, 1053 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1054 | fontScale=1, 1055 | thickness=1)[0] 1056 | cv2.putText(img=img, 1057 | text=txt, 1058 | org=( 1059 | pt[0] - txt_size[0], pt[1]), 1060 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1061 | fontScale=1, 1062 | color=[225, 255, 255], 1063 | thickness=1) 1064 | elif vertices.index(pt) == 1: # test right-up 1065 | pt = tuple([int(x) for x in pt.split(',')]) 1066 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 1067 | txt = 'RU' 1068 | txt_size = cv2.getTextSize(text=txt, 1069 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1070 | fontScale=1, 1071 | thickness=1)[0] 1072 | cv2.putText(img=img, 1073 | text=txt, 1074 | org=(pt[0], pt[1]), 1075 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1076 | fontScale=1, 1077 | color=[225, 255, 255], 1078 | thickness=1) 1079 | elif vertices.index(pt) == 2: # test right-down 1080 | pt = tuple([int(x) for x in pt.split(',')]) 1081 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 1082 | 1083 | txt = 'RD' 1084 | txt_size = cv2.getTextSize(text=txt, 1085 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1086 | fontScale=1, 1087 | thickness=1)[0] 1088 | 1089 | cv2.putText(img=img, 1090 | text=txt, 1091 | org=(pt[0], 1092 | pt[1] + txt_size[1]), 1093 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1094 | fontScale=1, 1095 | color=[225, 255, 255], 1096 | thickness=1) 1097 | else: # test left-down 1098 | pt = tuple([int(x) for x in pt.split(',')]) 1099 | cv2.circle(img, pt, 5, (0, 255, 255), -1) 1100 | 1101 | txt = 'LD' 1102 | txt_size = cv2.getTextSize(text=txt, 1103 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1104 | fontScale=1, 1105 | thickness=1)[0] 1106 | 1107 | cv2.putText(img=img, 1108 | text=txt, 1109 | org=(pt[0] - txt_size[0], 1110 | pt[1] + txt_size[1]), 1111 | fontFace=cv2.FONT_HERSHEY_PLAIN, 1112 | fontScale=1, 1113 | color=[225, 255, 255], 1114 | thickness=1) 1115 | # cv2.imshow('test', img) 1116 | # cv2.waitKey() 1117 | 1118 | res_path = viz_res_dir + '/' + x 1119 | cv2.imwrite(res_path, img) 1120 | 1121 | 1122 | # 解析xml并转换成labels 1123 | def xml2label(img_path, xml_path, label_path, f_name): 1124 | """ 1125 | :param img_path: 1126 | :param xml_path: 1127 | :param label_path: 1128 | :param file_name: 1129 | :return: 1130 | """ 1131 | xml_f_name = xml_path + '/' + f_name + '.xml' 1132 | label_f_name = label_path + '/' + f_name + '.txt' 1133 | in_file = open(xml_f_name, 'r', encoding='utf-8') # 指定打开xml的编码方式 1134 | out_file = open(label_f_name, 'w', encoding='utf-8') 1135 | 1136 | # 解析xml字段 1137 | tree = ET.parse(in_file) 1138 | root = tree.getroot() 1139 | size = root.find('size') 1140 | W = int(size.find('width').text) # 整张图宽度 1141 | H = int(size.find('height').text) # 整张图高度 1142 | # print('w: %d, h: %d' % (W, H)) 1143 | 1144 | # 处理一张图 1145 | for obj in root.iter('object'): # object字段 1146 | cls = obj.find('name').text # 类别: 车牌... 1147 | if cls not in classes: 1148 | continue 1149 | 1150 | # 处理图中的每一个车牌 1151 | for plate in obj.iter('plate'): # plate字段 1152 | vertices = plate.find('vertexs') 1153 | if None == vertices or 4 != len(vertices): 1154 | continue 1155 | 1156 | vertices = [(int(v.find('x').text), int(v.find('y').text)) 1157 | for v in vertices] 1158 | vertices = format_lp_vertices(vertices) 1159 | 1160 | out_file.write(' '.join([str(x).replace('(', '').replace( 1161 | ')', '').replace(' ', '') for x in vertices]) + '\n') 1162 | 1163 | # 释放资源 1164 | in_file.close() 1165 | out_file.close() 1166 | 1167 | 1168 | def parse_xml(in_file): 1169 | """ 1170 | @param in_file: file handle of read xml 1171 | """ 1172 | tree = ET.parse(in_file) 1173 | root = tree.getroot() 1174 | size = root.find('size') 1175 | W = int(size.find('width').text) # 整张图宽度 1176 | H = int(size.find('height').text) # 整张图高度 1177 | # print('w: %d, h: %d' % (W, H)) 1178 | 1179 | # 处理一张图 1180 | label = [] 1181 | for obj in root.iter('object'): # object字段 1182 | cls = obj.find('name').text # 类别: 车牌... 1183 | if cls not in classes: 1184 | continue 1185 | 1186 | # 处理图中的每一个车牌 1187 | for plate in obj.iter('plate'): # plate字段 1188 | vertices = plate.find('vertexs') 1189 | if None == vertices or 4 != len(vertices): 1190 | continue 1191 | 1192 | vertices = [(int(v.find('x').text), int(v.find('y').text)) 1193 | for v in vertices] 1194 | vertices = format_lp_vertices(vertices) 1195 | label.append(vertices) 1196 | return label 1197 | 1198 | 1199 | # 分割预处理结束后的数据集 1200 | def split_data(path_dir, thresh): 1201 | """ 1202 | :param path_dir: 包含all.txt(所有文件目录的) 1203 | :param thresh: 1204 | :return: 1205 | """ 1206 | f = open(os.path.join(path_dir, 'all.txt'), 'r') 1207 | train_list = open(os.path.join(path_dir, 'train.txt'), 'w') 1208 | valid_list = open(os.path.join(path_dir, 'valid.txt'), 'w') 1209 | 1210 | for line in f.readlines(): 1211 | if random.random() < thresh: 1212 | valid_list.write(line) 1213 | else: 1214 | train_list.write(line) 1215 | 1216 | # 释放资源 1217 | f.close() 1218 | train_list.close() 1219 | valid_list.close() 1220 | print('=> splitting done.') 1221 | 1222 | 1223 | def generate_labels(root_dir, img_path, xml_path, label_path): 1224 | """ 1225 | :param root_dir: 1226 | :param img_path: 1227 | :param xml_path: 1228 | :param label_path: 1229 | :return: None 1230 | """ 1231 | all_files = open(root_dir + '/' + 'all.txt', 'r') 1232 | for i, line in enumerate(all_files.readlines()): 1233 | # print('line: %s, len(line): %s\k' % (line, len(line))) 1234 | f_name = os.path.split(line)[1].strip('\n')[:-4] 1235 | 1236 | print('\n=> processing number %d: %s' % (i + 1, f_name)) 1237 | xml2label(img_path, xml_path, label_path, f_name) 1238 | if i % 100 == 0: 1239 | print('=> {} labels generated'.format(i + 1)) 1240 | 1241 | # 释放资源 1242 | all_files.close() 1243 | # print('=> bbox max width: %d, max height: %d' %(bbox_max_width, bbox_max_height)) 1244 | print('=> generating all labels done.') 1245 | 1246 | 1247 | def bbox_from_vertices(vertices): 1248 | """ 1249 | get bounding box from 4 vertices: 1250 | 0 1 2 3 1251 | x_min, x_max, y_min, y_max 1252 | """ 1253 | return min(vertices[0][0], vertices[3][0]), \ 1254 | max(vertices[1][0], vertices[2][0]), \ 1255 | min(vertices[0][1], vertices[1][1]), \ 1256 | max(vertices[3][1], vertices[2][1]) 1257 | 1258 | 1259 | global count_crop_id 1260 | count_crop_id = 0 1261 | 1262 | 1263 | def rand_crop_around_center(root, 1264 | fix_size=256, 1265 | border=15): 1266 | """ 1267 | 围绕中心随机裁剪固定大小的图像块 1268 | 生成新的训练数据和标签 1269 | """ 1270 | if fix_size <= (border + border): 1271 | print('[Err]: incompatible border and fix_size.') 1272 | 1273 | # 清空目标目录 1274 | crop_img_dir = root + '/CroppedImages/' 1275 | crop_label_dir = root + '/CroppedLabels/' 1276 | img_list = os.listdir(crop_img_dir) 1277 | label_list = os.listdir(crop_label_dir) 1278 | if len(img_list) != 0: 1279 | for x in img_list: 1280 | x_path = crop_img_dir + x 1281 | if os.path.exists(x_path): 1282 | os.remove(x_path) 1283 | if len(label_list) != 0: 1284 | for x in label_list: 1285 | x_path = crop_label_dir + x 1286 | if os.path.exists(x_path): 1287 | os.remove(x_path) 1288 | 1289 | global count_crop_id 1290 | 1291 | all_files = open(root + '/' + 'all.txt', 'r') 1292 | for i, line in enumerate(all_files.readlines()): 1293 | # print('line: %s, len(line): %s\k' % (line, len(line))) 1294 | f_name = os.path.split(line)[1].strip('\n')[: -4] 1295 | img_f_path = root + '/JPEGImages/' + f_name + '.jpg' 1296 | xml_f_path = root + '/Annotations/' + f_name + '.xml' 1297 | 1298 | if os.path.isfile(img_f_path) and os.path.isfile(xml_f_path): 1299 | with open(xml_f_path, 'r', encoding='utf-8') as f_xml: 1300 | label = parse_xml(f_xml) 1301 | 1302 | # 处理每一个车牌 1303 | for vertices in label: 1304 | # 确定leftup坐标范围 1305 | if None != vertices and len(vertices) == 4: 1306 | bbox = bbox_from_vertices(vertices) 1307 | x_min = bbox[0] - \ 1308 | (fix_size - (bbox[1] - bbox[0])) + border 1309 | x_max = bbox[0] - border 1310 | y_min = bbox[2] - \ 1311 | (fix_size - (bbox[3] - bbox[2])) + border 1312 | y_max = bbox[2] - border 1313 | if x_max > x_min and y_max > y_min: 1314 | # 随机leftup坐标 1315 | left_up = x_min + int(random.random() * (x_max - x_min)), \ 1316 | y_min + int(random.random() * (y_max - y_min)) 1317 | # print('=> leftup: ', left_up) 1318 | 1319 | # 格式化 1320 | img = cv2.imread(img_f_path, cv2.IMREAD_UNCHANGED) 1321 | if img.shape[2] == 3: # turn all 3 channels to RGB format 1322 | image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 1323 | elif img.shape[2] == 1: # turn 1 channel to RGB 1324 | img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) 1325 | 1326 | # 固定尺寸裁剪 1327 | ROI = img[left_up[1]: left_up[1] + fix_size, 1328 | left_up[0]: left_up[0] + fix_size] 1329 | if ROI.shape == (fix_size, fix_size, 3): 1330 | # 生成ROI 1331 | id_name = '{:0>6d}'.format(count_crop_id) 1332 | format_name = type_name + date_name + id_name 1333 | dst_f_path = crop_img_dir + format_name + '.jpg' 1334 | cv2.imwrite(dst_f_path, ROI) 1335 | 1336 | # 对vertices转换坐标系 1337 | vertices = [ 1338 | (coord[0] - left_up[0], coord[1] - left_up[1]) for coord in vertices] 1339 | 1340 | # 生成label 1341 | label_f_path = crop_label_dir + format_name + '.txt' 1342 | f_label = open( 1343 | label_f_path, 'w', encoding='utf-8') 1344 | f_label.write(' '.join([str(x).replace('(', '').replace( 1345 | ')', '').replace(' ', '') for x in vertices]) + '\n') 1346 | f_label.close() 1347 | 1348 | count_crop_id += 1 1349 | # cv2.imshow('test', ROI) 1350 | # cv2.waitKey() 1351 | print('\n=> processing number %d: %s' % (i + 1, f_name)) 1352 | 1353 | 1354 | def process_batch(src_root, dst_root): 1355 | """ 1356 | :param src_root: 1357 | :param dst_root: 1358 | :return: None 1359 | """ 1360 | # step-1: 创建子目录 1361 | img_path = dst_root + os.path.sep + 'JPEGImages' 1362 | xml_path = dst_root + os.path.sep + 'Annotations' 1363 | label_path = dst_root + os.path.sep + 'labels' 1364 | 1365 | # 第一次会创建子目录, 然后复用 1366 | if not os.path.exists(dst_root): 1367 | os.makedirs(dst_root) 1368 | print('=> %s made.' % dst_root) 1369 | else: 1370 | print('=> %s already exists.' % dst_root) 1371 | 1372 | if not os.path.exists(img_path): 1373 | os.makedirs(img_path) 1374 | print('=> %s made.' % img_path) 1375 | else: 1376 | print('=> %s already exists.' % img_path) 1377 | 1378 | if not os.path.exists(xml_path): 1379 | os.makedirs(xml_path) 1380 | print('=> %s made.' % xml_path) 1381 | else: 1382 | print('=> %s already exists.' % xml_path) 1383 | 1384 | if not os.path.exists(label_path): 1385 | os.makedirs(label_path) 1386 | print('=> %s made.' % label_path) 1387 | else: 1388 | print('=> %s already exists.' % label_path) 1389 | # ---------------------------三个子目录创建完成 1390 | 1391 | # step-2: 切分原始数据集并重命名(图像数据和xml标注文件) 1392 | # move_rename(src_root, img_path, xml_path) 1393 | 1394 | # step-3: 生成图像文件绝对路径列表(all.txt) 1395 | get_img_path_list(img_path) 1396 | 1397 | # step-4: 转换annotations(解析xml, 生成labels) 1398 | generate_labels(dst_root, img_path, xml_path, label_path) 1399 | 1400 | # step-5: 将数据集分割为训练数据集和验证数据集 1401 | split_data(dst_root, 0.0) 1402 | 1403 | print('=> Test done.') 1404 | 1405 | 1406 | def get_outfile_name(init_counter=0): 1407 | outfile_counter = init_counter 1408 | type_label = "ocr" 1409 | # date_label = time.strftime('%4Y%2m%2d', time.localtime(time.time())) 1410 | # id_label = "%06d"%(count_id) 1411 | 1412 | while True: 1413 | id_label = '{:0>6d}'.format(outfile_counter) 1414 | yield type_label + date_name + id_label 1415 | outfile_counter += 1 1416 | 1417 | 1418 | # 递归搜索包含jpg的目录 1419 | def get_src_path(root, dirs): 1420 | for file in os.listdir(root): 1421 | file_path = os.path.join(root, file) 1422 | if os.path.isdir(file_path): 1423 | get_src_path(file_path, dirs) 1424 | else: 1425 | if os.path.isfile(file_path) and file_path.endswith('.jpg'): 1426 | dirs.append(root) 1427 | break 1428 | 1429 | 1430 | def test_8_neighbor_CA(): 1431 | """ 1432 | 测试8邻域连通区域检测 1433 | """ 1434 | img = cv2.imread('e:/gauss.jpg', cv2.IMREAD_UNCHANGED) 1435 | print('=> img.shape: ', img.shape) 1436 | print('=> img:\n', img) 1437 | cv2.imshow('test', img) 1438 | cv2.waitKey() 1439 | 1440 | # ret, img_bin = cv2.threshold(src=img, 1441 | # thresh=127, 1442 | # maxval=255, 1443 | # type=cv2.THRESH_BINARY) 1444 | # cv2.imshow('test', img_bin) 1445 | # cv2.waitKey() 1446 | 1447 | img = img / 255.0 1448 | 1449 | ret, img_bin = cv2.threshold(src=img, 1450 | thresh=0.6, 1451 | maxval=1.0, 1452 | type=cv2.THRESH_BINARY) 1453 | img_bin = img_bin.astype(np.uint8) 1454 | 1455 | print(type(img_bin[0][0])) 1456 | print('=> img_bin:\n', img_bin[:10]) 1457 | # cv2.imshow('test', img_bin) 1458 | # cv2.waitKey() 1459 | 1460 | _, labels, stats, centroids = cv2.connectedComponentsWithStats(img_bin) 1461 | print('=> labels[:10]:\n', labels[:10]) 1462 | print('=> labels[10:20]:\n', labels[10:20]) 1463 | print('=> labels[20:]:\n', labels[20:]) 1464 | print('\n=> stats:\n', stats) 1465 | print('\n=> centroids:\n', centroids) 1466 | 1467 | # np.where(img) 1468 | 1469 | 1470 | def test_forward(): 1471 | """ 1472 | 测试前向运算 1473 | """ 1474 | net = LP_Net() 1475 | net.load_state_dict(torch.load( 1476 | 'e:/plate_data_pro/checkpoint/epoch_780.pth')) 1477 | net.eval() 1478 | 1479 | img = Image.open( 1480 | 'e:/plate_data_pro/CroppedImages/det_2018_09_19_000013.jpg') 1481 | if img.mode == 'L' or img.mode == 'I': 1482 | img = img.convert('RGB') 1483 | 1484 | transform = T.Compose([ 1485 | T.Resize(256), 1486 | T.CenterCrop(256), 1487 | T.ToTensor(), 1488 | T.Normalize(mean=[0.485, 0.456, 0.406], 1489 | std=[0.229, 0.224, 0.225]) 1490 | ]) 1491 | img = transform(img) 1492 | img = img.view(1, 3, 256, 256) 1493 | 1494 | output = net.forward(img) 1495 | print(output.shape) 1496 | 1497 | # 转换成numpy.ndarray并可视化 1498 | leftup_map = output[0][0].detach().numpy() 1499 | rightdown_map = output[0][1].detach().numpy() 1500 | leftup_map = leftup_map.astype(np.uint8) 1501 | rightdown_map = rightdown_map.astype(np.uint8) 1502 | 1503 | left_non_0_ids = np.where(leftup_map != 0) 1504 | print('=> leftup non zero ids:', left_non_0_ids) 1505 | 1506 | cv2.imwrite('e:/leftup_map.jpg', leftup_map) 1507 | cv2.imwrite('e:/rightdown_map.jpg', rightdown_map) 1508 | 1509 | cv2.imshow('leftup_map', leftup_map) 1510 | cv2.waitKey() 1511 | cv2.imshow('rightdown_map', rightdown_map) 1512 | cv2.waitKey() 1513 | 1514 | print(output[0][0][:10]) 1515 | print('\n', output[0][0][10:20]) 1516 | print('\n', output[0][0][20:]) 1517 | 1518 | 1519 | def test_random_choice(): 1520 | """ 1521 | 测试numpy.random.choice 1522 | """ 1523 | a = np.array([19, 71, 36, 81, 69, 55]) 1524 | # for i in range(10): 1525 | # select = np.random.choice(a, 4, replace=False) 1526 | # print('=> select: ', select) 1527 | 1528 | for i in range(10): 1529 | select = np.random.choice(len(a), 4, replace=False) 1530 | print('=> select_ids: ', select) 1531 | 1532 | 1533 | def vertex2bbox_corner(vertices): 1534 | """ 1535 | input 4 vertices(8 coordinates) and output 2 corners of bbox 1536 | """ 1537 | assert len(vertices) == 4 1538 | x1 = min(int(vertices[0][0]), int(vertices[3][0])) 1539 | y1 = min(int(vertices[0][1]), int(vertices[1][1])) 1540 | x2 = max(int(vertices[1][0]), int(vertices[2][0])) 1541 | y2 = max(int(vertices[3][1]), int(vertices[2][1])) 1542 | assert x1 >= 0 and y1 >= 0 and x1 < x2 and y1 < y2 1543 | return str(x1), str(y1), str(x2), str(y2) 1544 | 1545 | 1546 | def vertex2bbox(vertices): 1547 | """ 1548 | input 4 vertices(8 coordinates) and output left, right, top, bottom 1549 | """ 1550 | assert len(vertices) == 4 1551 | x1 = min(int(vertices[0][0]), int(vertices[3][0])) 1552 | y1 = min(int(vertices[0][1]), int(vertices[1][1])) 1553 | x2 = max(int(vertices[1][0]), int(vertices[2][0])) 1554 | y2 = max(int(vertices[3][1]), int(vertices[2][1])) 1555 | assert x1 >= 0 and y1 >= 0 and x1 < x2 and y1 < y2 1556 | return str(x1), str(x2), str(y1), str(y2) 1557 | 1558 | 1559 | def label2anno(label_dir, anno_dir): 1560 | """ 1561 | 从label格式转换成MTCNN格式 1562 | """ 1563 | if not os.path.isdir(label_dir): 1564 | print('[Err]: invalid src dir.') 1565 | return 1566 | 1567 | # JPEG目录 1568 | jpg_dir = label_dir.replace('labels', 'JPEGImages') 1569 | if not os.path.isdir(jpg_dir): 1570 | print('[Err]: invalid jpg dir.') 1571 | 1572 | if not os.path.isdir(anno_dir): 1573 | os.makedirs(anno_dir) 1574 | anno_file = anno_dir + '/' + 'bbox_annos.txt' 1575 | 1576 | # 格式转换: 将所有label写到一个txt文件 1577 | i = 0 1578 | with open(anno_file, 'w') as anno_h: 1579 | for x in os.listdir(label_dir): 1580 | x_path = label_dir + '/' + x 1581 | jpg_path = x_path.replace( 1582 | 'labels', 'JPEGImages').replace('.txt', '.jpg') 1583 | if os.path.isfile(x_path) and os.path.isfile(jpg_path): 1584 | jpg_name = os.path.split(jpg_path)[-1] 1585 | with open(x_path, 'r') as f_h: 1586 | # 写文件相对路径 1587 | anno_h.write(jpg_name) 1588 | 1589 | # 写bbox_corner坐标 1590 | for line in f_h.readlines(): 1591 | vertices = [x.split(',') 1592 | for x in line.strip().split(' ')] 1593 | coorners = vertex2bbox_corner(vertices) 1594 | coordinates = ' ' + ' '.join(coorners) 1595 | anno_h.write(coordinates) 1596 | anno_h.write('\n') 1597 | i += 1 1598 | if i % 1000 == 0: 1599 | print('=> %d labels processed.' % i) 1600 | 1601 | 1602 | def label2bbox_landmark(label_dir, anno_dir): 1603 | """ 1604 | 将车牌标注信息转换成: 路径 + bbox_corners + landmarks 1605 | """ 1606 | if not os.path.isdir(label_dir): 1607 | print('[Err]: invalid src dir.') 1608 | return 1609 | 1610 | # JPEG目录 1611 | jpg_dir = label_dir.replace('labels', 'JPEGImages') 1612 | if not os.path.isdir(jpg_dir): 1613 | print('[Err]: invalid jpg dir.') 1614 | 1615 | if not os.path.isdir(anno_dir): 1616 | os.makedirs(anno_dir) 1617 | anno_file = anno_dir + '/' + 'bbox_landmark.txt' 1618 | 1619 | # 格式转换: 将所有label写到一个txt文件 1620 | i = 0 1621 | with open(anno_file, 'w') as anno_h: 1622 | for x in os.listdir(label_dir): 1623 | x_path = label_dir + '/' + x 1624 | jpg_path = x_path.replace( 1625 | 'labels', 'JPEGImages').replace('.txt', '.jpg') 1626 | if os.path.isfile(x_path) and os.path.isfile(jpg_path): 1627 | jpg_name = os.path.split(jpg_path)[-1] 1628 | with open(x_path, 'r') as f_h: 1629 | # 过滤出只包含一张车牌的数据 1630 | # 并且写bbox_corner坐标与landmark坐标 1631 | lines = f_h.readlines() 1632 | if len(lines) == 1: 1633 | label = jpg_name 1634 | vertices = [x.split(',') 1635 | for x in lines[0].strip().split(' ')] 1636 | 1637 | # 写bbox_corner: left, right, top, bottom(x1, x2, y1, y2) 1638 | coorners = vertex2bbox(vertices) 1639 | coordinates = ' ' + ' '.join(coorners) 1640 | label += coordinates 1641 | 1642 | # 写landmarks 1643 | landmarks = [c for pt in vertices for c in pt] 1644 | landmarks = ' ' + ' '.join(landmarks) 1645 | label += landmarks 1646 | 1647 | # 写换行符 1648 | label += '\n' 1649 | anno_h.write(label) 1650 | i += 1 1651 | 1652 | if i % 1000 == 0: 1653 | print('=> %d labels processed.' % i) 1654 | 1655 | 1656 | def verify_annos(anno_path, root): 1657 | """ 1658 | 验证label转换成annotations是正确的: 1659 | 0: image path 1660 | 1-4: left, right, top, bottom of bbox => left top right down 1661 | 5-14: (x1, y1), (x2, y2), (x3, y3), (x4, y4), (x5, y5) 1662 | """ 1663 | if not os.path.isfile(anno_path): 1664 | print('=> [Err]: invalid anno file.') 1665 | return 1666 | 1667 | with open(anno_path, 'r') as f_h: 1668 | for line in f_h.readlines(): 1669 | line = line.strip().split(' ') 1670 | 1671 | # 确保数据维度正确 1672 | assert len(line) == 13 1673 | 1674 | # 读取数据预处理 1675 | img_name = line[0] 1676 | corners = [int(x) for x in line[1:5]] 1677 | landmarks = [int(float(x)) for x in line[5:]] 1678 | 1679 | img_path = root + '/' + img_name 1680 | assert os.path.isfile(img_path) 1681 | img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) 1682 | 1683 | # 画矩形 1684 | cv2.rectangle(img, 1685 | (corners[0], corners[2]), # left-up corner 1686 | (corners[1], corners[3]), # right-down corner 1687 | (0, 0, 255), 1688 | thickness=2) 1689 | 1690 | # 画关键点 1691 | cv2.circle(img, (landmarks[0], landmarks[1]), 3, (0, 255, 255), -1) 1692 | cv2.circle(img, (landmarks[2], landmarks[3]), 3, (0, 255, 255), -1) 1693 | cv2.circle(img, (landmarks[4], landmarks[5]), 3, (0, 255, 255), -1) 1694 | cv2.circle(img, (landmarks[6], landmarks[7]), 3, (0, 255, 255), -1) 1695 | # cv2.circle(img, (landmarks[8], landmarks[9]), 3, (0, 255, 255), -1) 1696 | 1697 | cv2.imshow(img_path, img) 1698 | cv2.waitKey() 1699 | 1700 | 1701 | def merge2one_dir(root): 1702 | """ 1703 | """ 1704 | if not os.path.isdir(root): 1705 | print('=> [Err]: invalid root.') 1706 | return 1707 | 1708 | # 创建JPEGImages目录 1709 | jpg_dir = root + '/' + 'JPEGImages' 1710 | if not os.path.exists(jpg_dir): 1711 | os.makedirs(jpg_dir) 1712 | 1713 | # 合并到一个目录 1714 | for x in tqdm(os.listdir(root)): 1715 | x_path = root + '/' + x 1716 | if os.path.isdir(x_path): 1717 | for y in os.listdir(x_path): 1718 | if y.endswith('.jpg'): 1719 | y_path = x_path + '/' + y 1720 | if os.path.isfile(y_path): 1721 | dst_path = jpg_dir + '/' + y 1722 | if os.path.isfile(dst_path): 1723 | continue 1724 | shutil.copy(y_path, jpg_dir) 1725 | 1726 | # 删除其余目录 1727 | for x in os.listdir(root): 1728 | x_path = root + '/' + x 1729 | if os.path.isdir(x_path): 1730 | if x != 'JPEGImages': 1731 | shutil.rmtree(x_path) 1732 | 1733 | 1734 | def gen_data_set(): 1735 | temp_dirs = [] 1736 | src_roots = ['e:/plate_data'] 1737 | dst_root = 'e:/plate_data_pro' 1738 | 1739 | for rd in src_roots: 1740 | get_src_path(rd, temp_dirs) 1741 | print('=> src dirs: ', temp_dirs) 1742 | 1743 | # 处理每一个src根目录 1744 | for temp_dir in temp_dirs: 1745 | process_batch(temp_dir, dst_root) 1746 | 1747 | 1748 | if __name__ == '__main__': 1749 | # -------------------------处理一堆车牌目录 1750 | # gen_data_set() 1751 | 1752 | # -------------中间结果测试 1753 | # vertices = [('1686', '540'), ('1665', '625'), ('1855', '716'), ('1876', '624')] 1754 | # vertices = format_lp_vertices(vertices) 1755 | # print(vertices) 1756 | 1757 | # viz_vertices('e:/plate_data_pro') 1758 | 1759 | # rand_crop_around_center('e:/plate_data_pro') 1760 | 1761 | # viz_vertex(JPEG_dir='e:/plate_data_pro/CroppedImages', 1762 | # label_dir='e:/plate_data_pro/CroppedLabels', 1763 | # viz_res_dir='e:/plate_data_pro/viz_crop_result') 1764 | 1765 | # test_8_neighbor_CA() 1766 | # test_forward() 1767 | # test_random_choice() 1768 | 1769 | # label2anno('e:/plate_data_pro/labels', 'e:/plate_data_pro/annos') 1770 | # merge2one_dir('f:/car_1009') 1771 | # verify_annos(anno_path='g:/Car_DR/mtcnn_pytorch/annotations/landmark_imagelist.txt', 1772 | # root='f:/bbox_landmark_train') 1773 | 1774 | # label2bbox_landmark(label_dir='e:/plate_data_pro/labels', 1775 | # anno_dir='e:/plate_data_pro/annos') 1776 | 1777 | # verify_annos(anno_path='e:/plate_data_pro/annos/bbox_landmark.txt', 1778 | # root='e:/plate_data_pro/JPEGImages') 1779 | 1780 | # ------------------ generating lp patches 1781 | # gen_lp_patches(root='e:/plate_data_pro', 1782 | # offset_radius=15, 1783 | # min_scale=0.22, 1784 | # max_scale=3.0) 1785 | 1786 | # gen_lplm_patches(root='e:/plate_data_pro', 1787 | # offset_radius=15, 1788 | # min_scale=0.2, 1789 | # max_scale=4.0, 1790 | # is_viz=False) 1791 | 1792 | gen_lplm_pos_patches(src_root='e:/plate_data_pro', 1793 | dst_root='e:/plate_data_pro/patchesLM', 1794 | offset_radius=15, 1795 | is_viz=False) 1796 | 1797 | gen_pure_neg_patches(src_root='e:/plate_data_pro', 1798 | dst_root='e:/neg_patch', 1799 | num=5000) 1800 | 1801 | # for iter in range(5): 1802 | # gen_lplm_patches(root='e:/plate_data_pro', 1803 | # offset_radius=12, 1804 | # min_scale=0.25, 1805 | # max_scale=2.5, 1806 | # is_viz=False) 1807 | 1808 | # print('=> iter %d done' % (iter + 1)) 1809 | --------------------------------------------------------------------------------