├── Feature_Learning ├── README.md ├── feature_expansion │ ├── data │ │ ├── query_new.txt │ │ └── test_track_id.txt │ ├── feature_concat.py │ ├── feature_expansion.py │ ├── gallery_feature.py │ ├── query_expansion.py │ ├── query_expansion_stamp.py │ ├── stamp │ │ ├── frameGallery.npy │ │ ├── frameQuery.npy │ │ ├── g_index_s02.npy │ │ ├── g_index_s05.npy │ │ ├── gallery_camera_inf.npy │ │ ├── orisGallery.npy │ │ ├── orisQuery.npy │ │ ├── q_cams.npy │ │ ├── q_index_s02.npy │ │ └── q_index_s05.npy │ └── use_stamps.py └── learning_model │ ├── config │ ├── __init__.py │ └── defaults.py │ ├── configs │ ├── softmax_triplet_VR.yml │ ├── softmax_triplet_VR_test.yml │ └── softmax_triplet_VeRi.yml │ ├── data │ ├── __init__.py │ ├── build.py │ ├── collate_batch.py │ ├── datasets │ │ ├── VR.py │ │ ├── VR_test.py │ │ ├── VeRi.py │ │ ├── __init__.py │ │ ├── bases.py │ │ ├── cuhk03.py │ │ ├── dataset_loader.py │ │ ├── dukemtmcreid.py │ │ ├── eval_reid.py │ │ ├── market1501.py │ │ └── ranking.py │ ├── samplers │ │ ├── __init__.py │ │ └── triplet_sampler.py │ └── transforms │ │ ├── __init__.py │ │ ├── build.py │ │ └── transforms.py │ ├── engine │ ├── __init__.py │ ├── __init__.pyc │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── inference.cpython-37.pyc │ │ └── trainer.cpython-37.pyc │ ├── inference.py │ ├── inference.pyc │ ├── trainer.py │ └── trainer.pyc │ ├── layers │ ├── GHMC_Loss.py │ ├── LabelSmoothing.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── GHMC_Loss.cpython-37.pyc │ │ ├── LabelSmoothing.cpython-37.pyc │ │ ├── __init__.cpython-37.pyc │ │ └── triplet_loss.cpython-37.pyc │ └── triplet_loss.py │ ├── modeling │ ├── __init__.py │ ├── backbones │ │ ├── __init__.py │ │ └── torchvision_models.py │ └── baseline_de.py │ ├── solver │ ├── __init__.py │ ├── __init__.pyc │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── build.cpython-37.pyc │ │ └── lr_scheduler.cpython-37.pyc │ ├── build.py │ ├── build.pyc │ ├── lr_scheduler.py │ └── lr_scheduler.pyc │ ├── tests │ ├── __init__.py │ └── lr_scheduler_test.py │ ├── tools │ ├── __init__.py │ ├── test.py │ ├── train.py │ └── update.py │ └── utils │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── iotools.cpython-37.pyc │ ├── logger.cpython-37.pyc │ ├── model_loder.cpython-37.pyc │ ├── re_ranking.cpython-37.pyc │ └── reid_metric.cpython-37.pyc │ ├── iotools.py │ ├── logger.py │ ├── model_loder.py │ ├── re_ranking.py │ └── reid_metric.py ├── LICENSE └── README.md /Feature_Learning/README.md: -------------------------------------------------------------------------------- 1 | # Vehicle ReID_baseline 2 | Baseline model (with bottleneck) for vehicle ReID (using softmax and triplet loss). 3 | 4 | ## learning model 5 | This part is for learning feature extractor. The code is modified form [reid_baseline](https://github.com/L1aoXingyu/reid_baseline), you can check each folder's purpose by yourself. 6 | 7 | ------------ 8 | 9 | 10 | **TRAIN AND TEST ON VERI DATASET** 11 | #### Train 12 | `CUDA_VISIBLE_DEVICES=0 python3 tools/train.py --config_file='configs/softmax_triplet_VeRi.yml' MODEL.NAME 'densenet121' ` 13 | #### Test 14 | `CUDA_VISIBLE_DEVICES=1 python3 tools/test.py --config_file='configs/softmax_triplet_VeRi.yml' TEST.WEIGHT './CHECKPOINTS/VeRi/softmax_triplet/XX.pth' MODEL.NAME 'densenet121' ` 15 | 16 | ------------ 17 | 18 | 19 | **TRAIN AND TEST ON AICITY DATASET** 20 | #### Train 21 | The dataloader is in *./data/datasets/VR.py*.
22 | During the training, the test dataset is unavailable, we adopt the test dataset of Veri as the validation. This means that training a model on Aicity and testing it on Veri.
23 | `CUDA_VISIBLE_DEVICES=0 python3 tools/train.py --config_file='configs/softmax_triplet_VR.yml' MODEL.NAME 'densenet121' ` 24 | #### Test 25 | Extract the feature representations of test images of Aicity dataset.
26 | `CUDA_VISIBLE_DEVICES=0 python3 tools/update.py --config_file='configs/softmax_triplet_VR_test.yml' TEST.WEIGHT './CHECKPOINTS/VR/softmax_triplet/XX.pth' MODEL.NAME 'densenet121'` 27 | 28 | ------------ 29 | 30 | 31 | **MODEL_ENSEMBLE (AICITY)**
32 | To further improve the discriminative ability of features, we adopt model ensemble method. 33 | ##### 1. Model1 34 | Train model with default settings.
35 | ###### *train:*
36 | `CUDA_VISIBLE_DEVICES=0 python3 tools/train.py --config_file='configs/softmax_triplet_VR.yml' MODEL.NAME 'densenet121' OUTPUT_DIR "./CHECKPOINTS/VR/model1"`
37 | ###### *test:*
38 | `CUDA_VISIBLE_DEVICES=0 python3 tools/update.py --config_file='configs/softmax_triplet_VR_test.yml' TEST.WEIGHT './CHECKPOINTS/VR/model1/XX.pth' MODEL.NAME 'densenet121' TEST.QF_NAME 'qf_1' TEST.GF_NAME 'gf_1'`
39 | ##### 2. Model2 40 | Train model with extra data augmentation (COLORJITTER).
41 | ###### *train:*
42 | `CUDA_VISIBLE_DEVICES=0 python3 tools/train.py --config_file='configs/softmax_triplet_VR.yml' MODEL.NAME 'densenet121' OUTPUT_DIR "./CHECKPOINTS/VR/model2" INPUT.COLORJITTER 'True'`
43 | ###### *test:*
44 | `CUDA_VISIBLE_DEVICES=0 python3 tools/update.py --config_file='configs/softmax_triplet_VR_test.yml' TEST.WEIGHT './CHECKPOINTS/VR/model2/XX.pth' MODEL.NAME 'densenet121' INPUT.COLORJITTER 'True' TEST.QF_NAME 'qf_2' TEST.GF_NAME 'gf_2'`
45 | ##### 3. Model3 46 | Use softmargin for the triplet loss.
47 | ###### *train:*
48 | `CUDA_VISIBLE_DEVICES=0 python3 tools/train.py --config_file='configs/softmax_triplet_VR.yml' MODEL.NAME 'densenet121' OUTPUT_DIR "./CHECKPOINTS/VR/model3" DATALOADER.SOFT_MARGIN 'True'`
49 | ###### *test:*
50 | `CUDA_VISIBLE_DEVICES=0 python3 tools/update.py --config_file='configs/softmax_triplet_VR_test.yml' TEST.WEIGHT './CHECKPOINTS/VR/model3/XX.pth' MODEL.NAME 'densenet121' TEST.QF_NAME 'qf_3' TEST.GF_NAME 'gf_3'`
51 | ##### 4. Concatenate 52 | The features of three models are saved in './feature_expansion/data/'.
53 | `python feature_concat.py` 54 | ## Feature expansion 55 | This part consists of gallery expansion and query expansion. 56 | ##### gallery 57 | `python3 gallery_feature.py ` 58 | ##### Train 59 | `python3 query_expansion.py ` 60 | -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/data/query_new.txt: -------------------------------------------------------------------------------- 1 | 000346.jpg 2 | 000901.jpg 3 | 000003.jpg 4 | 000510.jpg 5 | 000458.jpg 6 | 001038.jpg 7 | 000561.jpg 8 | 000572.jpg 9 | 000009.jpg 10 | 000010.jpg 11 | 000011.jpg 12 | 000012.jpg 13 | 000180.jpg 14 | 000014.jpg 15 | 000015.jpg 16 | 000016.jpg 17 | 000554.jpg 18 | 001011.jpg 19 | 000376.jpg 20 | 000078.jpg 21 | 000058.jpg 22 | 000022.jpg 23 | 000902.jpg 24 | 000155.jpg 25 | 000025.jpg 26 | 000480.jpg 27 | 000027.jpg 28 | 000091.jpg 29 | 000029.jpg 30 | 000733.jpg 31 | 000031.jpg 32 | 000276.jpg 33 | 000090.jpg 34 | 000282.jpg 35 | 001005.jpg 36 | 000036.jpg 37 | 000687.jpg 38 | 000262.jpg 39 | 000094.jpg 40 | 000984.jpg 41 | 000041.jpg 42 | 000949.jpg 43 | 000988.jpg 44 | 000958.jpg 45 | 000045.jpg 46 | 000197.jpg 47 | 000047.jpg 48 | 000048.jpg 49 | 000426.jpg 50 | 000402.jpg 51 | 000036.jpg 52 | 000052.jpg 53 | 000864.jpg 54 | 000260.jpg 55 | 000055.jpg 56 | 000056.jpg 57 | 000554.jpg 58 | 000058.jpg 59 | 000999.jpg 60 | 000060.jpg 61 | 000413.jpg 62 | 000564.jpg 63 | 000720.jpg 64 | 000015.jpg 65 | 000065.jpg 66 | 000880.jpg 67 | 000097.jpg 68 | 000525.jpg 69 | 000542.jpg 70 | 000350.jpg 71 | 000276.jpg 72 | 000072.jpg 73 | 000073.jpg 74 | 000074.jpg 75 | 000265.jpg 76 | 000902.jpg 77 | 000572.jpg 78 | 000078.jpg 79 | 001041.jpg 80 | 000080.jpg 81 | 000083.jpg 82 | 000085.jpg 83 | 000083.jpg 84 | 000660.jpg 85 | 000085.jpg 86 | 001009.jpg 87 | 000588.jpg 88 | 000088.jpg 89 | 000089.jpg 90 | 000090.jpg 91 | 000091.jpg 92 | 000505.jpg 93 | 000093.jpg 94 | 000094.jpg 95 | 000282.jpg 96 | 001016.jpg 97 | 000097.jpg 98 | 000243.jpg 99 | 000592.jpg 100 | 000100.jpg 101 | 000101.jpg 102 | 000102.jpg 103 | 000985.jpg 104 | 000720.jpg 105 | 000936.jpg 106 | 000106.jpg 107 | 000090.jpg 108 | 000672.jpg 109 | 000109.jpg 110 | 000340.jpg 111 | 000340.jpg 112 | 000112.jpg 113 | 000113.jpg 114 | 000114.jpg 115 | 001043.jpg 116 | 000116.jpg 117 | 000117.jpg 118 | 000981.jpg 119 | 000119.jpg 120 | 000698.jpg 121 | 000699.jpg 122 | 000909.jpg 123 | 000123.jpg 124 | 000885.jpg 125 | 000125.jpg 126 | 000126.jpg 127 | 000367.jpg 128 | 000217.jpg 129 | 000647.jpg 130 | 000768.jpg 131 | 000131.jpg 132 | 000828.jpg 133 | 000739.jpg 134 | 000134.jpg 135 | 000416.jpg 136 | 000510.jpg 137 | 000244.jpg 138 | 000047.jpg 139 | 000885.jpg 140 | 000898.jpg 141 | 000517.jpg 142 | 000651.jpg 143 | 000047.jpg 144 | 000844.jpg 145 | 000145.jpg 146 | 000828.jpg 147 | 000816.jpg 148 | 000362.jpg 149 | 000762.jpg 150 | 000886.jpg 151 | 000699.jpg 152 | 000012.jpg 153 | 000902.jpg 154 | 000277.jpg 155 | 000155.jpg 156 | 000156.jpg 157 | 000854.jpg 158 | 000097.jpg 159 | 000159.jpg 160 | 000943.jpg 161 | 000161.jpg 162 | 000680.jpg 163 | 000829.jpg 164 | 000510.jpg 165 | 000165.jpg 166 | 000848.jpg 167 | 000125.jpg 168 | 000773.jpg 169 | 000169.jpg 170 | 000116.jpg 171 | 000434.jpg 172 | 000542.jpg 173 | 000803.jpg 174 | 000517.jpg 175 | 000415.jpg 176 | 000176.jpg 177 | 000936.jpg 178 | 000091.jpg 179 | 000179.jpg 180 | 000180.jpg 181 | 000089.jpg 182 | 000591.jpg 183 | 000011.jpg 184 | 000220.jpg 185 | 000554.jpg 186 | 000186.jpg 187 | 000271.jpg 188 | 000810.jpg 189 | 000776.jpg 190 | 000190.jpg 191 | 000626.jpg 192 | 000058.jpg 193 | 000048.jpg 194 | 000556.jpg 195 | 000058.jpg 196 | 000196.jpg 197 | 000197.jpg 198 | 000330.jpg 199 | 000480.jpg 200 | 000902.jpg 201 | 000201.jpg 202 | 000202.jpg 203 | 000485.jpg 204 | 000413.jpg 205 | 000324.jpg 206 | 001033.jpg 207 | 000389.jpg 208 | 000208.jpg 209 | 000209.jpg 210 | 000839.jpg 211 | 000211.jpg 212 | 000212.jpg 213 | 000197.jpg 214 | 000344.jpg 215 | 000331.jpg 216 | 000896.jpg 217 | 000217.jpg 218 | 000413.jpg 219 | 000641.jpg 220 | 000220.jpg 221 | 001043.jpg 222 | 000222.jpg 223 | 000223.jpg 224 | 000224.jpg 225 | 000225.jpg 226 | 000029.jpg 227 | 000367.jpg 228 | 000224.jpg 229 | 000339.jpg 230 | 001016.jpg 231 | 000244.jpg 232 | 000232.jpg 233 | 000233.jpg 234 | 000500.jpg 235 | 000641.jpg 236 | 000558.jpg 237 | 000586.jpg 238 | 000774.jpg 239 | 000239.jpg 240 | 000055.jpg 241 | 000241.jpg 242 | 000242.jpg 243 | 000243.jpg 244 | 000244.jpg 245 | 000345.jpg 246 | 000657.jpg 247 | 000926.jpg 248 | 000248.jpg 249 | 000012.jpg 250 | 000250.jpg 251 | 000097.jpg 252 | 000252.jpg 253 | 000885.jpg 254 | 000254.jpg 255 | 001033.jpg 256 | 000243.jpg 257 | 000065.jpg 258 | 000258.jpg 259 | 000259.jpg 260 | 000260.jpg 261 | 000776.jpg 262 | 000262.jpg 263 | 000263.jpg 264 | 000376.jpg 265 | 000265.jpg 266 | 000960.jpg 267 | 000267.jpg 268 | 000588.jpg 269 | 000217.jpg 270 | 000270.jpg 271 | 000271.jpg 272 | 000746.jpg 273 | 000958.jpg 274 | 000298.jpg 275 | 000326.jpg 276 | 000276.jpg 277 | 000277.jpg 278 | 000971.jpg 279 | 000810.jpg 280 | 001011.jpg 281 | 000703.jpg 282 | 000282.jpg 283 | 000739.jpg 284 | 000284.jpg 285 | 000285.jpg 286 | 000243.jpg 287 | 000277.jpg 288 | 000971.jpg 289 | 000259.jpg 290 | 000958.jpg 291 | 000119.jpg 292 | 000510.jpg 293 | 000293.jpg 294 | 000186.jpg 295 | 000958.jpg 296 | 000607.jpg 297 | 000326.jpg 298 | 000298.jpg 299 | 000299.jpg 300 | 000967.jpg 301 | 000359.jpg 302 | 000344.jpg 303 | 000379.jpg 304 | 001004.jpg 305 | 000305.jpg 306 | 000416.jpg 307 | 000871.jpg 308 | 000348.jpg 309 | 000243.jpg 310 | 000591.jpg 311 | 000981.jpg 312 | 000208.jpg 313 | 000846.jpg 314 | 000314.jpg 315 | 000536.jpg 316 | 000065.jpg 317 | 000642.jpg 318 | 000217.jpg 319 | 000319.jpg 320 | 000010.jpg 321 | 000436.jpg 322 | 000322.jpg 323 | 000323.jpg 324 | 000324.jpg 325 | 000437.jpg 326 | 000326.jpg 327 | 000058.jpg 328 | 000352.jpg 329 | 000329.jpg 330 | 000330.jpg 331 | 000331.jpg 332 | 000782.jpg 333 | 000729.jpg 334 | 000334.jpg 335 | 000222.jpg 336 | 000811.jpg 337 | 000500.jpg 338 | 000876.jpg 339 | 000339.jpg 340 | 000340.jpg 341 | 000341.jpg 342 | 000342.jpg 343 | 000343.jpg 344 | 000344.jpg 345 | 000345.jpg 346 | 000346.jpg 347 | 000808.jpg 348 | 000348.jpg 349 | 000532.jpg 350 | 000350.jpg 351 | 000047.jpg 352 | 000352.jpg 353 | 000197.jpg 354 | 000112.jpg 355 | 000355.jpg 356 | 000379.jpg 357 | 000209.jpg 358 | 000967.jpg 359 | 000359.jpg 360 | 000348.jpg 361 | 001028.jpg 362 | 000362.jpg 363 | 000225.jpg 364 | 000751.jpg 365 | 000165.jpg 366 | 000949.jpg 367 | 000367.jpg 368 | 000208.jpg 369 | 000369.jpg 370 | 000117.jpg 371 | 000949.jpg 372 | 000961.jpg 373 | 000282.jpg 374 | 000660.jpg 375 | 000561.jpg 376 | 000376.jpg 377 | 000968.jpg 378 | 000794.jpg 379 | 000379.jpg 380 | 000380.jpg 381 | 000585.jpg 382 | 000003.jpg 383 | 000462.jpg 384 | 001028.jpg 385 | 000896.jpg 386 | 001045.jpg 387 | 000387.jpg 388 | 000180.jpg 389 | 000389.jpg 390 | 000981.jpg 391 | 000088.jpg 392 | 000369.jpg 393 | 000102.jpg 394 | 000346.jpg 395 | 000003.jpg 396 | 000549.jpg 397 | 000416.jpg 398 | 000917.jpg 399 | 000552.jpg 400 | 000271.jpg 401 | 000871.jpg 402 | 000402.jpg 403 | 000549.jpg 404 | 000532.jpg 405 | 000505.jpg 406 | 000602.jpg 407 | 000935.jpg 408 | 000880.jpg 409 | 000586.jpg 410 | 000984.jpg 411 | 000415.jpg 412 | 000782.jpg 413 | 000413.jpg 414 | 000462.jpg 415 | 000415.jpg 416 | 000416.jpg 417 | 000088.jpg 418 | 000613.jpg 419 | 000960.jpg 420 | 000453.jpg 421 | 000421.jpg 422 | 000422.jpg 423 | 000525.jpg 424 | 000585.jpg 425 | 000811.jpg 426 | 000426.jpg 427 | 000859.jpg 428 | 000239.jpg 429 | 000022.jpg 430 | 000552.jpg 431 | 000441.jpg 432 | 000723.jpg 433 | 000916.jpg 434 | 000434.jpg 435 | 000102.jpg 436 | 000436.jpg 437 | 000437.jpg 438 | 000962.jpg 439 | 000439.jpg 440 | 000362.jpg 441 | 000441.jpg 442 | 000155.jpg 443 | 000768.jpg 444 | 000444.jpg 445 | 000201.jpg 446 | 000926.jpg 447 | 000052.jpg 448 | 000448.jpg 449 | 000052.jpg 450 | 000641.jpg 451 | 000864.jpg 452 | 000859.jpg 453 | 000453.jpg 454 | 000454.jpg 455 | 000009.jpg 456 | 000241.jpg 457 | 001033.jpg 458 | 000458.jpg 459 | 000459.jpg 460 | 000460.jpg 461 | 000421.jpg 462 | 000462.jpg 463 | 000222.jpg 464 | 000464.jpg 465 | 000467.jpg 466 | 000657.jpg 467 | 000467.jpg 468 | 000350.jpg 469 | 000537.jpg 470 | 000470.jpg 471 | 000434.jpg 472 | 000898.jpg 473 | 000090.jpg 474 | 000047.jpg 475 | 000848.jpg 476 | 000803.jpg 477 | 000871.jpg 478 | 000703.jpg 479 | 000259.jpg 480 | 000480.jpg 481 | 000848.jpg 482 | 000871.jpg 483 | 000585.jpg 484 | 000484.jpg 485 | 000485.jpg 486 | 000517.jpg 487 | 000165.jpg 488 | 000926.jpg 489 | 000022.jpg 490 | 000282.jpg 491 | 000733.jpg 492 | 000900.jpg 493 | 000470.jpg 494 | 000113.jpg 495 | 000755.jpg 496 | 000854.jpg 497 | 000578.jpg 498 | 000498.jpg 499 | 000012.jpg 500 | 000500.jpg 501 | 000501.jpg 502 | 000502.jpg 503 | 000558.jpg 504 | 000169.jpg 505 | 000505.jpg 506 | 001001.jpg 507 | 000379.jpg 508 | 000698.jpg 509 | 001011.jpg 510 | 000510.jpg 511 | 000511.jpg 512 | 000512.jpg 513 | 000998.jpg 514 | 000514.jpg 515 | 000794.jpg 516 | 000926.jpg 517 | 000517.jpg 518 | 000909.jpg 519 | 000519.jpg 520 | 000052.jpg 521 | 000926.jpg 522 | 000522.jpg 523 | 000523.jpg 524 | 000252.jpg 525 | 000525.jpg 526 | 000529.jpg 527 | 000871.jpg 528 | 000233.jpg 529 | 000529.jpg 530 | 000074.jpg 531 | 000531.jpg 532 | 000532.jpg 533 | 000367.jpg 534 | 000239.jpg 535 | 000131.jpg 536 | 000536.jpg 537 | 000537.jpg 538 | 000467.jpg 539 | 000539.jpg 540 | 000458.jpg 541 | 000541.jpg 542 | 000542.jpg 543 | 000448.jpg 544 | 000971.jpg 545 | 000886.jpg 546 | 000389.jpg 547 | 000547.jpg 548 | 000548.jpg 549 | 000549.jpg 550 | 000285.jpg 551 | 000276.jpg 552 | 000552.jpg 553 | 000529.jpg 554 | 000554.jpg 555 | 000555.jpg 556 | 000556.jpg 557 | 000355.jpg 558 | 000558.jpg 559 | 000559.jpg 560 | 000241.jpg 561 | 000561.jpg 562 | 000041.jpg 563 | 000259.jpg 564 | 000564.jpg 565 | 000536.jpg 566 | 000047.jpg 567 | 000886.jpg 568 | 000568.jpg 569 | 001001.jpg 570 | 000525.jpg 571 | 000733.jpg 572 | 000572.jpg 573 | 000389.jpg 574 | 000638.jpg 575 | 000225.jpg 576 | 000576.jpg 577 | 000577.jpg 578 | 000578.jpg 579 | 000547.jpg 580 | 000517.jpg 581 | 000626.jpg 582 | 000582.jpg 583 | 000241.jpg 584 | 000917.jpg 585 | 000585.jpg 586 | 000586.jpg 587 | 000517.jpg 588 | 000588.jpg 589 | 000470.jpg 590 | 000723.jpg 591 | 000591.jpg 592 | 000592.jpg 593 | 000593.jpg 594 | 000211.jpg 595 | 000056.jpg 596 | 001011.jpg 597 | 000597.jpg 598 | 000846.jpg 599 | 000343.jpg 600 | 000600.jpg 601 | 000074.jpg 602 | 000602.jpg 603 | 000376.jpg 604 | 000585.jpg 605 | 000949.jpg 606 | 000606.jpg 607 | 000607.jpg 608 | 000549.jpg 609 | 000743.jpg 610 | 000523.jpg 611 | 000611.jpg 612 | 000498.jpg 613 | 000613.jpg 614 | 000638.jpg 615 | 000576.jpg 616 | 000529.jpg 617 | 001004.jpg 618 | 000334.jpg 619 | 000029.jpg 620 | 000093.jpg 621 | 000556.jpg 622 | 000536.jpg 623 | 000576.jpg 624 | 000554.jpg 625 | 000625.jpg 626 | 000626.jpg 627 | 000999.jpg 628 | 000341.jpg 629 | 000459.jpg 630 | 000586.jpg 631 | 000003.jpg 632 | 000211.jpg 633 | 000220.jpg 634 | 000742.jpg 635 | 000876.jpg 636 | 000636.jpg 637 | 000665.jpg 638 | 000638.jpg 639 | 000657.jpg 640 | 000828.jpg 641 | 000641.jpg 642 | 000642.jpg 643 | 000065.jpg 644 | 000644.jpg 645 | 000645.jpg 646 | 000031.jpg 647 | 000647.jpg 648 | 000648.jpg 649 | 000265.jpg 650 | 000650.jpg 651 | 000651.jpg 652 | 000112.jpg 653 | 001045.jpg 654 | 000712.jpg 655 | 000898.jpg 656 | 000823.jpg 657 | 000657.jpg 658 | 000532.jpg 659 | 001034.jpg 660 | 000660.jpg 661 | 001007.jpg 662 | 000755.jpg 663 | 000742.jpg 664 | 001016.jpg 665 | 000665.jpg 666 | 000464.jpg 667 | 000554.jpg 668 | 000803.jpg 669 | 000510.jpg 670 | 000484.jpg 671 | 000593.jpg 672 | 000672.jpg 673 | 000673.jpg 674 | 000864.jpg 675 | 000379.jpg 676 | 000165.jpg 677 | 000339.jpg 678 | 000339.jpg 679 | 000436.jpg 680 | 000680.jpg 681 | 000324.jpg 682 | 000045.jpg 683 | 000426.jpg 684 | 000684.jpg 685 | 000755.jpg 686 | 000755.jpg 687 | 000687.jpg 688 | 000260.jpg 689 | 000559.jpg 690 | 000690.jpg 691 | 000691.jpg 692 | 000376.jpg 693 | 000949.jpg 694 | 000467.jpg 695 | 000340.jpg 696 | 000696.jpg 697 | 000613.jpg 698 | 000698.jpg 699 | 000699.jpg 700 | 000552.jpg 701 | 000058.jpg 702 | 000348.jpg 703 | 000703.jpg 704 | 000782.jpg 705 | 000578.jpg 706 | 000498.jpg 707 | 000117.jpg 708 | 000556.jpg 709 | 000156.jpg 710 | 000085.jpg 711 | 000254.jpg 712 | 000712.jpg 713 | 000898.jpg 714 | 000212.jpg 715 | 000379.jpg 716 | 000211.jpg 717 | 000606.jpg 718 | 000339.jpg 719 | 000224.jpg 720 | 000720.jpg 721 | 000647.jpg 722 | 000722.jpg 723 | 000723.jpg 724 | 000762.jpg 725 | 000591.jpg 726 | 000699.jpg 727 | 000454.jpg 728 | 000600.jpg 729 | 000729.jpg 730 | 000730.jpg 731 | 000731.jpg 732 | 000732.jpg 733 | 000733.jpg 734 | 000734.jpg 735 | 000735.jpg 736 | 000699.jpg 737 | 000212.jpg 738 | 000738.jpg 739 | 000739.jpg 740 | 001007.jpg 741 | 000060.jpg 742 | 000742.jpg 743 | 000743.jpg 744 | 000270.jpg 745 | 000745.jpg 746 | 000746.jpg 747 | 000917.jpg 748 | 000572.jpg 749 | 000948.jpg 750 | 000348.jpg 751 | 000751.jpg 752 | 000642.jpg 753 | 000859.jpg 754 | 000588.jpg 755 | 000755.jpg 756 | 000864.jpg 757 | 000757.jpg 758 | 000517.jpg 759 | 000334.jpg 760 | 000548.jpg 761 | 000723.jpg 762 | 000762.jpg 763 | 000536.jpg 764 | 000810.jpg 765 | 000585.jpg 766 | 000254.jpg 767 | 000767.jpg 768 | 000768.jpg 769 | 000369.jpg 770 | 000690.jpg 771 | 000885.jpg 772 | 000186.jpg 773 | 000773.jpg 774 | 000774.jpg 775 | 000252.jpg 776 | 000776.jpg 777 | 000458.jpg 778 | 000828.jpg 779 | 000058.jpg 780 | 000780.jpg 781 | 000698.jpg 782 | 000782.jpg 783 | 000380.jpg 784 | 000687.jpg 785 | 000607.jpg 786 | 000786.jpg 787 | 000787.jpg 788 | 000211.jpg 789 | 000500.jpg 790 | 001029.jpg 791 | 000072.jpg 792 | 000341.jpg 793 | 000774.jpg 794 | 000794.jpg 795 | 000029.jpg 796 | 000962.jpg 797 | 000985.jpg 798 | 000116.jpg 799 | 000480.jpg 800 | 000800.jpg 801 | 000703.jpg 802 | 000720.jpg 803 | 000803.jpg 804 | 000453.jpg 805 | 000525.jpg 806 | 000691.jpg 807 | 000607.jpg 808 | 000808.jpg 809 | 000787.jpg 810 | 000810.jpg 811 | 000811.jpg 812 | 000036.jpg 813 | 000131.jpg 814 | 000277.jpg 815 | 000343.jpg 816 | 000816.jpg 817 | 000501.jpg 818 | 000485.jpg 819 | 000819.jpg 820 | 000402.jpg 821 | 001037.jpg 822 | 000822.jpg 823 | 000823.jpg 824 | 000437.jpg 825 | 000572.jpg 826 | 001045.jpg 827 | 000827.jpg 828 | 000828.jpg 829 | 000829.jpg 830 | 000660.jpg 831 | 000690.jpg 832 | 000909.jpg 833 | 000554.jpg 834 | 000834.jpg 835 | 000444.jpg 836 | 000537.jpg 837 | 000822.jpg 838 | 000787.jpg 839 | 000839.jpg 840 | 000917.jpg 841 | 000559.jpg 842 | 000282.jpg 843 | 000843.jpg 844 | 000844.jpg 845 | 000626.jpg 846 | 000846.jpg 847 | 000186.jpg 848 | 000848.jpg 849 | 000027.jpg 850 | 000542.jpg 851 | 000484.jpg 852 | 000564.jpg 853 | 000853.jpg 854 | 000854.jpg 855 | 000277.jpg 856 | 000856.jpg 857 | 000233.jpg 858 | 000606.jpg 859 | 000859.jpg 860 | 000902.jpg 861 | 000755.jpg 862 | 000389.jpg 863 | 000117.jpg 864 | 000864.jpg 865 | 000865.jpg 866 | 000935.jpg 867 | 001034.jpg 868 | 000943.jpg 869 | 000444.jpg 870 | 000293.jpg 871 | 000871.jpg 872 | 000114.jpg 873 | 000505.jpg 874 | 000564.jpg 875 | 000529.jpg 876 | 000876.jpg 877 | 000517.jpg 878 | 000638.jpg 879 | 000359.jpg 880 | 000880.jpg 881 | 000270.jpg 882 | 000085.jpg 883 | 000948.jpg 884 | 000884.jpg 885 | 000885.jpg 886 | 000886.jpg 887 | 000887.jpg 888 | 000094.jpg 889 | 000816.jpg 890 | 000022.jpg 891 | 000787.jpg 892 | 000967.jpg 893 | 000787.jpg 894 | 000277.jpg 895 | 000244.jpg 896 | 000896.jpg 897 | 000897.jpg 898 | 000898.jpg 899 | 000690.jpg 900 | 000900.jpg 901 | 000901.jpg 902 | 000902.jpg 903 | 000031.jpg 904 | 000743.jpg 905 | 000905.jpg 906 | 000703.jpg 907 | 000588.jpg 908 | 001043.jpg 909 | 000909.jpg 910 | 000549.jpg 911 | 001001.jpg 912 | 000271.jpg 913 | 000225.jpg 914 | 000822.jpg 915 | 001004.jpg 916 | 000916.jpg 917 | 000917.jpg 918 | 000334.jpg 919 | 000088.jpg 920 | 000112.jpg 921 | 001033.jpg 922 | 000239.jpg 923 | 000532.jpg 924 | 000924.jpg 925 | 000925.jpg 926 | 000926.jpg 927 | 000179.jpg 928 | 000556.jpg 929 | 000436.jpg 930 | 000738.jpg 931 | 000413.jpg 932 | 000981.jpg 933 | 000803.jpg 934 | 000762.jpg 935 | 000935.jpg 936 | 000936.jpg 937 | 000085.jpg 938 | 000985.jpg 939 | 000755.jpg 940 | 000119.jpg 941 | 000083.jpg 942 | 000680.jpg 943 | 000943.jpg 944 | 000015.jpg 945 | 000434.jpg 946 | 000346.jpg 947 | 000968.jpg 948 | 000948.jpg 949 | 000949.jpg 950 | 000651.jpg 951 | 000774.jpg 952 | 000293.jpg 953 | 000734.jpg 954 | 000348.jpg 955 | 000197.jpg 956 | 000956.jpg 957 | 000962.jpg 958 | 000958.jpg 959 | 000959.jpg 960 | 000960.jpg 961 | 000961.jpg 962 | 000962.jpg 963 | 001011.jpg 964 | 000593.jpg 965 | 000965.jpg 966 | 000774.jpg 967 | 000967.jpg 968 | 000968.jpg 969 | 000948.jpg 970 | 000065.jpg 971 | 000971.jpg 972 | 000607.jpg 973 | 000948.jpg 974 | 000454.jpg 975 | 000041.jpg 976 | 000453.jpg 977 | 000819.jpg 978 | 000626.jpg 979 | 000523.jpg 980 | 000083.jpg 981 | 000981.jpg 982 | 000010.jpg 983 | 000943.jpg 984 | 000984.jpg 985 | 000985.jpg 986 | 000986.jpg 987 | 000415.jpg 988 | 000988.jpg 989 | 001041.jpg 990 | 000990.jpg 991 | 000262.jpg 992 | 000780.jpg 993 | 000993.jpg 994 | 000426.jpg 995 | 000523.jpg 996 | 000998.jpg 997 | 000997.jpg 998 | 000998.jpg 999 | 000999.jpg 1000 | 000243.jpg 1001 | 001001.jpg 1002 | 000720.jpg 1003 | 000459.jpg 1004 | 001004.jpg 1005 | 001005.jpg 1006 | 000480.jpg 1007 | 001007.jpg 1008 | 000329.jpg 1009 | 001009.jpg 1010 | 001010.jpg 1011 | 001011.jpg 1012 | 000556.jpg 1013 | 000402.jpg 1014 | 000448.jpg 1015 | 000985.jpg 1016 | 001016.jpg 1017 | 001017.jpg 1018 | 000762.jpg 1019 | 000665.jpg 1020 | 000340.jpg 1021 | 000638.jpg 1022 | 000880.jpg 1023 | 000012.jpg 1024 | 000426.jpg 1025 | 000380.jpg 1026 | 000720.jpg 1027 | 000558.jpg 1028 | 001028.jpg 1029 | 001029.jpg 1030 | 000262.jpg 1031 | 000532.jpg 1032 | 000871.jpg 1033 | 001033.jpg 1034 | 001034.jpg 1035 | 001035.jpg 1036 | 001036.jpg 1037 | 001037.jpg 1038 | 001038.jpg 1039 | 001039.jpg 1040 | 000738.jpg 1041 | 001041.jpg 1042 | 000458.jpg 1043 | 001043.jpg 1044 | 000738.jpg 1045 | 001045.jpg 1046 | 000819.jpg 1047 | 000319.jpg 1048 | 000917.jpg 1049 | 000117.jpg 1050 | 000738.jpg 1051 | 001051.jpg 1052 | 000201.jpg -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/feature_concat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 27 15:03:13 2019 4 | 5 | @author: Deng 6 | """ 7 | 8 | import numpy as np 9 | 10 | #--------------------------- load feature -----------------# 11 | # load feature 12 | gf_1 = np.load('./data/gf_1.npy') 13 | gf_1_n = np.linalg.norm(gf_1, axis=1, keepdims=True) 14 | gf_1 = gf_1 / gf_1_n 15 | 16 | qf_1 = np.load('./data/qf_1.npy') 17 | qf_1_n = np.linalg.norm(qf_1, axis=1, keepdims=True) 18 | qf_1 = qf_1 / qf_1_n 19 | 20 | # model 2 21 | gf_2 = np.load('./data/gf_2.npy') 22 | gf_2_n = np.linalg.norm(gf_2, axis=1, keepdims=True) 23 | gf_2 = gf_2 / gf_2_n 24 | 25 | qf_2 = np.load('./data/qf_2.npy') 26 | qf_2_n = np.linalg.norm(qf_2, axis=1, keepdims=True) 27 | qf_2 = qf_2 / qf_2_n 28 | 29 | # model 3 30 | gf_3 = np.load('./data/gf_3.npy') 31 | gf_3_n = np.linalg.norm(gf_3, axis=1, keepdims=True) 32 | gf_3 = gf_3 / gf_3_n 33 | 34 | qf_3 = np.load('./data/qf_3.npy') 35 | qf_3_n = np.linalg.norm(qf_3, axis=1, keepdims=True) 36 | qf_3 = qf_3 / qf_3_n 37 | 38 | #--------------------------- feature concat -----------------# 39 | qf_ori = np.concatenate((qf_1, qf_2, qf_3), axis=1) /np.sqrt(3) 40 | gf_ori = np.concatenate((gf_1, gf_2, gf_3), axis=1) /np.sqrt(3) 41 | 42 | #--------------------------- save feature -----------------# 43 | np.save('./data/qf_ori.npy', qf_ori) 44 | np.save('./data/gf_ori.npy', gf_ori) 45 | -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/feature_expansion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 27 15:03:13 2019 4 | 5 | @author: PAMI Deng 6 | """ 7 | 8 | import numpy as np 9 | import copy 10 | 11 | 12 | # frame inforamtion 13 | frameQuery = np.load('./data/frameQuery.npy') 14 | frameTracklets = np.load('./data/frameGallery.npy') 15 | frameGallery = np.zeros([18290]) 16 | 17 | # tracklet name with ID 18 | g_name = [] 19 | f = open('./test_track_id.txt', 'r') 20 | for k, line in enumerate(f): 21 | temp = list(map(int, line.split(' ')[:-1])) 22 | g_name.append(list(map(lambda x: x-1, temp))) 23 | for indexTemp, idTemp in enumerate(temp): 24 | frameGallery[idTemp-1] = frameTracklets[k] 25 | f.close 26 | 27 | # gallery labels and query labels 28 | q_label = np.ones([1052])*1000 29 | g_label = np.ones([18290])*1000 30 | 31 | # camera inforamation 32 | q_cams = np.load('./data/q_cams.npy') 33 | g_cams = np.load('./data/gallery_camera_inf.npy') 34 | 35 | # open results of S02 36 | f = open('./data/s02_query_gallery.txt', 'r') 37 | for k, line in enumerate(f): 38 | temp = line.split(' : ') 39 | temp0 = list(map(int, temp[0].split(' '))) 40 | q_label[list(map(lambda x: x-1, temp0))] = int(k) 41 | 42 | temp1 = list(map(int, temp[1].split(' '))) 43 | temp2 = list(map(lambda x: x-1, temp1)) 44 | for i in range(len(temp2)): 45 | g_label[np.array(g_name)[temp2[i]]] = int(k) 46 | f.close() 47 | 48 | q_index = np.nonzero(q_label!=1000)[0] 49 | g_index = np.nonzero(g_label!=1000)[0] 50 | 51 | #-------------------------------------- query expansion version 1 -------------------------# 52 | # load feature 53 | gf = np.load('./data/gf_multi.npy') 54 | qf = np.load('./data/qf_multi.npy') 55 | #dist = np.load('./data/distmat.npy') 56 | 57 | dist = np.dot(qf, np.transpose(gf)) 58 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 59 | d_max = 10.0 60 | 61 | # time filter 62 | frameGap = 190 63 | for i in range(len(q_index)): 64 | key = q_index[i] 65 | indice = abs(frameGallery - frameQuery[key])> frameGap 66 | if q_cams[key]==6: 67 | indice = indice | (abs(frameGallery - frameQuery[key])> 60) & (g_cams == 9) 68 | elif q_cams[key] == 9: 69 | indice = indice | (abs(frameGallery - frameQuery[key])> 50) & (g_cams == 6) 70 | indice = np.nonzero(indice)[0] 71 | dist[key][indice] = d_max 72 | 73 | # camera filter for S02 and S05 74 | for i in range(len(dist)): 75 | indice = np.nonzero((g_cams == q_cams[i]))[0] 76 | dist[i][indice] = d_max 77 | 78 | # scene filter for S02 79 | g_indice_scene = np.nonzero(g_label==1000)[0] #S05 indice 80 | for i in range(len(q_index)): 81 | key = q_index[i] 82 | dist[key][g_indice_scene] = d_max 83 | 84 | # scene filter for S05 85 | g_indice_scene = np.nonzero(g_label!=1000)[0] #S02 indice 86 | s05_indice = np.nonzero(q_label==1000)[0] 87 | for i in range(len(s05_indice)): 88 | key = s05_indice[i] 89 | dist[key][g_indice_scene] = d_max 90 | 91 | #---------------------------- query expansion ----------------------------# 92 | qf_new = [] 93 | T = 9 94 | num = 1 95 | d_max = 10.0 96 | 97 | for t in range(num): 98 | qf_new = [] 99 | for i in range(len(dist)): 100 | indice = np.argsort(dist[i])[:T] 101 | temp = np.concatenate((qf[i][np.newaxis, :], gf[indice]), axis=0) 102 | #temp = gf[indice] 103 | qf_new.append(np.mean(temp, axis=0, keepdims=True)) 104 | qf = np.squeeze(np.array(qf_new)) 105 | # feature norm 106 | q_n = np.linalg.norm(qf, axis=1, keepdims=True) 107 | qf = qf / q_n 108 | 109 | dist = np.dot(qf, np.transpose(gf)) 110 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 111 | #np.save('./data/gf.npy', gf) 112 | np.save('./data/qf_new_%d.npy' % t, qf) 113 | 114 | # time filter 115 | frameGap = 190 116 | for i in range(len(q_index)): 117 | key = q_index[i] 118 | indice = abs(frameGallery - frameQuery[key])> frameGap 119 | if q_cams[key]==6: 120 | indice = indice | (abs(frameGallery - frameQuery[key])> 60) & (g_cams == 9) 121 | elif q_cams[key] == 9: 122 | indice = indice | (abs(frameGallery - frameQuery[key])> 50) & (g_cams == 6) 123 | indice = np.nonzero(indice)[0] 124 | dist[key][indice] = d_max 125 | 126 | # camera filter for S02 and S05 127 | for i in range(len(dist)): 128 | indice = np.nonzero((g_cams == q_cams[i]))[0] 129 | dist[i][indice] = d_max 130 | 131 | # scene filter for S02 132 | g_indice_scene = np.nonzero(g_label==1000)[0] #S05 indice 133 | for i in range(len(q_index)): 134 | key = q_index[i] 135 | dist[key][g_indice_scene] = d_max 136 | 137 | # scene filter for S05 138 | g_indice_scene = np.nonzero(g_label!=1000)[0] #S02 indice 139 | s05_indice = np.nonzero(q_label==1000)[0] 140 | for i in range(len(s05_indice)): 141 | key = s05_indice[i] 142 | dist[key][g_indice_scene] = d_max 143 | 144 | ''' 145 | #-------------------------------------- query expansion version 2 -------------------------# 146 | # load feature 147 | gf = np.load('./data/gf_multi.npy') 148 | qf = np.load('./data/qf_multi.npy') 149 | 150 | f = open('./results/track2_time_all_order.txt', 'r') 151 | dist = [] 152 | 153 | for line in f: 154 | temp0 = list(map(int, line.strip(' \n').split(' '))) 155 | temp = list(map(lambda x: x-1, temp0)) 156 | dist.append(temp) 157 | 158 | dist = np.array(dist) 159 | dist = np.load('./data/distmat.npy') 160 | qf_new = [] 161 | T = 10 162 | weights = 1 163 | 164 | for i in range(len(dist)): 165 | indice = np.argsort(dist[i])[:T] 166 | #temp = gf[indice] 167 | temp = np.mean(gf[indice], axis=0, keepdims=True) 168 | temp = np.concatenate((qf[i][np.newaxis, :], temp), axis=0) 169 | qf_new.append(np.mean(temp, axis=0, keepdims=True)) 170 | qf = np.squeeze(np.array(qf_new)) 171 | # feature norm 172 | q_n = np.linalg.norm(qf, axis=1, keepdims=True) 173 | qf = qf / q_n 174 | 175 | dist = np.dot(qf, np.transpose(gf)) 176 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 177 | #np.save('./data/gf.npy', gf) 178 | np.save('./data/qf_new_0.npy', qf) 179 | ''' 180 | -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/gallery_feature.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 27 15:03:13 2019 4 | 5 | @author: Deng 6 | """ 7 | 8 | import numpy as np 9 | import copy 10 | from sklearn.decomposition import PCA 11 | 12 | 13 | def softmax(x): 14 | """Compute the softmax in a numerically stable way.""" 15 | x = x - np.max(x) 16 | exp_x = np.exp(x) 17 | softmax_x = exp_x / np.sum(exp_x) 18 | return softmax_x 19 | 20 | # load feature 21 | gf = np.load('./data/gf_ori.npy') 22 | 23 | # tracklet name with ID 24 | track_name = [] 25 | f = open('./data/test_track_id.txt', 'r') 26 | for k, line in enumerate(f): 27 | temp = list(map(int, line.split(' ')[:-1])) 28 | track_name.append(list(map(lambda x: x-1, temp))) 29 | f.close 30 | 31 | # re-organize gallery feature 32 | T=6 #6 33 | for i in range(len(track_name)): 34 | indice = track_name[i] 35 | for j in range(0, len(indice), T): 36 | if (j+T)>len(indice): 37 | ind = indice[j:] 38 | else: 39 | ind = indice[j:j+T] 40 | gf_temp = np.mean(gf[ind], axis=0, keepdims=True) 41 | gf[ind] = gf_temp 42 | 43 | # feature norm 44 | g_n = np.linalg.norm(gf, axis=1, keepdims=True) 45 | gf = gf / g_n 46 | 47 | ''' 48 | # PCA for feature 49 | pca=PCA(n_components=1024) 50 | gf_new = pca.fit_transform(gf) 51 | qf_new = pca.transform(qf) 52 | ''' 53 | np.save('./data/gf_multi.npy', gf) 54 | -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/query_expansion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 27 15:03:13 2019 4 | 5 | @author: Deng 6 | """ 7 | 8 | import numpy as np 9 | 10 | 11 | #--------------------------- query expansion -----------------# 12 | # load feature 13 | gf = np.load('./data/gf_multi.npy') 14 | qf = np.load('./data/qf_ori.npy') 15 | 16 | # load indice 17 | qf_new = np.load('./data/qf_ori.npy') 18 | query_index = [] 19 | f = open('./data/query_new.txt', 'r') 20 | for k, line in enumerate(f): 21 | temp = int(line[0:6]) - 1 22 | query_index.append(temp) 23 | qf[k] = qf_new[temp] 24 | f.close() 25 | 26 | # feature norm 27 | q_n = np.linalg.norm(qf, axis=1, keepdims=True) 28 | qf = qf / q_n 29 | 30 | dist = np.dot(qf, np.transpose(gf)) 31 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 32 | 33 | qf_new = [] 34 | T = 9 35 | num = 1 36 | d_max = 10.0 37 | 38 | for t in range(num): 39 | qf_new = [] 40 | for i in range(len(dist)): 41 | indice = np.argsort(dist[i])[:T] 42 | temp = np.concatenate((qf[i][np.newaxis, :], gf[indice]), axis=0) 43 | qf_new.append(np.mean(temp, axis=0, keepdims=True)) 44 | 45 | qf = np.squeeze(np.array(qf_new)) 46 | # feature norm 47 | q_n = np.linalg.norm(qf, axis=1, keepdims=True) 48 | qf = qf / q_n 49 | 50 | dist = np.dot(qf, np.transpose(gf)) 51 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 52 | np.save('./data/qf_multi_%d.npy' % t, qf) 53 | -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/query_expansion_stamp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 27 15:03:13 2019 4 | 5 | @author: Deng 6 | """ 7 | 8 | import numpy as np 9 | 10 | 11 | # frame inforamtion 12 | frameQuery = np.load('./stamp/frameQuery.npy') 13 | frameTracklets = np.load('./stamp/frameGallery.npy') 14 | frameGallery = np.zeros([18290]) 15 | 16 | # tracklet name with ID 17 | g_name = [] 18 | f = open('./data/test_track_id.txt', 'r') 19 | for k, line in enumerate(f): 20 | temp = list(map(int, line.split(' ')[:-1])) 21 | g_name.append(list(map(lambda x: x-1, temp))) 22 | for indexTemp, idTemp in enumerate(temp): 23 | frameGallery[idTemp-1] = frameTracklets[k] 24 | f.close 25 | 26 | # camera inforamation 27 | q_cams = np.load('./stamp/q_cams.npy') 28 | g_cams = np.load('./stamp/gallery_camera_inf.npy') 29 | 30 | # location information 31 | g_index_s05 = np.load('./stamp/g_index_s05.npy') 32 | q_index_s05 = np.load('./stamp/q_index_s05.npy') 33 | 34 | q_index = np.load('./stamp/q_index_s02.npy') 35 | g_index = np.load('./stamp/g_index_s02.npy') 36 | 37 | #--------------- query expansion with location and time info --------------# 38 | # load feature 39 | gf = np.load('./data/gf_multi.npy') 40 | qf = np.load('./data/qf_multi.npy') 41 | 42 | dist = np.dot(qf, np.transpose(gf)) 43 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 44 | d_max = 10.0 45 | 46 | # time filter 47 | frameGap = 190 48 | for i in range(len(q_index)): 49 | key = q_index[i] 50 | indice = abs(frameGallery - frameQuery[key])> frameGap 51 | if q_cams[key]==6: 52 | indice = indice | (abs(frameGallery - frameQuery[key])> 60) & (g_cams == 9) 53 | elif q_cams[key] == 9: 54 | indice = indice | (abs(frameGallery - frameQuery[key])> 50) & (g_cams == 6) 55 | indice = np.nonzero(indice)[0] 56 | dist[key][indice] = d_max 57 | 58 | # camera filter for S02 and S05 59 | for i in range(len(dist)): 60 | indice = np.nonzero((g_cams == q_cams[i]))[0] 61 | dist[i][indice] = d_max 62 | 63 | # scene filter for S02 64 | g_indice_scene = g_index_s05 #S05 indice 65 | for i in range(len(q_index)): 66 | key = q_index[i] 67 | dist[key][g_indice_scene] = d_max 68 | 69 | # scene filter for S05 70 | g_indice_scene = g_index #S02 indice 71 | s05_indice = q_index_s05 72 | for i in range(len(s05_indice)): 73 | key = s05_indice[i] 74 | dist[key][g_indice_scene] = d_max 75 | 76 | #---------------------------- query expansion ----------------------------# 77 | qf_new = [] 78 | T = 9 79 | 80 | qf_new = [] 81 | for i in range(len(dist)): 82 | indice = np.argsort(dist[i])[:T] 83 | temp = np.concatenate((qf[i][np.newaxis, :], gf[indice]), axis=0) 84 | #temp = gf[indice] 85 | qf_new.append(np.mean(temp, axis=0, keepdims=True)) 86 | qf = np.squeeze(np.array(qf_new)) 87 | # feature norm 88 | q_n = np.linalg.norm(qf, axis=1, keepdims=True) 89 | qf = qf / q_n 90 | 91 | dist = np.dot(qf, np.transpose(gf)) 92 | dist = 2. - 2 * dist # change the cosine similarity metric to euclidean similarity metric 93 | #np.save('./data/gf.npy', gf) 94 | np.save('./data/qf_new.npy', qf) 95 | -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/frameGallery.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/frameGallery.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/frameQuery.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/frameQuery.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/g_index_s02.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/g_index_s02.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/g_index_s05.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/g_index_s05.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/gallery_camera_inf.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/gallery_camera_inf.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/orisGallery.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/orisGallery.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/orisQuery.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/orisQuery.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/q_cams.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/q_cams.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/q_index_s02.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/q_index_s02.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/stamp/q_index_s05.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/feature_expansion/stamp/q_index_s05.npy -------------------------------------------------------------------------------- /Feature_Learning/feature_expansion/use_stamps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Created on Sat Apr 27 15:03:13 2019 5 | 6 | @author: Lv 7 | """ 8 | 9 | import numpy as np 10 | import copy 11 | 12 | 13 | # frame inforamtion 14 | frameQuery = np.load('./stamp/frameQuery.npy') 15 | frameTracklets = np.load('./stamp/frameGallery.npy') 16 | frameGallery = np.zeros([18290]) #18290 is the num of gallery images 17 | 18 | # tracklet name with ID 19 | g_name = [] 20 | f = open('./data/test_track_id.txt', 'r') 21 | for k, line in enumerate(f): 22 | temp = list(map(int, line.split(' ')[:-1])) 23 | if len(temp)==1: 24 | continue 25 | g_name.append(list(map(lambda x: x-1, temp))) 26 | for indexTemp, idTemp in enumerate(temp): 27 | frameGallery[idTemp-1] = frameTracklets[k] 28 | f.close 29 | 30 | # camera inforamation 31 | q_cams = np.load('./stamp/q_cams.npy') 32 | g_cams = np.load('./stamp/gallery_camera_inf.npy') 33 | 34 | # distance matrix 35 | qf = np.load('./data/qf_new.npy') 36 | gf = np.load('./data/gf_multi.npy') 37 | distori = np.dot(qf, np.transpose(gf)) 38 | distori = 2. - 2 * distori # change the cosine similarity metric to euclidean similarity metric 39 | 40 | distmat = copy.deepcopy(distori) 41 | 42 | distance1 = np.zeros(36) 43 | distance1[27-1] = 0 44 | distance1[28-1] = 28 45 | distance1[29-1] = 46 46 | distance1[33-1] = 60 47 | distance1[34-1] = 76 48 | distance1[35-1] = 84 49 | distance1[36-1] = 90 50 | 51 | distance2 = np.zeros(36) 52 | distance2[27-1] = 94 53 | distance2[28-1] = 42 54 | distance2[29-1] = 36 55 | distance2[33-1] = 25 56 | distance2[34-1] = 21 57 | distance2[35-1] = 13 58 | distance2[36-1] = 0 59 | 60 | framegaps = np.ones([36,36])*400 61 | framegaps[26,:]= 460 62 | framegaps[27,:]= 180 63 | framegaps[28,:]= 240 64 | framegaps[32,:]= 270 65 | framegaps[33,:]= 170 66 | framegaps[34,:]= 130 67 | framegaps[35,:]= 120 68 | 69 | orisQuery = np.load('./stamp/orisQuery.npy') 70 | orisGallery = np.load('./stamp/orisGallery.npy') 71 | 72 | def func_stamps(distmat, q_camids, g_camids, frameQuery, frameGallery, framegaps, 73 | distance1, distance2, orisQuerys, orisGallerys, max_rank=100): 74 | 75 | num_q, num_g = distmat.shape 76 | newdist = np.zeros([num_q, num_g]) 77 | if num_g < max_rank: 78 | max_rank = num_g 79 | print("Note: number of gallery samples is quite small, got {}".format(num_g)) 80 | indices = np.argsort(distmat, axis=1) 81 | 82 | for q_idx in range(num_q): 83 | q_camid = q_camids[q_idx] 84 | q_frame = frameQuery[q_idx] 85 | oriQuery = orisQuerys[int(q_idx)] 86 | # remove gallery samples that have the same pid and camid with query 87 | order = indices[q_idx] 88 | remove = g_camids[order] == q_camid 89 | 90 | if q_camid < 10 : 91 | if q_camid == 6: 92 | remove = remove | ((abs(frameGallery[order] - q_frame) > 60) & (g_camids[order] == 9)) 93 | elif q_camid == 9: 94 | remove = remove | ((abs(frameGallery[order] - q_frame) > 50) & (g_camids[order] == 6)) 95 | else: 96 | cameras = [27, 28, 29, 33, 34, 35, 36] 97 | distQG = q_frame - frameGallery[order] 98 | for camera in cameras: 99 | if camera == q_camid: 100 | continue 101 | cameraGallery = camera 102 | if oriQuery == 0: 103 | center = (distance1[q_camid - 1] - distance1[cameraGallery - 1]) * 10 104 | else: 105 | center = (distance2[q_camid - 1] - distance2[cameraGallery - 1]) * 10 106 | remove = remove | ((abs(distQG - center) > framegaps[q_camid - 1, cameraGallery - 1]) & ( 107 | g_camids[order] == cameraGallery)) 108 | if oriQuery == 0: 109 | remove = remove | (orisGallery == 1.0) 110 | else: 111 | remove = remove | (orisGallery == 0.0) 112 | # filter retrieve results 113 | keep = np.invert(remove) 114 | newdist[q_idx, :np.sum(keep)] = distmat[q_idx][keep] 115 | distmat[q_idx][remove] = 2 # set distance to max number 116 | 117 | return distmat 118 | 119 | # fliter retrieve results by using location and time stamps 120 | dist = func_stamps(distmat, q_cams, g_cams, frameQuery, frameGallery, framegaps, 121 | distance1, distance2, orisQuery, orisGallery) 122 | 123 | # location information 124 | g_index_s05 = np.load('./stamp/g_index_s05.npy') 125 | q_index_s05 = np.load('./stamp/q_index_s05.npy') 126 | 127 | q_index = np.load('./stamp/q_index_s02.npy') 128 | g_index = np.load('./stamp/g_index_s02.npy') 129 | 130 | # scene filter for S02 131 | g_indice_scene = g_index_s05 #S05 indice 132 | for i in range(len(q_index)): 133 | key = q_index[i] 134 | dist[key][g_indice_scene] = 2 135 | 136 | # scene filter for S05 137 | g_indice_scene = g_index #S02 indice 138 | s05_indice = q_index_s05 139 | for i in range(len(s05_indice)): 140 | key = s05_indice[i] 141 | dist[key][g_indice_scene] = 2 142 | # save results 143 | np.save('./results/distmat_after.npy', dist) 144 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/config/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .defaults import _C as cfg 8 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/config/defaults.py: -------------------------------------------------------------------------------- 1 | from yacs.config import CfgNode as CN 2 | 3 | # ----------------------------------------------------------------------------- 4 | # Convention about Training / Test specific parameters 5 | # ----------------------------------------------------------------------------- 6 | # Whenever an argument can be either used for training or for testing, the 7 | # corresponding name will be post-fixed by a _TRAIN for a training parameter, 8 | # or _TEST for a test-specific parameter. 9 | # For example, the number of images during training will be 10 | # IMAGES_PER_BATCH_TRAIN, while the number of images for testing will be 11 | # IMAGES_PER_BATCH_TEST 12 | 13 | # ----------------------------------------------------------------------------- 14 | # Config definition 15 | # ----------------------------------------------------------------------------- 16 | 17 | _C = CN() 18 | 19 | _C.MODEL = CN() 20 | _C.MODEL.DEVICE = "cuda" 21 | _C.MODEL.NAME = 'densenet121' 22 | _C.MODEL.LAST_STRIDE = 1 23 | _C.MODEL.PRETRAIN_PATH = '' 24 | # ----------------------------------------------------------------------------- 25 | # INPUT 26 | # ----------------------------------------------------------------------------- 27 | _C.INPUT = CN() 28 | # Size of the image during training 29 | _C.INPUT.SIZE_TRAIN = [384, 128] 30 | # Size of the image during test 31 | _C.INPUT.SIZE_TEST = [384, 128] 32 | # Random probability for image horizontal flip 33 | _C.INPUT.PROB = 0.5 34 | # Values to be used for image normalization 35 | _C.INPUT.PIXEL_MEAN = [0.485, 0.456, 0.406] 36 | # Values to be used for image normalization 37 | _C.INPUT.PIXEL_STD = [0.229, 0.224, 0.225] 38 | # Value of padding size 39 | _C.INPUT.PADDING = 10 40 | # COLORJITTER 41 | _C.INPUT.COLORJITTER = False 42 | 43 | # ----------------------------------------------------------------------------- 44 | # Dataset 45 | # ----------------------------------------------------------------------------- 46 | _C.DATASETS = CN() 47 | # List of the dataset names for training, as present in paths_catalog.py 48 | _C.DATASETS.NAMES = ('market1501') 49 | 50 | # ----------------------------------------------------------------------------- 51 | # DataLoader 52 | # ----------------------------------------------------------------------------- 53 | _C.DATALOADER = CN() 54 | # Number of data loading threads 55 | _C.DATALOADER.NUM_WORKERS = 8 56 | # Sampler for data loading 57 | _C.DATALOADER.SAMPLER = 'softmax' 58 | # Number of instance for one batch 59 | _C.DATALOADER.NUM_INSTANCE = 16 60 | # SOFT_MARGIN 61 | _C.DATALOADER.SOFT_MARGIN = False 62 | 63 | # ---------------------------------------------------------------------------- # 64 | # Solver 65 | # ---------------------------------------------------------------------------- # 66 | _C.SOLVER = CN() 67 | _C.SOLVER.OPTIMIZER_NAME = "Adam" 68 | 69 | _C.SOLVER.MAX_EPOCHS = 50 70 | 71 | _C.SOLVER.BASE_LR = 3e-4 72 | _C.SOLVER.BIAS_LR_FACTOR = 2 73 | 74 | _C.SOLVER.MOMENTUM = 0.9 75 | 76 | _C.SOLVER.MARGIN = 0.3 # None for soft margin or 0.3 77 | 78 | _C.SOLVER.WEIGHT_DECAY = 0.0005 79 | _C.SOLVER.WEIGHT_DECAY_BIAS = 0. 80 | 81 | _C.SOLVER.GAMMA = 0.1 82 | _C.SOLVER.STEPS = (30, 55) 83 | 84 | _C.SOLVER.WARMUP_FACTOR = 1.0 / 3 85 | _C.SOLVER.WARMUP_ITERS = 500 86 | _C.SOLVER.WARMUP_METHOD = "linear" 87 | 88 | _C.SOLVER.CHECKPOINT_PERIOD = 50 89 | _C.SOLVER.LOG_PERIOD = 100 90 | _C.SOLVER.EVAL_PERIOD = 50 91 | # Number of images per batch 92 | # This is global, so if we have 8 GPUs and IMS_PER_BATCH = 16, each GPU will 93 | # see 2 images per batch 94 | _C.SOLVER.IMS_PER_BATCH = 64 95 | 96 | # This is global, so if we have 8 GPUs and IMS_PER_BATCH = 16, each GPU will 97 | # see 2 images per batch 98 | _C.TEST = CN() 99 | _C.TEST.IMS_PER_BATCH = 128 100 | _C.TEST.WEIGHT = "" 101 | _C.TEST.QF_NAME = "" 102 | _C.TEST.GF_NAME = "" 103 | # ---------------------------------------------------------------------------- # 104 | # Misc options 105 | # ---------------------------------------------------------------------------- # 106 | _C.OUTPUT_DIR = "" 107 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/configs/softmax_triplet_VR.yml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | PRETRAIN_PATH: './modeling/densenet121-fbdb23505.pth' 3 | 4 | 5 | INPUT: 6 | SIZE_TRAIN: [288, 288] 7 | SIZE_TEST: [256, 256] 8 | PROB: 0.5 # random horizontal flip 9 | PADDING: 10 10 | COLORJITTER: False # for model ensemble 11 | 12 | DATASETS: 13 | NAMES: ('VR') 14 | 15 | DATALOADER: 16 | SAMPLER: 'softmax_triplet' 17 | NUM_INSTANCE: 4 18 | NUM_WORKERS: 2 19 | SOFT_MARGIN: False # for model ensemble 20 | 21 | SOLVER: 22 | OPTIMIZER_NAME: 'SGD' 23 | MAX_EPOCHS: 120 24 | BASE_LR: 0.01 #0.00035 25 | BIAS_LR_FACTOR: 1 26 | WEIGHT_DECAY: 0.0005 27 | WEIGHT_DECAY_BIAS: 0.0005 28 | IMS_PER_BATCH: 64 29 | 30 | STEPS: [30, 60, 100] 31 | GAMMA: 0.1 32 | 33 | WARMUP_FACTOR: 0.01 34 | WARMUP_ITERS: 10 35 | WARMUP_METHOD: 'linear' 36 | 37 | CHECKPOINT_PERIOD: 20 38 | LOG_PERIOD: 120 39 | EVAL_PERIOD: 20 40 | 41 | TEST: 42 | IMS_PER_BATCH: 256 43 | WEIGHT: "path" 44 | 45 | OUTPUT_DIR: "./CHECKPOINTS/VR/softmax_triplet_LSR_VR" 46 | 47 | 48 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/configs/softmax_triplet_VR_test.yml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | PRETRAIN_PATH: './modeling/densenet121-fbdb23505.pth' 3 | 4 | 5 | INPUT: 6 | SIZE_TRAIN: [288, 288] 7 | SIZE_TEST: [256, 256] 8 | PROB: 0.5 # random horizontal flip 9 | PADDING: 10 10 | COLORJITTER: False # for model ensemble 11 | 12 | DATASETS: 13 | NAMES: ('VR_test') 14 | 15 | DATALOADER: 16 | SAMPLER: 'softmax_triplet' 17 | NUM_INSTANCE: 4 18 | NUM_WORKERS: 2 19 | SOFT_MARGIN: False # for model ensemble 20 | 21 | SOLVER: 22 | OPTIMIZER_NAME: 'SGD' 23 | MAX_EPOCHS: 120 24 | BASE_LR: 0.01 #0.00035 25 | BIAS_LR_FACTOR: 1 26 | WEIGHT_DECAY: 0.0005 27 | WEIGHT_DECAY_BIAS: 0.0005 28 | IMS_PER_BATCH: 64 29 | 30 | STEPS: [30, 60, 100] 31 | GAMMA: 0.1 32 | 33 | WARMUP_FACTOR: 0.01 34 | WARMUP_ITERS: 10 35 | WARMUP_METHOD: 'linear' 36 | 37 | CHECKPOINT_PERIOD: 20 38 | LOG_PERIOD: 120 39 | EVAL_PERIOD: 20 40 | 41 | TEST: 42 | IMS_PER_BATCH: 256 43 | WEIGHT: "path" 44 | QF_NAME: "qf_ori" 45 | GF_NAME: "gf_ori" 46 | 47 | OUTPUT_DIR: "./CHECKPOINTS/VR/softmax_triplet_LSR_VeRi_test" 48 | 49 | 50 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/configs/softmax_triplet_VeRi.yml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | PRETRAIN_PATH: './modeling/densenet121-fbdb23505.pth' 3 | 4 | 5 | INPUT: 6 | SIZE_TRAIN: [288, 288] 7 | SIZE_TEST: [256, 256] 8 | PROB: 0.5 # random horizontal flip 9 | PADDING: 10 10 | COLORJITTER: False # for model ensemble 11 | 12 | DATASETS: 13 | NAMES: ('VeRi') 14 | 15 | DATALOADER: 16 | SAMPLER: 'softmax_triplet' 17 | NUM_INSTANCE: 4 18 | NUM_WORKERS: 2 19 | SOFT_MARGIN: False # for model ensemble 20 | 21 | SOLVER: 22 | OPTIMIZER_NAME: 'SGD' 23 | MAX_EPOCHS: 120 24 | BASE_LR: 0.01 #0.00035 25 | BIAS_LR_FACTOR: 1 26 | WEIGHT_DECAY: 0.0005 27 | WEIGHT_DECAY_BIAS: 0.0005 28 | IMS_PER_BATCH: 64 29 | 30 | STEPS: [30, 60, 100] 31 | GAMMA: 0.1 32 | 33 | WARMUP_FACTOR: 0.01 34 | WARMUP_ITERS: 10 35 | WARMUP_METHOD: 'linear' 36 | 37 | CHECKPOINT_PERIOD: 20 38 | LOG_PERIOD: 120 39 | EVAL_PERIOD: 20 40 | 41 | TEST: 42 | IMS_PER_BATCH: 256 43 | WEIGHT: "path" 44 | 45 | OUTPUT_DIR: "./CHECKPOINTS/VeRi/softmax_triplet_LSR_VeRi" 46 | 47 | 48 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import make_data_loader 8 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from torch.utils.data import DataLoader 8 | 9 | from .collate_batch import train_collate_fn, val_collate_fn 10 | from .datasets import init_dataset, ImageDataset 11 | from .samplers import RandomIdentitySampler 12 | from .transforms import build_transforms 13 | 14 | 15 | def make_data_loader(cfg): 16 | train_transforms = build_transforms(cfg, is_train=True) 17 | val_transforms = build_transforms(cfg, is_train=False) 18 | num_workers = cfg.DATALOADER.NUM_WORKERS 19 | if len(cfg.DATASETS.NAMES) == 1: 20 | dataset = init_dataset(cfg.DATASETS.NAMES) 21 | else: 22 | # TODO: add multi dataset to train 23 | dataset = init_dataset(cfg.DATASETS.NAMES) 24 | 25 | num_classes = dataset.num_train_pids 26 | train_set = ImageDataset(dataset.train, train_transforms) 27 | if cfg.DATALOADER.SAMPLER == 'softmax': 28 | train_loader = DataLoader( 29 | train_set, batch_size=cfg.SOLVER.IMS_PER_BATCH, shuffle=True, num_workers=num_workers, 30 | collate_fn=train_collate_fn 31 | ) 32 | else: 33 | train_loader = DataLoader( 34 | train_set, batch_size=cfg.SOLVER.IMS_PER_BATCH, 35 | sampler=RandomIdentitySampler(dataset.train, cfg.SOLVER.IMS_PER_BATCH, cfg.DATALOADER.NUM_INSTANCE), 36 | num_workers=num_workers, collate_fn=train_collate_fn 37 | ) 38 | 39 | val_set = ImageDataset(dataset.query + dataset.gallery, val_transforms) 40 | val_loader = DataLoader( 41 | val_set, batch_size=cfg.TEST.IMS_PER_BATCH, shuffle=False, num_workers=num_workers, 42 | collate_fn=val_collate_fn 43 | ) 44 | return train_loader, val_loader, len(dataset.query), num_classes 45 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/collate_batch.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | 9 | 10 | def train_collate_fn(batch): 11 | imgs, pids, _, _, = zip(*batch) 12 | pids = torch.tensor(pids, dtype=torch.int64) 13 | return torch.stack(imgs, dim=0), pids 14 | 15 | 16 | def val_collate_fn(batch): 17 | imgs, pids, camids, im_path = zip(*batch) 18 | return torch.stack(imgs, dim=0), pids, camids 19 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/VR.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: weijian 4 | @contact: dengwj16@gmail.com 5 | """ 6 | 7 | import glob 8 | import re 9 | import xml.dom.minidom as XD 10 | import os.path as osp 11 | 12 | from .bases import BaseImageDataset 13 | 14 | 15 | class VR(BaseImageDataset): 16 | """ 17 | VR 18 | 19 | Dataset statistics: 20 | 21 | """ 22 | dataset_dir = 'VR' 23 | dataset_dir_test = './data/VeRi' 24 | 25 | def __init__(self, root='./data', verbose=True, **kwargs): 26 | super(VR, self).__init__() 27 | self.dataset_dir = osp.join(root, self.dataset_dir) 28 | self.train_dir = osp.join(self.dataset_dir, 'image_train/') 29 | self.query_dir = osp.join(self.dataset_dir_test, 'image_query/') 30 | self.gallery_dir = osp.join(self.dataset_dir_test, 'image_test/') 31 | 32 | self._check_before_run() 33 | 34 | train = self._process_dir(self.train_dir, relabel=True) 35 | query = self._process_dir_test(self.query_dir, relabel=False) 36 | gallery = self._process_dir_test(self.gallery_dir, relabel=False) 37 | 38 | if verbose: 39 | print("=> VR loaded") 40 | self.print_dataset_statistics(train, query, gallery) 41 | 42 | self.train = train 43 | self.query = query 44 | self.gallery = gallery 45 | 46 | self.num_train_pids, self.num_train_imgs, self.num_train_cams = self.get_imagedata_info(self.train) 47 | self.num_query_pids, self.num_query_imgs, self.num_query_cams = self.get_imagedata_info(self.query) 48 | self.num_gallery_pids, self.num_gallery_imgs, self.num_gallery_cams = self.get_imagedata_info(self.gallery) 49 | 50 | def _check_before_run(self): 51 | """Check if all files are available before going deeper""" 52 | if not osp.exists(self.dataset_dir): 53 | raise RuntimeError("'{}' is not available".format(self.dataset_dir)) 54 | if not osp.exists(self.train_dir): 55 | raise RuntimeError("'{}' is not available".format(self.train_dir)) 56 | if not osp.exists(self.query_dir): 57 | raise RuntimeError("'{}' is not available".format(self.query_dir)) 58 | if not osp.exists(self.gallery_dir): 59 | raise RuntimeError("'{}' is not available".format(self.gallery_dir)) 60 | 61 | def _process_dir(self, dir_path, relabel=False): 62 | xml_dir =osp.join('./data/VR', 'train_label.xml') 63 | info = XD.parse(xml_dir).documentElement.getElementsByTagName('Item') 64 | 65 | pid_container = set() 66 | for element in range(len(info)): 67 | pid = int(info[element].getAttribute('vehicleID')) 68 | if pid == -1: continue # junk images are just ignored 69 | pid_container.add(pid) 70 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 71 | 72 | dataset = [] 73 | for element in range(len(info)): 74 | pid, camid = map(int, [info[element].getAttribute('vehicleID'), info[element].getAttribute('cameraID')[1:]]) 75 | image_name = str(info[element].getAttribute('imageName')) 76 | if pid == -1: continue # junk images are just ignored 77 | if relabel: pid = pid2label[pid] 78 | dataset.append((osp.join(dir_path, image_name), pid, camid)) 79 | 80 | return dataset 81 | 82 | def _process_dir_test(self, dir_path, relabel=False): 83 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 84 | pattern = re.compile(r'([-\d]+)_c(\d\d\d)') 85 | pid_container = set() 86 | for img_path in img_paths: 87 | pid, _ = map(int, pattern.search(img_path).groups()) 88 | if pid == -1: continue # junk images are just ignored 89 | pid_container.add(pid) 90 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 91 | 92 | dataset = [] 93 | for img_path in img_paths: 94 | pid, camid = map(int, pattern.search(img_path).groups()) 95 | if pid == -1: continue # junk images are just ignored 96 | camid -= 1 # index starts from 0 97 | if relabel: pid = pid2label[pid] 98 | dataset.append((img_path, pid, camid)) 99 | 100 | return dataset 101 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/VR_test.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: weijian 4 | @contact: dengwj16@gmail.com 5 | """ 6 | 7 | import glob 8 | import re 9 | import xml.dom.minidom as XD 10 | import os.path as osp 11 | 12 | from .bases import BaseImageDataset 13 | 14 | 15 | class VR_test(BaseImageDataset): 16 | """ 17 | VR_test 18 | 19 | Dataset statistics: 20 | 21 | """ 22 | dataset_dir = 'VR' 23 | dataset_dir_test = './data/VR' 24 | 25 | def __init__(self, root='./data', verbose=True, **kwargs): 26 | super(VR_test, self).__init__() 27 | self.dataset_dir = osp.join(root, self.dataset_dir) 28 | self.train_dir = osp.join(self.dataset_dir, 'image_train/') 29 | self.query_dir = osp.join(self.dataset_dir_test, 'image_query/') 30 | self.gallery_dir = osp.join(self.dataset_dir_test, 'image_test/') 31 | 32 | self._check_before_run() 33 | 34 | train = self._process_dir(self.train_dir, relabel=True) 35 | query = self._process_dir_demo(self.query_dir, relabel=False) 36 | gallery = self._process_dir_demo(self.gallery_dir, relabel=False) 37 | if verbose: 38 | print("=> VR loaded") 39 | self.print_dataset_statistics(train, query, gallery) 40 | 41 | self.train = train 42 | self.query = query 43 | self.gallery = gallery 44 | 45 | self.num_train_pids, self.num_train_imgs, self.num_train_cams = self.get_imagedata_info(self.train) 46 | self.num_query_pids, self.num_query_imgs, self.num_query_cams = self.get_imagedata_info(self.query) 47 | self.num_gallery_pids, self.num_gallery_imgs, self.num_gallery_cams = self.get_imagedata_info(self.gallery) 48 | 49 | def _check_before_run(self): 50 | """Check if all files are available before going deeper""" 51 | if not osp.exists(self.dataset_dir): 52 | raise RuntimeError("'{}' is not available".format(self.dataset_dir)) 53 | if not osp.exists(self.train_dir): 54 | raise RuntimeError("'{}' is not available".format(self.train_dir)) 55 | if not osp.exists(self.query_dir): 56 | raise RuntimeError("'{}' is not available".format(self.query_dir)) 57 | if not osp.exists(self.gallery_dir): 58 | raise RuntimeError("'{}' is not available".format(self.gallery_dir)) 59 | 60 | def _process_dir(self, dir_path, relabel=False): 61 | xml_dir =osp.join('./data/VR', 'train_label.xml') 62 | info = XD.parse(xml_dir).documentElement.getElementsByTagName('Item') 63 | 64 | pid_container = set() 65 | for element in range(len(info)): 66 | pid = int(info[element].getAttribute('vehicleID')) 67 | if pid == -1: continue # junk images are just ignored 68 | pid_container.add(pid) 69 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 70 | 71 | dataset = [] 72 | for element in range(len(info)): 73 | pid, camid = map(int, [info[element].getAttribute('vehicleID'), info[element].getAttribute('cameraID')[1:]]) 74 | image_name = str(info[element].getAttribute('imageName')) 75 | camid -= 1 # index starts from 0 76 | if relabel: pid = pid2label[pid] 77 | dataset.append((osp.join(dir_path, image_name), pid, camid)) 78 | 79 | return dataset 80 | 81 | 82 | def _process_dir_demo(self, dir_path, relabel=False): 83 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 84 | img_paths.sort() 85 | pid_container = set() 86 | for img_path in img_paths: 87 | pid = 1 88 | if pid == -1: continue # junk images are just ignored 89 | pid_container.add(pid) 90 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 91 | 92 | dataset = [] 93 | for img_path in img_paths: 94 | pid, camid = 1, 2 95 | if pid == -1: continue # junk images are just ignored 96 | camid -= 1 # index starts from 0 97 | if relabel: pid = pid2label[pid] 98 | dataset.append((img_path, pid, camid)) 99 | 100 | return dataset 101 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/VeRi.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: weijian 4 | @contact: dengwj16@gmail.com 5 | """ 6 | 7 | import glob 8 | import re 9 | import xml.dom.minidom as XD 10 | import os.path as osp 11 | 12 | from .bases import BaseImageDataset 13 | 14 | 15 | class VeRi(BaseImageDataset): 16 | """ 17 | 18 | VeRi 19 | 20 | """ 21 | dataset_dir = 'VeRi' 22 | dataset_dir_test = './data/VeRi' 23 | 24 | def __init__(self, root='./data', verbose=True, **kwargs): 25 | super(VeRi, self).__init__() 26 | self.dataset_dir = osp.join(root, self.dataset_dir) 27 | self.train_dir = osp.join(self.dataset_dir, 'image_train/') 28 | self.query_dir = osp.join(self.dataset_dir, 'image_query/') 29 | self.gallery_dir = osp.join(self.dataset_dir, 'image_test/') 30 | 31 | self._check_before_run() 32 | 33 | train = self._process_dir_test(self.train_dir, relabel=True) 34 | query = self._process_dir_test(self.query_dir, relabel=False) 35 | gallery = self._process_dir_test(self.gallery_dir, relabel=False) 36 | 37 | if verbose: 38 | print("=> VR loaded") 39 | self.print_dataset_statistics(train, query, gallery) 40 | 41 | self.train = train 42 | self.query = query 43 | self.gallery = gallery 44 | 45 | self.num_train_pids, self.num_train_imgs, self.num_train_cams = self.get_imagedata_info(self.train) 46 | self.num_query_pids, self.num_query_imgs, self.num_query_cams = self.get_imagedata_info(self.query) 47 | self.num_gallery_pids, self.num_gallery_imgs, self.num_gallery_cams = self.get_imagedata_info(self.gallery) 48 | 49 | def _check_before_run(self): 50 | """Check if all files are available before going deeper""" 51 | if not osp.exists(self.dataset_dir): 52 | raise RuntimeError("'{}' is not available".format(self.dataset_dir)) 53 | if not osp.exists(self.train_dir): 54 | raise RuntimeError("'{}' is not available".format(self.train_dir)) 55 | if not osp.exists(self.query_dir): 56 | raise RuntimeError("'{}' is not available".format(self.query_dir)) 57 | if not osp.exists(self.gallery_dir): 58 | raise RuntimeError("'{}' is not available".format(self.gallery_dir)) 59 | 60 | 61 | def _process_dir_test(self, dir_path, relabel=False): 62 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 63 | pattern = re.compile(r'([-\d]+)_c(\d\d\d)') 64 | pid_container = set() 65 | for img_path in img_paths: 66 | pid, _ = map(int, pattern.search(img_path).groups()) 67 | if pid == -1: continue # junk images are just ignored 68 | pid_container.add(pid) 69 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 70 | 71 | dataset = [] 72 | for img_path in img_paths: 73 | pid, camid = map(int, pattern.search(img_path).groups()) 74 | if pid == -1: continue # junk images are just ignored 75 | camid -= 1 # index starts from 0 76 | if relabel: pid = pid2label[pid] 77 | dataset.append((img_path, pid, camid)) 78 | 79 | return dataset 80 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | from .cuhk03 import CUHK03 7 | from .dukemtmcreid import DukeMTMCreID 8 | from .market1501 import Market1501 9 | from .VR import VR 10 | from .VR_test import VR_test 11 | from .VeRi import VeRi 12 | from .dataset_loader import ImageDataset 13 | 14 | __factory = { 15 | 'market1501': Market1501, 16 | 'cuhk03': CUHK03, 17 | 'dukemtmc': DukeMTMCreID, 18 | 'VR': VR, 19 | 'VR_test': VR_test, 20 | 'VeRi': VeRi, 21 | } 22 | 23 | 24 | def get_names(): 25 | return __factory.keys() 26 | 27 | 28 | def init_dataset(name, *args, **kwargs): 29 | if name not in __factory.keys(): 30 | raise KeyError("Unknown datasets: {}".format(name)) 31 | return __factory[name](*args, **kwargs) 32 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/bases.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import numpy as np 8 | 9 | 10 | class BaseDataset(object): 11 | """ 12 | Base class of reid dataset 13 | """ 14 | 15 | def get_imagedata_info(self, data): 16 | pids, cams = [], [] 17 | for _, pid, camid in data: 18 | pids += [pid] 19 | cams += [camid] 20 | pids = set(pids) 21 | cams = set(cams) 22 | num_pids = len(pids) 23 | num_cams = len(cams) 24 | num_imgs = len(data) 25 | return num_pids, num_imgs, num_cams 26 | 27 | def get_videodata_info(self, data, return_tracklet_stats=False): 28 | pids, cams, tracklet_stats = [], [], [] 29 | for img_paths, pid, camid in data: 30 | pids += [pid] 31 | cams += [camid] 32 | tracklet_stats += [len(img_paths)] 33 | pids = set(pids) 34 | cams = set(cams) 35 | num_pids = len(pids) 36 | num_cams = len(cams) 37 | num_tracklets = len(data) 38 | if return_tracklet_stats: 39 | return num_pids, num_tracklets, num_cams, tracklet_stats 40 | return num_pids, num_tracklets, num_cams 41 | 42 | def print_dataset_statistics(self): 43 | raise NotImplementedError 44 | 45 | 46 | class BaseImageDataset(BaseDataset): 47 | """ 48 | Base class of image reid dataset 49 | """ 50 | 51 | def print_dataset_statistics(self, train, query, gallery): 52 | num_train_pids, num_train_imgs, num_train_cams = self.get_imagedata_info(train) 53 | num_query_pids, num_query_imgs, num_query_cams = self.get_imagedata_info(query) 54 | num_gallery_pids, num_gallery_imgs, num_gallery_cams = self.get_imagedata_info(gallery) 55 | 56 | print("Dataset statistics:") 57 | print(" ----------------------------------------") 58 | print(" subset | # ids | # images | # cameras") 59 | print(" ----------------------------------------") 60 | print(" train | {:5d} | {:8d} | {:9d}".format(num_train_pids, num_train_imgs, num_train_cams)) 61 | print(" query | {:5d} | {:8d} | {:9d}".format(num_query_pids, num_query_imgs, num_query_cams)) 62 | print(" gallery | {:5d} | {:8d} | {:9d}".format(num_gallery_pids, num_gallery_imgs, num_gallery_cams)) 63 | print(" ----------------------------------------") 64 | 65 | 66 | class BaseVideoDataset(BaseDataset): 67 | """ 68 | Base class of video reid dataset 69 | """ 70 | 71 | def print_dataset_statistics(self, train, query, gallery): 72 | num_train_pids, num_train_tracklets, num_train_cams, train_tracklet_stats = \ 73 | self.get_videodata_info(train, return_tracklet_stats=True) 74 | 75 | num_query_pids, num_query_tracklets, num_query_cams, query_tracklet_stats = \ 76 | self.get_videodata_info(query, return_tracklet_stats=True) 77 | 78 | num_gallery_pids, num_gallery_tracklets, num_gallery_cams, gallery_tracklet_stats = \ 79 | self.get_videodata_info(gallery, return_tracklet_stats=True) 80 | 81 | tracklet_stats = train_tracklet_stats + query_tracklet_stats + gallery_tracklet_stats 82 | min_num = np.min(tracklet_stats) 83 | max_num = np.max(tracklet_stats) 84 | avg_num = np.mean(tracklet_stats) 85 | 86 | print("Dataset statistics:") 87 | print(" -------------------------------------------") 88 | print(" subset | # ids | # tracklets | # cameras") 89 | print(" -------------------------------------------") 90 | print(" train | {:5d} | {:11d} | {:9d}".format(num_train_pids, num_train_tracklets, num_train_cams)) 91 | print(" query | {:5d} | {:11d} | {:9d}".format(num_query_pids, num_query_tracklets, num_query_cams)) 92 | print(" gallery | {:5d} | {:11d} | {:9d}".format(num_gallery_pids, num_gallery_tracklets, num_gallery_cams)) 93 | print(" -------------------------------------------") 94 | print(" number of images per tracklet: {} ~ {}, average {:.2f}".format(min_num, max_num, avg_num)) 95 | print(" -------------------------------------------") 96 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/cuhk03.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: liaoxingyu2@jd.com 5 | """ 6 | 7 | import h5py 8 | import os.path as osp 9 | from scipy.io import loadmat 10 | from scipy.misc import imsave 11 | 12 | from utils.iotools import mkdir_if_missing, write_json, read_json 13 | from .bases import BaseImageDataset 14 | 15 | 16 | class CUHK03(BaseImageDataset): 17 | """ 18 | CUHK03 19 | Reference: 20 | Li et al. DeepReID: Deep Filter Pairing Neural Network for Person Re-identification. CVPR 2014. 21 | URL: http://www.ee.cuhk.edu.hk/~xgwang/CUHK_identification.html#! 22 | 23 | Dataset statistics: 24 | # identities: 1360 25 | # images: 13164 26 | # cameras: 6 27 | # splits: 20 (classic) 28 | Args: 29 | split_id (int): split index (default: 0) 30 | cuhk03_labeled (bool): whether to load labeled images; if false, detected images are loaded (default: False) 31 | """ 32 | dataset_dir = 'cuhk03' 33 | 34 | def __init__(self, root='/export/home/lxy/DATA/reid', split_id=0, cuhk03_labeled=False, 35 | cuhk03_classic_split=False, verbose=True, 36 | **kwargs): 37 | super(CUHK03, self).__init__() 38 | self.dataset_dir = osp.join(root, self.dataset_dir) 39 | self.data_dir = osp.join(self.dataset_dir, 'cuhk03_release') 40 | self.raw_mat_path = osp.join(self.data_dir, 'cuhk-03.mat') 41 | 42 | self.imgs_detected_dir = osp.join(self.dataset_dir, 'images_detected') 43 | self.imgs_labeled_dir = osp.join(self.dataset_dir, 'images_labeled') 44 | 45 | self.split_classic_det_json_path = osp.join(self.dataset_dir, 'splits_classic_detected.json') 46 | self.split_classic_lab_json_path = osp.join(self.dataset_dir, 'splits_classic_labeled.json') 47 | 48 | self.split_new_det_json_path = osp.join(self.dataset_dir, 'splits_new_detected.json') 49 | self.split_new_lab_json_path = osp.join(self.dataset_dir, 'splits_new_labeled.json') 50 | 51 | self.split_new_det_mat_path = osp.join(self.dataset_dir, 'cuhk03_new_protocol_config_detected.mat') 52 | self.split_new_lab_mat_path = osp.join(self.dataset_dir, 'cuhk03_new_protocol_config_labeled.mat') 53 | 54 | self._check_before_run() 55 | self._preprocess() 56 | 57 | if cuhk03_labeled: 58 | image_type = 'labeled' 59 | split_path = self.split_classic_lab_json_path if cuhk03_classic_split else self.split_new_lab_json_path 60 | else: 61 | image_type = 'detected' 62 | split_path = self.split_classic_det_json_path if cuhk03_classic_split else self.split_new_det_json_path 63 | 64 | splits = read_json(split_path) 65 | assert split_id < len(splits), "Condition split_id ({}) < len(splits) ({}) is false".format(split_id, 66 | len(splits)) 67 | split = splits[split_id] 68 | print("Split index = {}".format(split_id)) 69 | 70 | train = split['train'] 71 | query = split['query'] 72 | gallery = split['gallery'] 73 | 74 | if verbose: 75 | print("=> CUHK03 ({}) loaded".format(image_type)) 76 | self.print_dataset_statistics(train, query, gallery) 77 | 78 | self.train = train 79 | self.query = query 80 | self.gallery = gallery 81 | 82 | self.num_train_pids, self.num_train_imgs, self.num_train_cams = self.get_imagedata_info(self.train) 83 | self.num_query_pids, self.num_query_imgs, self.num_query_cams = self.get_imagedata_info(self.query) 84 | self.num_gallery_pids, self.num_gallery_imgs, self.num_gallery_cams = self.get_imagedata_info(self.gallery) 85 | 86 | def _check_before_run(self): 87 | """Check if all files are available before going deeper""" 88 | if not osp.exists(self.dataset_dir): 89 | raise RuntimeError("'{}' is not available".format(self.dataset_dir)) 90 | if not osp.exists(self.data_dir): 91 | raise RuntimeError("'{}' is not available".format(self.data_dir)) 92 | if not osp.exists(self.raw_mat_path): 93 | raise RuntimeError("'{}' is not available".format(self.raw_mat_path)) 94 | if not osp.exists(self.split_new_det_mat_path): 95 | raise RuntimeError("'{}' is not available".format(self.split_new_det_mat_path)) 96 | if not osp.exists(self.split_new_lab_mat_path): 97 | raise RuntimeError("'{}' is not available".format(self.split_new_lab_mat_path)) 98 | 99 | def _preprocess(self): 100 | """ 101 | This function is a bit complex and ugly, what it does is 102 | 1. Extract data from cuhk-03.mat and save as png images. 103 | 2. Create 20 classic splits. (Li et al. CVPR'14) 104 | 3. Create new split. (Zhong et al. CVPR'17) 105 | """ 106 | print( 107 | "Note: if root path is changed, the previously generated json files need to be re-generated (delete them first)") 108 | if osp.exists(self.imgs_labeled_dir) and \ 109 | osp.exists(self.imgs_detected_dir) and \ 110 | osp.exists(self.split_classic_det_json_path) and \ 111 | osp.exists(self.split_classic_lab_json_path) and \ 112 | osp.exists(self.split_new_det_json_path) and \ 113 | osp.exists(self.split_new_lab_json_path): 114 | return 115 | 116 | mkdir_if_missing(self.imgs_detected_dir) 117 | mkdir_if_missing(self.imgs_labeled_dir) 118 | 119 | print("Extract image data from {} and save as png".format(self.raw_mat_path)) 120 | mat = h5py.File(self.raw_mat_path, 'r') 121 | 122 | def _deref(ref): 123 | return mat[ref][:].T 124 | 125 | def _process_images(img_refs, campid, pid, save_dir): 126 | img_paths = [] # Note: some persons only have images for one view 127 | for imgid, img_ref in enumerate(img_refs): 128 | img = _deref(img_ref) 129 | # skip empty cell 130 | if img.size == 0 or img.ndim < 3: continue 131 | # images are saved with the following format, index-1 (ensure uniqueness) 132 | # campid: index of camera pair (1-5) 133 | # pid: index of person in 'campid'-th camera pair 134 | # viewid: index of view, {1, 2} 135 | # imgid: index of image, (1-10) 136 | viewid = 1 if imgid < 5 else 2 137 | img_name = '{:01d}_{:03d}_{:01d}_{:02d}.png'.format(campid + 1, pid + 1, viewid, imgid + 1) 138 | img_path = osp.join(save_dir, img_name) 139 | if not osp.isfile(img_path): 140 | imsave(img_path, img) 141 | img_paths.append(img_path) 142 | return img_paths 143 | 144 | def _extract_img(name): 145 | print("Processing {} images (extract and save) ...".format(name)) 146 | meta_data = [] 147 | imgs_dir = self.imgs_detected_dir if name == 'detected' else self.imgs_labeled_dir 148 | for campid, camp_ref in enumerate(mat[name][0]): 149 | camp = _deref(camp_ref) 150 | num_pids = camp.shape[0] 151 | for pid in range(num_pids): 152 | img_paths = _process_images(camp[pid, :], campid, pid, imgs_dir) 153 | assert len(img_paths) > 0, "campid{}-pid{} has no images".format(campid, pid) 154 | meta_data.append((campid + 1, pid + 1, img_paths)) 155 | print("- done camera pair {} with {} identities".format(campid + 1, num_pids)) 156 | return meta_data 157 | 158 | meta_detected = _extract_img('detected') 159 | meta_labeled = _extract_img('labeled') 160 | 161 | def _extract_classic_split(meta_data, test_split): 162 | train, test = [], [] 163 | num_train_pids, num_test_pids = 0, 0 164 | num_train_imgs, num_test_imgs = 0, 0 165 | for i, (campid, pid, img_paths) in enumerate(meta_data): 166 | 167 | if [campid, pid] in test_split: 168 | for img_path in img_paths: 169 | camid = int(osp.basename(img_path).split('_')[2]) - 1 # make it 0-based 170 | test.append((img_path, num_test_pids, camid)) 171 | num_test_pids += 1 172 | num_test_imgs += len(img_paths) 173 | else: 174 | for img_path in img_paths: 175 | camid = int(osp.basename(img_path).split('_')[2]) - 1 # make it 0-based 176 | train.append((img_path, num_train_pids, camid)) 177 | num_train_pids += 1 178 | num_train_imgs += len(img_paths) 179 | return train, num_train_pids, num_train_imgs, test, num_test_pids, num_test_imgs 180 | 181 | print("Creating classic splits (# = 20) ...") 182 | splits_classic_det, splits_classic_lab = [], [] 183 | for split_ref in mat['testsets'][0]: 184 | test_split = _deref(split_ref).tolist() 185 | 186 | # create split for detected images 187 | train, num_train_pids, num_train_imgs, test, num_test_pids, num_test_imgs = \ 188 | _extract_classic_split(meta_detected, test_split) 189 | splits_classic_det.append({ 190 | 'train': train, 'query': test, 'gallery': test, 191 | 'num_train_pids': num_train_pids, 'num_train_imgs': num_train_imgs, 192 | 'num_query_pids': num_test_pids, 'num_query_imgs': num_test_imgs, 193 | 'num_gallery_pids': num_test_pids, 'num_gallery_imgs': num_test_imgs, 194 | }) 195 | 196 | # create split for labeled images 197 | train, num_train_pids, num_train_imgs, test, num_test_pids, num_test_imgs = \ 198 | _extract_classic_split(meta_labeled, test_split) 199 | splits_classic_lab.append({ 200 | 'train': train, 'query': test, 'gallery': test, 201 | 'num_train_pids': num_train_pids, 'num_train_imgs': num_train_imgs, 202 | 'num_query_pids': num_test_pids, 'num_query_imgs': num_test_imgs, 203 | 'num_gallery_pids': num_test_pids, 'num_gallery_imgs': num_test_imgs, 204 | }) 205 | 206 | write_json(splits_classic_det, self.split_classic_det_json_path) 207 | write_json(splits_classic_lab, self.split_classic_lab_json_path) 208 | 209 | def _extract_set(filelist, pids, pid2label, idxs, img_dir, relabel): 210 | tmp_set = [] 211 | unique_pids = set() 212 | for idx in idxs: 213 | img_name = filelist[idx][0] 214 | camid = int(img_name.split('_')[2]) - 1 # make it 0-based 215 | pid = pids[idx] 216 | if relabel: pid = pid2label[pid] 217 | img_path = osp.join(img_dir, img_name) 218 | tmp_set.append((img_path, int(pid), camid)) 219 | unique_pids.add(pid) 220 | return tmp_set, len(unique_pids), len(idxs) 221 | 222 | def _extract_new_split(split_dict, img_dir): 223 | train_idxs = split_dict['train_idx'].flatten() - 1 # index-0 224 | pids = split_dict['labels'].flatten() 225 | train_pids = set(pids[train_idxs]) 226 | pid2label = {pid: label for label, pid in enumerate(train_pids)} 227 | query_idxs = split_dict['query_idx'].flatten() - 1 228 | gallery_idxs = split_dict['gallery_idx'].flatten() - 1 229 | filelist = split_dict['filelist'].flatten() 230 | train_info = _extract_set(filelist, pids, pid2label, train_idxs, img_dir, relabel=True) 231 | query_info = _extract_set(filelist, pids, pid2label, query_idxs, img_dir, relabel=False) 232 | gallery_info = _extract_set(filelist, pids, pid2label, gallery_idxs, img_dir, relabel=False) 233 | return train_info, query_info, gallery_info 234 | 235 | print("Creating new splits for detected images (767/700) ...") 236 | train_info, query_info, gallery_info = _extract_new_split( 237 | loadmat(self.split_new_det_mat_path), 238 | self.imgs_detected_dir, 239 | ) 240 | splits = [{ 241 | 'train': train_info[0], 'query': query_info[0], 'gallery': gallery_info[0], 242 | 'num_train_pids': train_info[1], 'num_train_imgs': train_info[2], 243 | 'num_query_pids': query_info[1], 'num_query_imgs': query_info[2], 244 | 'num_gallery_pids': gallery_info[1], 'num_gallery_imgs': gallery_info[2], 245 | }] 246 | write_json(splits, self.split_new_det_json_path) 247 | 248 | print("Creating new splits for labeled images (767/700) ...") 249 | train_info, query_info, gallery_info = _extract_new_split( 250 | loadmat(self.split_new_lab_mat_path), 251 | self.imgs_labeled_dir, 252 | ) 253 | splits = [{ 254 | 'train': train_info[0], 'query': query_info[0], 'gallery': gallery_info[0], 255 | 'num_train_pids': train_info[1], 'num_train_imgs': train_info[2], 256 | 'num_query_pids': query_info[1], 'num_query_imgs': query_info[2], 257 | 'num_gallery_pids': gallery_info[1], 'num_gallery_imgs': gallery_info[2], 258 | }] 259 | write_json(splits, self.split_new_lab_json_path) 260 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/dataset_loader.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os.path as osp 8 | from PIL import Image 9 | from torch.utils.data import Dataset 10 | 11 | 12 | def read_image(img_path): 13 | """Keep reading image until succeed. 14 | This can avoid IOError incurred by heavy IO process.""" 15 | got_img = False 16 | if not osp.exists(img_path): 17 | raise IOError("{} does not exist".format(img_path)) 18 | while not got_img: 19 | try: 20 | img = Image.open(img_path).convert('RGB') 21 | got_img = True 22 | except IOError: 23 | print("IOError incurred when reading '{}'. Will redo. Don't worry. Just chill.".format(img_path)) 24 | pass 25 | return img 26 | 27 | 28 | class ImageDataset(Dataset): 29 | """Image Person ReID Dataset""" 30 | 31 | def __init__(self, dataset, transform=None): 32 | self.dataset = dataset 33 | self.transform = transform 34 | 35 | def __len__(self): 36 | return len(self.dataset) 37 | 38 | def __getitem__(self, index): 39 | img_path, pid, camid = self.dataset[index] 40 | img = read_image(img_path) 41 | 42 | if self.transform is not None: 43 | img = self.transform(img) 44 | 45 | return img, pid, camid, img_path 46 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/dukemtmcreid.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: liaoxingyu2@jd.com 5 | """ 6 | 7 | import glob 8 | import re 9 | import urllib 10 | import zipfile 11 | 12 | import os.path as osp 13 | 14 | from utils.iotools import mkdir_if_missing 15 | from .bases import BaseImageDataset 16 | 17 | 18 | class DukeMTMCreID(BaseImageDataset): 19 | """ 20 | DukeMTMC-reID 21 | Reference: 22 | 1. Ristani et al. Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking. ECCVW 2016. 23 | 2. Zheng et al. Unlabeled Samples Generated by GAN Improve the Person Re-identification Baseline in vitro. ICCV 2017. 24 | URL: https://github.com/layumi/DukeMTMC-reID_evaluation 25 | 26 | Dataset statistics: 27 | # identities: 1404 (train + query) 28 | # images:16522 (train) + 2228 (query) + 17661 (gallery) 29 | # cameras: 8 30 | """ 31 | dataset_dir = 'dukemtmc-reid' 32 | 33 | def __init__(self, root='/export/home/lxy/DATA/reid', verbose=True, **kwargs): 34 | super(DukeMTMCreID, self).__init__() 35 | self.dataset_dir = osp.join(root, self.dataset_dir) 36 | self.dataset_url = 'http://vision.cs.duke.edu/DukeMTMC/data/misc/DukeMTMC-reID.zip' 37 | self.train_dir = osp.join(self.dataset_dir, 'DukeMTMC-reID/bounding_box_train') 38 | self.query_dir = osp.join(self.dataset_dir, 'DukeMTMC-reID/query') 39 | self.gallery_dir = osp.join(self.dataset_dir, 'DukeMTMC-reID/bounding_box_test') 40 | 41 | self._download_data() 42 | self._check_before_run() 43 | 44 | train = self._process_dir(self.train_dir, relabel=True) 45 | query = self._process_dir(self.query_dir, relabel=False) 46 | gallery = self._process_dir(self.gallery_dir, relabel=False) 47 | 48 | if verbose: 49 | print("=> DukeMTMC-reID loaded") 50 | self.print_dataset_statistics(train, query, gallery) 51 | 52 | self.train = train 53 | self.query = query 54 | self.gallery = gallery 55 | 56 | self.num_train_pids, self.num_train_imgs, self.num_train_cams = self.get_imagedata_info(self.train) 57 | self.num_query_pids, self.num_query_imgs, self.num_query_cams = self.get_imagedata_info(self.query) 58 | self.num_gallery_pids, self.num_gallery_imgs, self.num_gallery_cams = self.get_imagedata_info(self.gallery) 59 | 60 | def _download_data(self): 61 | if osp.exists(self.dataset_dir): 62 | print("This dataset has been downloaded.") 63 | return 64 | 65 | print("Creating directory {}".format(self.dataset_dir)) 66 | mkdir_if_missing(self.dataset_dir) 67 | fpath = osp.join(self.dataset_dir, osp.basename(self.dataset_url)) 68 | 69 | print("Downloading DukeMTMC-reID dataset") 70 | urllib.urlretrieve(self.dataset_url, fpath) 71 | 72 | print("Extracting files") 73 | zip_ref = zipfile.ZipFile(fpath, 'r') 74 | zip_ref.extractall(self.dataset_dir) 75 | zip_ref.close() 76 | 77 | def _check_before_run(self): 78 | """Check if all files are available before going deeper""" 79 | if not osp.exists(self.dataset_dir): 80 | raise RuntimeError("'{}' is not available".format(self.dataset_dir)) 81 | if not osp.exists(self.train_dir): 82 | raise RuntimeError("'{}' is not available".format(self.train_dir)) 83 | if not osp.exists(self.query_dir): 84 | raise RuntimeError("'{}' is not available".format(self.query_dir)) 85 | if not osp.exists(self.gallery_dir): 86 | raise RuntimeError("'{}' is not available".format(self.gallery_dir)) 87 | 88 | def _process_dir(self, dir_path, relabel=False): 89 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 90 | pattern = re.compile(r'([-\d]+)_c(\d)') 91 | 92 | pid_container = set() 93 | for img_path in img_paths: 94 | pid, _ = map(int, pattern.search(img_path).groups()) 95 | pid_container.add(pid) 96 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 97 | 98 | dataset = [] 99 | for img_path in img_paths: 100 | pid, camid = map(int, pattern.search(img_path).groups()) 101 | assert 1 <= camid <= 8 102 | camid -= 1 # index starts from 0 103 | if relabel: pid = pid2label[pid] 104 | dataset.append((img_path, pid, camid)) 105 | 106 | return dataset 107 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/eval_reid.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import numpy as np 8 | 9 | 10 | def eval_func(distmat, q_pids, g_pids, q_camids, g_camids, max_rank=50): 11 | """Evaluation with market1501 metric 12 | Key: for each query identity, its gallery images from the same camera view are discarded. 13 | """ 14 | num_q, num_g = distmat.shape 15 | if num_g < max_rank: 16 | max_rank = num_g 17 | print("Note: number of gallery samples is quite small, got {}".format(num_g)) 18 | indices = np.argsort(distmat, axis=1) 19 | matches = (g_pids[indices] == q_pids[:, np.newaxis]).astype(np.int32) 20 | 21 | # compute cmc curve for each query 22 | all_cmc = [] 23 | all_AP = [] 24 | num_valid_q = 0. # number of valid query 25 | for q_idx in range(num_q): 26 | # get query pid and camid 27 | q_pid = q_pids[q_idx] 28 | q_camid = q_camids[q_idx] 29 | 30 | # remove gallery samples that have the same pid and camid with query 31 | order = indices[q_idx] 32 | remove = (g_pids[order] == q_pid) & (g_camids[order] == q_camid) 33 | keep = np.invert(remove) 34 | 35 | # compute cmc curve 36 | # binary vector, positions with value 1 are correct matches 37 | orig_cmc = matches[q_idx][keep] 38 | if not np.any(orig_cmc): 39 | # this condition is true when query identity does not appear in gallery 40 | continue 41 | 42 | cmc = orig_cmc.cumsum() 43 | cmc[cmc > 1] = 1 44 | 45 | all_cmc.append(cmc[:max_rank]) 46 | num_valid_q += 1. 47 | 48 | # compute average precision 49 | # reference: https://en.wikipedia.org/wiki/Evaluation_measures_(information_retrieval)#Average_precision 50 | num_rel = orig_cmc.sum() 51 | tmp_cmc = orig_cmc.cumsum() 52 | tmp_cmc = [x / (i + 1.) for i, x in enumerate(tmp_cmc)] 53 | tmp_cmc = np.asarray(tmp_cmc) * orig_cmc 54 | AP = tmp_cmc.sum() / num_rel 55 | all_AP.append(AP) 56 | 57 | assert num_valid_q > 0, "Error: all query identities do not appear in gallery" 58 | 59 | all_cmc = np.asarray(all_cmc).astype(np.float32) 60 | all_cmc = all_cmc.sum(0) / num_valid_q 61 | mAP = np.mean(all_AP) 62 | 63 | return all_cmc, mAP 64 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/market1501.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import glob 8 | import re 9 | 10 | import os.path as osp 11 | 12 | from .bases import BaseImageDataset 13 | 14 | 15 | class Market1501(BaseImageDataset): 16 | """ 17 | Market1501 18 | Reference: 19 | Zheng et al. Scalable Person Re-identification: A Benchmark. ICCV 2015. 20 | URL: http://www.liangzheng.org/Project/project_reid.html 21 | 22 | Dataset statistics: 23 | # identities: 1501 (+1 for background) 24 | # images: 12936 (train) + 3368 (query) + 15913 (gallery) 25 | """ 26 | dataset_dir = 'market1501' 27 | 28 | def __init__(self, root='/export/home/lxy/DATA/reid', verbose=True, **kwargs): 29 | super(Market1501, self).__init__() 30 | self.dataset_dir = osp.join(root, self.dataset_dir) 31 | self.train_dir = osp.join(self.dataset_dir, 'bounding_box_train') 32 | self.query_dir = osp.join(self.dataset_dir, 'query') 33 | self.gallery_dir = osp.join(self.dataset_dir, 'bounding_box_test') 34 | 35 | self._check_before_run() 36 | 37 | train = self._process_dir(self.train_dir, relabel=True) 38 | query = self._process_dir(self.query_dir, relabel=False) 39 | gallery = self._process_dir(self.gallery_dir, relabel=False) 40 | 41 | if verbose: 42 | print("=> Market1501 loaded") 43 | self.print_dataset_statistics(train, query, gallery) 44 | 45 | self.train = train 46 | self.query = query 47 | self.gallery = gallery 48 | 49 | self.num_train_pids, self.num_train_imgs, self.num_train_cams = self.get_imagedata_info(self.train) 50 | self.num_query_pids, self.num_query_imgs, self.num_query_cams = self.get_imagedata_info(self.query) 51 | self.num_gallery_pids, self.num_gallery_imgs, self.num_gallery_cams = self.get_imagedata_info(self.gallery) 52 | 53 | def _check_before_run(self): 54 | """Check if all files are available before going deeper""" 55 | if not osp.exists(self.dataset_dir): 56 | raise RuntimeError("'{}' is not available".format(self.dataset_dir)) 57 | if not osp.exists(self.train_dir): 58 | raise RuntimeError("'{}' is not available".format(self.train_dir)) 59 | if not osp.exists(self.query_dir): 60 | raise RuntimeError("'{}' is not available".format(self.query_dir)) 61 | if not osp.exists(self.gallery_dir): 62 | raise RuntimeError("'{}' is not available".format(self.gallery_dir)) 63 | 64 | def _process_dir(self, dir_path, relabel=False): 65 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 66 | pattern = re.compile(r'([-\d]+)_c(\d)') 67 | 68 | pid_container = set() 69 | for img_path in img_paths: 70 | pid, _ = map(int, pattern.search(img_path).groups()) 71 | if pid == -1: continue # junk images are just ignored 72 | pid_container.add(pid) 73 | pid2label = {pid: label for label, pid in enumerate(pid_container)} 74 | 75 | dataset = [] 76 | for img_path in img_paths: 77 | pid, camid = map(int, pattern.search(img_path).groups()) 78 | if pid == -1: continue # junk images are just ignored 79 | assert 0 <= pid <= 1501 # pid == 0 means background 80 | assert 1 <= camid <= 6 81 | camid -= 1 # index starts from 0 82 | if relabel: pid = pid2label[pid] 83 | dataset.append((img_path, pid, camid)) 84 | 85 | return dataset 86 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/datasets/ranking.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from collections import defaultdict 3 | import torch 4 | import numpy as np 5 | from sklearn.metrics import average_precision_score 6 | 7 | def to_numpy(tensor): 8 | if torch.is_tensor(tensor): 9 | return tensor.cpu().numpy() 10 | elif type(tensor).__module__ != 'numpy': 11 | raise ValueError("Cannot convert {} to numpy array" 12 | .format(type(tensor))) 13 | return tensor 14 | 15 | def _unique_sample(ids_dict, num): 16 | mask = np.zeros(num, dtype=np.bool) 17 | for _, indices in ids_dict.items(): 18 | i = np.random.choice(indices) 19 | mask[i] = True 20 | return mask 21 | 22 | 23 | def cmc(distmat, query_ids=None, gallery_ids=None, 24 | query_cams=None, gallery_cams=None, topk=100, 25 | separate_camera_set=False, 26 | single_gallery_shot=False, 27 | first_match_break=True): 28 | distmat = to_numpy(distmat) 29 | m, n = distmat.shape 30 | # Fill up default values 31 | if query_ids is None: 32 | query_ids = np.arange(m) 33 | if gallery_ids is None: 34 | gallery_ids = np.arange(n) 35 | if query_cams is None: 36 | query_cams = np.zeros(m).astype(np.int32) 37 | if gallery_cams is None: 38 | gallery_cams = np.ones(n).astype(np.int32) 39 | # Ensure numpy array 40 | query_ids = np.asarray(query_ids) 41 | gallery_ids = np.asarray(gallery_ids) 42 | query_cams = np.asarray(query_cams) 43 | gallery_cams = np.asarray(gallery_cams) 44 | # Sort and find correct matches 45 | indices = np.argsort(distmat, axis=1) 46 | matches = (gallery_ids[indices] == query_ids[:, np.newaxis]) 47 | # Compute CMC for each query 48 | ret = np.zeros(topk) 49 | num_valid_queries = 0 50 | for i in range(m): 51 | # Filter out the same id and same camera 52 | valid = ((gallery_ids[indices[i]] != query_ids[i]) | 53 | (gallery_cams[indices[i]] != query_cams[i])) 54 | if separate_camera_set: 55 | # Filter out samples from same camera 56 | valid &= (gallery_cams[indices[i]] != query_cams[i]) 57 | if not np.any(matches[i, valid]): continue 58 | if single_gallery_shot: 59 | repeat = 10 60 | gids = gallery_ids[indices[i][valid]] 61 | inds = np.where(valid)[0] 62 | ids_dict = defaultdict(list) 63 | for j, x in zip(inds, gids): 64 | ids_dict[x].append(j) 65 | else: 66 | repeat = 1 67 | for _ in range(repeat): 68 | if single_gallery_shot: 69 | # Randomly choose one instance for each id 70 | sampled = (valid & _unique_sample(ids_dict, len(valid))) 71 | index = np.nonzero(matches[i, sampled])[0] 72 | else: 73 | index = np.nonzero(matches[i, valid])[0] 74 | delta = 1. / (len(index) * repeat) 75 | for j, k in enumerate(index): 76 | if k - j >= topk: break 77 | if first_match_break: 78 | ret[k - j] += 1 79 | break 80 | ret[k - j] += delta 81 | num_valid_queries += 1 82 | if num_valid_queries == 0: 83 | raise RuntimeError("No valid query") 84 | return ret.cumsum() / num_valid_queries 85 | 86 | 87 | def mean_ap(distmat, query_ids=None, gallery_ids=None, 88 | query_cams=None, gallery_cams=None): 89 | distmat = to_numpy(distmat) 90 | m, n = distmat.shape 91 | # Fill up default values 92 | if query_ids is None: 93 | query_ids = np.arange(m) 94 | if gallery_ids is None: 95 | gallery_ids = np.arange(n) 96 | if query_cams is None: 97 | query_cams = np.zeros(m).astype(np.int32) 98 | if gallery_cams is None: 99 | gallery_cams = np.ones(n).astype(np.int32) 100 | # Ensure numpy array 101 | query_ids = np.asarray(query_ids) 102 | gallery_ids = np.asarray(gallery_ids) 103 | query_cams = np.asarray(query_cams) 104 | gallery_cams = np.asarray(gallery_cams) 105 | # Sort and find correct matches 106 | indices = np.argsort(distmat, axis=1) 107 | matches = (gallery_ids[indices] == query_ids[:, np.newaxis]) 108 | # Compute AP for each query 109 | aps = [] 110 | for i in range(m): 111 | # Filter out the same id and same camera 112 | valid = ((gallery_ids[indices[i]] != query_ids[i]) | 113 | (gallery_cams[indices[i]] != query_cams[i])) 114 | y_true = matches[i, valid] 115 | y_score = -distmat[i][indices[i]][valid] 116 | if not np.any(y_true): continue 117 | aps.append(average_precision_score(y_true, y_score)) 118 | if len(aps) == 0: 119 | raise RuntimeError("No valid query") 120 | return np.mean(aps) 121 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/samplers/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .triplet_sampler import RandomIdentitySampler 8 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/samplers/triplet_sampler.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: liaoxingyu2@jd.com 5 | """ 6 | 7 | import copy 8 | import random 9 | from collections import defaultdict 10 | 11 | import numpy as np 12 | from torch.utils.data.sampler import Sampler 13 | 14 | 15 | class RandomIdentitySampler(Sampler): 16 | """ 17 | Randomly sample N identities, then for each identity, 18 | randomly sample K instances, therefore batch size is N*K. 19 | Args: 20 | - data_source (list): list of (img_path, pid, camid). 21 | - num_instances (int): number of instances per identity in a batch. 22 | - batch_size (int): number of examples in a batch. 23 | """ 24 | 25 | def __init__(self, data_source, batch_size, num_instances): 26 | self.data_source = data_source 27 | self.batch_size = batch_size 28 | self.num_instances = num_instances 29 | self.num_pids_per_batch = self.batch_size // self.num_instances 30 | self.index_dic = defaultdict(list) 31 | for index, (_, pid, _) in enumerate(self.data_source): 32 | self.index_dic[pid].append(index) 33 | self.pids = list(self.index_dic.keys()) 34 | 35 | # estimate number of examples in an epoch 36 | self.length = 0 37 | for pid in self.pids: 38 | idxs = self.index_dic[pid] 39 | num = len(idxs) 40 | if num < self.num_instances: 41 | num = self.num_instances 42 | self.length += num - num % self.num_instances 43 | 44 | def __iter__(self): 45 | batch_idxs_dict = defaultdict(list) 46 | 47 | for pid in self.pids: 48 | idxs = copy.deepcopy(self.index_dic[pid]) 49 | if len(idxs) < self.num_instances: 50 | idxs = np.random.choice(idxs, size=self.num_instances, replace=True) 51 | random.shuffle(idxs) 52 | batch_idxs = [] 53 | for idx in idxs: 54 | batch_idxs.append(idx) 55 | if len(batch_idxs) == self.num_instances: 56 | batch_idxs_dict[pid].append(batch_idxs) 57 | batch_idxs = [] 58 | 59 | avai_pids = copy.deepcopy(self.pids) 60 | final_idxs = [] 61 | 62 | while len(avai_pids) >= self.num_pids_per_batch: 63 | selected_pids = random.sample(avai_pids, self.num_pids_per_batch) 64 | for pid in selected_pids: 65 | batch_idxs = batch_idxs_dict[pid].pop(0) 66 | final_idxs.extend(batch_idxs) 67 | if len(batch_idxs_dict[pid]) == 0: 68 | avai_pids.remove(pid) 69 | 70 | return iter(final_idxs) 71 | 72 | def __len__(self): 73 | return self.length 74 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/transforms/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import build_transforms 8 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/transforms/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: weijian 4 | @contact: dengwj16@gmail.com 5 | """ 6 | 7 | import torchvision.transforms as T 8 | 9 | import torch 10 | 11 | def build_transforms(cfg, is_train=True): 12 | normalize_transform = T.Normalize(mean=cfg.INPUT.PIXEL_MEAN, std=cfg.INPUT.PIXEL_STD) 13 | if is_train: 14 | transform = T.Compose([ 15 | T.Resize(cfg.INPUT.SIZE_TRAIN), 16 | T.RandomHorizontalFlip(p=cfg.INPUT.PROB), 17 | T.Pad(cfg.INPUT.PADDING), # pad=10 18 | T.RandomCrop([256, 256]), 19 | T.ToTensor(), 20 | normalize_transform 21 | ]) 22 | 23 | if is_train and cfg.INPUT.COLORJITTER: 24 | transform = T.Compose([ 25 | T.Resize(cfg.INPUT.SIZE_TRAIN), 26 | T.RandomHorizontalFlip(p=cfg.INPUT.PROB), 27 | T.Pad(cfg.INPUT.PADDING), # pad=10 28 | T.RandomCrop([256, 256]), 29 | T.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0),# light 30 | T.ToTensor(), 31 | normalize_transform 32 | ]) 33 | else: 34 | #cfg.INPUT.SIZE_TEST 35 | transform = T.Compose([ 36 | T.Resize(cfg.INPUT.SIZE_TEST), 37 | T.ToTensor(), 38 | normalize_transform 39 | ]) 40 | 41 | return transform 42 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/data/transforms/transforms.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: liaoxingyu2@jd.com 5 | """ 6 | 7 | import math 8 | import random 9 | 10 | 11 | class RandomErasing(object): 12 | """ Randomly selects a rectangle region in an image and erases its pixels. 13 | 'Random Erasing Data Augmentation' by Zhong et al. 14 | See https://arxiv.org/pdf/1708.04896.pdf 15 | Args: 16 | probability: The probability that the Random Erasing operation will be performed. 17 | sl: Minimum proportion of erased area against input image. 18 | sh: Maximum proportion of erased area against input image. 19 | r1: Minimum aspect ratio of erased area. 20 | mean: Erasing value. 21 | """ 22 | 23 | def __init__(self, probability=0.5, sl=0.02, sh=0.4, r1=0.3, mean=(0.4914, 0.4822, 0.4465)): 24 | self.probability = probability 25 | self.mean = mean 26 | self.sl = sl 27 | self.sh = sh 28 | self.r1 = r1 29 | 30 | def __call__(self, img): 31 | 32 | if random.uniform(0, 1) > self.probability: 33 | return img 34 | 35 | for attempt in range(100): 36 | area = img.size()[1] * img.size()[2] 37 | 38 | target_area = random.uniform(self.sl, self.sh) * area 39 | aspect_ratio = random.uniform(self.r1, 1 / self.r1) 40 | 41 | h = int(round(math.sqrt(target_area * aspect_ratio))) 42 | w = int(round(math.sqrt(target_area / aspect_ratio))) 43 | 44 | if w < img.size()[2] and h < img.size()[1]: 45 | x1 = random.randint(0, img.size()[1] - h) 46 | y1 = random.randint(0, img.size()[2] - w) 47 | if img.size()[0] == 3: 48 | img[0, x1:x1 + h, y1:y1 + w] = self.mean[0] 49 | img[1, x1:x1 + h, y1:y1 + w] = self.mean[1] 50 | img[2, x1:x1 + h, y1:y1 + w] = self.mean[2] 51 | else: 52 | img[0, x1:x1 + h, y1:y1 + w] = self.mean[0] 53 | return img 54 | 55 | return img 56 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/__init__.py -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/__init__.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/__pycache__/inference.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/__pycache__/inference.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/__pycache__/trainer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/__pycache__/trainer.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/inference.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | import logging 7 | 8 | import torch 9 | from ignite.engine import Engine 10 | 11 | from utils.reid_metric import R1_mAP 12 | import scipy.io 13 | 14 | def create_supervised_evaluator(model, metrics, 15 | device=None): 16 | """ 17 | Factory function for creating an evaluator for supervised models 18 | 19 | Args: 20 | model (`torch.nn.Module`): the model to train 21 | metrics (dict of str - :class:`ignite.metrics.Metric`): a map of metric names to Metrics 22 | device (str, optional): device type specification (default: None). 23 | Applies to both model and batches. 24 | Returns: 25 | Engine: an evaluator engine with supervised inference function 26 | """ 27 | if device: 28 | model.to(device) 29 | 30 | def _inference(engine, batch): 31 | model.eval() 32 | with torch.no_grad(): 33 | data, pids, camids = batch 34 | data = data.cuda() 35 | feat = model(data) 36 | return feat, pids, camids 37 | 38 | engine = Engine(_inference) 39 | 40 | for name, metric in metrics.items(): 41 | metric.attach(engine, name) 42 | 43 | return engine 44 | 45 | 46 | def inference( 47 | cfg, 48 | model, 49 | val_loader, 50 | num_query 51 | ): 52 | device = cfg.MODEL.DEVICE 53 | 54 | logger = logging.getLogger("reid_baseline.inference") 55 | logger.info("Start inferencing") 56 | evaluator = create_supervised_evaluator(model, metrics={'r1_mAP': R1_mAP(num_query)}, 57 | device=device) 58 | 59 | evaluator.run(val_loader) 60 | cmc, mAP = evaluator.state.metrics['r1_mAP'] 61 | logger.info('Validation Results') 62 | logger.info("mAP: {:.1%}".format(mAP)) 63 | for r in [1, 5, 10]: 64 | logger.info("CMC curve, Rank-{:<3}:{:.1%}".format(r, cmc[r - 1])) 65 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/inference.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/inference.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/trainer.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import logging 8 | 9 | import torch 10 | import torch.nn as nn 11 | from ignite.engine import Engine, Events 12 | from ignite.handlers import ModelCheckpoint, Timer 13 | from ignite.metrics import RunningAverage 14 | 15 | from utils.reid_metric import R1_mAP 16 | 17 | 18 | def create_supervised_trainer(model, optimizer, loss_fn, 19 | device=None): 20 | """ 21 | Factory function for creating a trainer for supervised models 22 | 23 | Args: 24 | model (`torch.nn.Module`): the model to train 25 | optimizer (`torch.optim.Optimizer`): the optimizer to use 26 | loss_fn (torch.nn loss function): the loss function to use 27 | device (str, optional): device type specification (default: None). 28 | Applies to both model and batches. 29 | 30 | Returns: 31 | Engine: a trainer engine with supervised update function 32 | """ 33 | if device: 34 | model.to(device) 35 | 36 | def _update(engine, batch): 37 | model.train() 38 | ''' 39 | for m in model.base.modules(): 40 | if isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): 41 | m.eval() 42 | m.weight.requires_grad = False 43 | m.bias.requires_grad = False 44 | ''' 45 | optimizer.zero_grad() 46 | img, target = batch 47 | img = img.cuda() 48 | target = target.cuda() 49 | score, feat = model(img) # fuse batch size and ncrops 50 | loss = loss_fn(score, feat, target) 51 | loss.backward() 52 | optimizer.step() 53 | # compute acc 54 | acc = (score.max(1)[1] == target).float().mean() 55 | return loss.item(), acc.item() 56 | 57 | return Engine(_update) 58 | 59 | 60 | def create_supervised_evaluator(model, metrics, 61 | device=None): 62 | """ 63 | Factory function for creating an evaluator for supervised models 64 | 65 | Args: 66 | model (`torch.nn.Module`): the model to train 67 | metrics (dict of str - :class:`ignite.metrics.Metric`): a map of metric names to Metrics 68 | device (str, optional): device type specification (default: None). 69 | Applies to both model and batches. 70 | Returns: 71 | Engine: an evaluator engine with supervised inference function 72 | """ 73 | if device: 74 | model.to(device) 75 | 76 | def _inference(engine, batch): 77 | model.eval() 78 | with torch.no_grad(): 79 | data, pids, camids = batch 80 | data = data.cuda() 81 | feat = model(data) 82 | return feat, pids, camids 83 | 84 | engine = Engine(_inference) 85 | 86 | for name, metric in metrics.items(): 87 | metric.attach(engine, name) 88 | 89 | return engine 90 | 91 | 92 | def do_train( 93 | cfg, 94 | model, 95 | train_loader, 96 | val_loader, 97 | optimizer, 98 | scheduler, 99 | loss_fn, 100 | num_query 101 | ): 102 | log_period = cfg.SOLVER.LOG_PERIOD 103 | checkpoint_period = cfg.SOLVER.CHECKPOINT_PERIOD 104 | eval_period = cfg.SOLVER.EVAL_PERIOD 105 | output_dir = cfg.OUTPUT_DIR 106 | device = cfg.MODEL.DEVICE 107 | epochs = cfg.SOLVER.MAX_EPOCHS 108 | 109 | logger = logging.getLogger("reid_baseline.train") 110 | logger.info("Start training") 111 | trainer = create_supervised_trainer(model, optimizer, loss_fn, device=device) 112 | evaluator = create_supervised_evaluator(model, metrics={'r1_mAP': R1_mAP(num_query)}, device=device) 113 | checkpointer = ModelCheckpoint(output_dir, cfg.MODEL.NAME, checkpoint_period, n_saved=10, require_empty=False) 114 | timer = Timer(average=True) 115 | 116 | trainer.add_event_handler(Events.EPOCH_COMPLETED, checkpointer, {'model': model.state_dict(), 117 | 'optimizer': optimizer.state_dict()}) 118 | timer.attach(trainer, start=Events.EPOCH_STARTED, resume=Events.ITERATION_STARTED, 119 | pause=Events.ITERATION_COMPLETED, step=Events.ITERATION_COMPLETED) 120 | 121 | # average metric to attach on trainer 122 | RunningAverage(output_transform=lambda x: x[0]).attach(trainer, 'avg_loss') 123 | RunningAverage(output_transform=lambda x: x[1]).attach(trainer, 'avg_acc') 124 | 125 | @trainer.on(Events.EPOCH_STARTED) 126 | def adjust_learning_rate(engine): 127 | scheduler.step() 128 | 129 | @trainer.on(Events.ITERATION_COMPLETED) 130 | def log_training_loss(engine): 131 | iter = (engine.state.iteration - 1) % len(train_loader) + 1 132 | 133 | if iter % log_period == 0: 134 | logger.info("Epoch[{}] Iteration[{}/{}] Loss: {:.3f}, Acc: {:.3f}, Base Lr: {:.2e}" 135 | .format(engine.state.epoch, iter, len(train_loader), 136 | engine.state.metrics['avg_loss'], engine.state.metrics['avg_acc'], 137 | scheduler.get_lr()[0])) 138 | 139 | # adding handlers using `trainer.on` decorator API 140 | @trainer.on(Events.EPOCH_COMPLETED) 141 | def print_times(engine): 142 | logger.info('Epoch {} done. Time per batch: {:.3f}[s] Speed: {:.1f}[samples/s]' 143 | .format(engine.state.epoch, timer.value() * timer.step_count, 144 | train_loader.batch_size / timer.value())) 145 | logger.info('-' * 10) 146 | timer.reset() 147 | 148 | @trainer.on(Events.EPOCH_COMPLETED) 149 | def log_validation_results(engine): 150 | if engine.state.epoch % eval_period == 0: 151 | evaluator.run(val_loader) 152 | cmc, mAP = evaluator.state.metrics['r1_mAP'] 153 | logger.info("Validation Results - Epoch: {}".format(engine.state.epoch)) 154 | logger.info("mAP: {:.1%}".format(mAP)) 155 | for r in [1, 5, 10]: 156 | logger.info("CMC curve, Rank-{:<3}:{:.1%}".format(r, cmc[r - 1])) 157 | 158 | trainer.run(train_loader, max_epochs=epochs) 159 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/engine/trainer.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/engine/trainer.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/GHMC_Loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | 4 | 5 | class GHMC_Loss: 6 | def __init__(self, bins=10, momentum=0): 7 | self.bins = bins 8 | self.momentum = momentum 9 | self.edges = [float(x) / bins for x in range(bins+1)] 10 | self.edges[-1] += 1e-6 11 | if momentum > 0: 12 | self.acc_sum = [0.0 for _ in range(bins)] 13 | 14 | def calc(self, input, target): 15 | """ Args: 16 | input [batch_num, class_num]: 17 | The direct prediction of classification fc layer. 18 | target [batch_num, class_num]: 19 | Binary target (0 or 1) for each sample each class. The value is -1 20 | when the sample is ignored. 21 | """ 22 | edges = self.edges 23 | mmt = self.momentum 24 | weights = torch.zeros_like(input) 25 | 26 | temp = torch.zeros(input.size(0), input.size(1)) 27 | target_onehot = temp.scatter_(1, target.cpu().view(-1, 1), 1).cuda() 28 | # gradient length 29 | g = torch.abs(input.sigmoid().detach() - target_onehot) 30 | 31 | #valid = mask > 0 32 | #tot = max(valid.float().sum().item(), 1.0) 33 | tot = input.size(0) 34 | n = 0 # n valid bins 35 | for i in range(self.bins): 36 | inds = (g >= edges[i]) & (g < edges[i+1]) #& valid 37 | num_in_bin = inds.sum().item() 38 | if num_in_bin > 0: 39 | if mmt > 0: 40 | self.acc_sum[i] = mmt * self.acc_sum[i] \ 41 | + (1 - mmt) * num_in_bin 42 | weights[inds] = tot / self.acc_sum[i] 43 | else: 44 | weights[inds] = tot / num_in_bin 45 | n += 1 46 | if n > 0: 47 | weights = weights / n 48 | 49 | loss = F.binary_cross_entropy_with_logits( 50 | input, target_onehot, weights, reduction='sum') / tot 51 | return loss -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/LabelSmoothing.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | import torch.nn as nn 4 | 5 | 6 | class LSR(nn.Module): 7 | 8 | def __init__(self, e=0.1, reduction='mean'): 9 | super().__init__() 10 | 11 | self.log_softmax = nn.LogSoftmax(dim=1) 12 | self.e = e 13 | self.reduction = reduction 14 | 15 | def _one_hot(self, labels, classes, value=1): 16 | """ 17 | Convert labels to one hot vectors 18 | 19 | Args: 20 | labels: torch tensor in format [label1, label2, label3, ...] 21 | classes: int, number of classes 22 | value: label value in one hot vector, default to 1 23 | 24 | Returns: 25 | return one hot format labels in shape [batchsize, classes] 26 | """ 27 | 28 | one_hot = torch.zeros(labels.size(0), classes) 29 | 30 | #labels and value_added size must match 31 | labels = labels.view(labels.size(0), -1) 32 | value_added = torch.Tensor(labels.size(0), 1).fill_(value) 33 | 34 | value_added = value_added.to(labels.device) 35 | one_hot = one_hot.to(labels.device) 36 | 37 | one_hot.scatter_add_(1, labels, value_added) 38 | 39 | return one_hot 40 | 41 | def _smooth_label(self, target, length, smooth_factor): 42 | """convert targets to one-hot format, and smooth 43 | them. 44 | 45 | Args: 46 | target: target in form with [label1, label2, label_batchsize] 47 | length: length of one-hot format(number of classes) 48 | smooth_factor: smooth factor for label smooth 49 | 50 | Returns: 51 | smoothed labels in one hot format 52 | """ 53 | one_hot = self._one_hot(target, length, value=1 - smooth_factor) 54 | one_hot += smooth_factor / length 55 | 56 | return one_hot.to(target.device) 57 | 58 | def forward(self, x, target): 59 | 60 | if x.size(0) != target.size(0): 61 | raise ValueError('Expected input batchsize ({}) to match target batch_size({})' 62 | .format(x.size(0), target.size(0))) 63 | 64 | if x.dim() < 2: 65 | raise ValueError('Expected input tensor to have least 2 dimensions(got {})' 66 | .format(x.size(0))) 67 | 68 | if x.dim() != 2: 69 | raise ValueError('Only 2 dimension tensor are implemented, (got {})' 70 | .format(x.size())) 71 | 72 | 73 | smoothed_target = self._smooth_label(target, x.size(1), self.e) 74 | x = self.log_softmax(x) 75 | loss = torch.sum(- x * smoothed_target, dim=1) 76 | 77 | if self.reduction == 'none': 78 | return loss 79 | 80 | elif self.reduction == 'sum': 81 | return torch.sum(loss) 82 | 83 | elif self.reduction == 'mean': 84 | return torch.mean(loss) 85 | 86 | else: 87 | raise ValueError('unrecognized option, expect reduction to be one of none, mean, sum') 88 | 89 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch.nn.functional as F 8 | 9 | from .triplet_loss import TripletLoss 10 | from .LabelSmoothing import LSR 11 | from .GHMC_Loss import GHMC_Loss 12 | 13 | def make_loss(cfg): 14 | sampler = cfg.DATALOADER.SAMPLER 15 | if cfg.DATALOADER.SOFT_MARGIN: 16 | triplet = TripletLoss() 17 | else: 18 | triplet = TripletLoss(cfg.SOLVER.MARGIN) 19 | lsr_loss = LSR() 20 | G_Loss = GHMC_Loss() 21 | if sampler == 'softmax': 22 | def loss_func(score, feat, target): 23 | return F.cross_entropy(score, target) 24 | elif cfg.DATALOADER.SAMPLER == 'triplet': 25 | def loss_func(score, feat, target): 26 | return triplet(feat, target)[0] 27 | elif cfg.DATALOADER.SAMPLER == 'softmax_triplet': 28 | def loss_func(score, feat, target): 29 | return lsr_loss(score, target) + triplet(feat, target)[0] 30 | else: 31 | print('expected sampler should be softmax, triplet or softmax_triplet, ' 32 | 'but got {}'.format(cfg.DATALOADER.SAMPLER)) 33 | return loss_func 34 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/__pycache__/GHMC_Loss.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/layers/__pycache__/GHMC_Loss.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/__pycache__/LabelSmoothing.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/layers/__pycache__/LabelSmoothing.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/layers/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/__pycache__/triplet_loss.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/layers/__pycache__/triplet_loss.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/layers/triplet_loss.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | import torch 7 | from torch import nn 8 | 9 | 10 | def normalize(x, axis=-1): 11 | """Normalizing to unit length along the specified dimension. 12 | Args: 13 | x: pytorch Variable 14 | Returns: 15 | x: pytorch Variable, same shape as input 16 | """ 17 | x = 1. * x / (torch.norm(x, 2, axis, keepdim=True).expand_as(x) + 1e-12) 18 | return x 19 | 20 | 21 | def euclidean_dist(x, y): 22 | """ 23 | Args: 24 | x: pytorch Variable, with shape [m, d] 25 | y: pytorch Variable, with shape [n, d] 26 | Returns: 27 | dist: pytorch Variable, with shape [m, n] 28 | """ 29 | m, n = x.size(0), y.size(0) 30 | xx = torch.pow(x, 2).sum(1, keepdim=True).expand(m, n) 31 | yy = torch.pow(y, 2).sum(1, keepdim=True).expand(n, m).t() 32 | dist = xx + yy 33 | dist.addmm_(1, -2, x, y.t()) 34 | dist = dist.clamp(min=1e-12).sqrt() # for numerical stability 35 | return dist 36 | 37 | 38 | def hard_example_mining(dist_mat, labels, return_inds=False): 39 | """For each anchor, find the hardest positive and negative sample. 40 | Args: 41 | dist_mat: pytorch Variable, pair wise distance between samples, shape [N, N] 42 | labels: pytorch LongTensor, with shape [N] 43 | return_inds: whether to return the indices. Save time if `False`(?) 44 | Returns: 45 | dist_ap: pytorch Variable, distance(anchor, positive); shape [N] 46 | dist_an: pytorch Variable, distance(anchor, negative); shape [N] 47 | p_inds: pytorch LongTensor, with shape [N]; 48 | indices of selected hard positive samples; 0 <= p_inds[i] <= N - 1 49 | n_inds: pytorch LongTensor, with shape [N]; 50 | indices of selected hard negative samples; 0 <= n_inds[i] <= N - 1 51 | NOTE: Only consider the case in which all labels have same num of samples, 52 | thus we can cope with all anchors in parallel. 53 | """ 54 | 55 | assert len(dist_mat.size()) == 2 56 | assert dist_mat.size(0) == dist_mat.size(1) 57 | N = dist_mat.size(0) 58 | 59 | # shape [N, N] 60 | is_pos = labels.expand(N, N).eq(labels.expand(N, N).t()) 61 | is_neg = labels.expand(N, N).ne(labels.expand(N, N).t()) 62 | 63 | # `dist_ap` means distance(anchor, positive) 64 | # both `dist_ap` and `relative_p_inds` with shape [N, 1] 65 | dist_ap, relative_p_inds = torch.max( 66 | dist_mat[is_pos].contiguous().view(N, -1), 1, keepdim=True) 67 | # `dist_an` means distance(anchor, negative) 68 | # both `dist_an` and `relative_n_inds` with shape [N, 1] 69 | dist_an, relative_n_inds = torch.min( 70 | dist_mat[is_neg].contiguous().view(N, -1), 1, keepdim=True) 71 | # shape [N] 72 | dist_ap = dist_ap.squeeze(1) 73 | dist_an = dist_an.squeeze(1) 74 | 75 | if return_inds: 76 | # shape [N, N] 77 | ind = (labels.new().resize_as_(labels) 78 | .copy_(torch.arange(0, N).long()) 79 | .unsqueeze(0).expand(N, N)) 80 | # shape [N, 1] 81 | p_inds = torch.gather( 82 | ind[is_pos].contiguous().view(N, -1), 1, relative_p_inds.data) 83 | n_inds = torch.gather( 84 | ind[is_neg].contiguous().view(N, -1), 1, relative_n_inds.data) 85 | # shape [N] 86 | p_inds = p_inds.squeeze(1) 87 | n_inds = n_inds.squeeze(1) 88 | return dist_ap, dist_an, p_inds, n_inds 89 | 90 | return dist_ap, dist_an 91 | 92 | 93 | class TripletLoss(object): 94 | """Modified from Tong Xiao's open-reid (https://github.com/Cysu/open-reid). 95 | Related Triplet Loss theory can be found in paper 'In Defense of the Triplet 96 | Loss for Person Re-Identification'.""" 97 | 98 | def __init__(self, margin=None): 99 | self.margin = margin 100 | if margin is not None: 101 | self.ranking_loss = nn.MarginRankingLoss(margin=margin) 102 | else: 103 | self.ranking_loss = nn.SoftMarginLoss() 104 | 105 | def __call__(self, global_feat, labels, normalize_feature=False): 106 | if normalize_feature: 107 | global_feat = normalize(global_feat, axis=-1) 108 | dist_mat = euclidean_dist(global_feat, global_feat) 109 | dist_ap, dist_an = hard_example_mining( 110 | dist_mat, labels) 111 | y = dist_an.new().resize_as_(dist_an).fill_(1) 112 | if self.margin is not None: 113 | loss = self.ranking_loss(dist_an, dist_ap, y) 114 | else: 115 | loss = self.ranking_loss(dist_an - dist_ap, y) 116 | return loss, dist_ap, dist_an 117 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/modeling/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .baseline_de import Baseline_de 8 | 9 | def build_model(cfg, num_classes): 10 | 11 | if cfg.MODEL.NAME == 'densenet121': 12 | model = Baseline_de(num_classes, cfg.MODEL.LAST_STRIDE, cfg.MODEL.PRETRAIN_PATH) 13 | 14 | return model 15 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/modeling/backbones/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/modeling/backbones/__init__.py -------------------------------------------------------------------------------- /Feature_Learning/learning_model/modeling/backbones/torchvision_models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function, division, absolute_import 3 | import torchvision.models as models 4 | import torch.utils.model_zoo as model_zoo 5 | import torch.nn.functional as F 6 | import types 7 | import re 8 | import torch 9 | ################################################################# 10 | # You can find the definitions of those models here: 11 | # https://github.com/pytorch/vision/blob/master/torchvision/models 12 | # 13 | # To fit the API, we usually added/redefined some methods and 14 | # renamed some attributs (see below for each models). 15 | # 16 | # However, you usually do not need to see the original model 17 | # definition from torchvision. Just use `print(model)` to see 18 | # the modules and see bellow the `model.features` and 19 | # `model.classifier` definitions. 20 | ################################################################# 21 | 22 | __all__ = [ 23 | 'alexnet', 24 | 'densenet121', 'densenet169', 'densenet201', 'densenet161', 25 | 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152', 26 | 'inceptionv3', 27 | 'squeezenet1_0', 'squeezenet1_1', 28 | 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 29 | 'vgg19_bn', 'vgg19' 30 | ] 31 | 32 | model_urls = { 33 | 'alexnet': 'https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth', 34 | 'densenet121': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet121-fbdb23505.pth', 35 | 'densenet169': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet169-f470b90a4.pth', 36 | 'densenet201': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet201-5750cbb1e.pth', 37 | 'densenet161': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet161-347e6b360.pth', 38 | 'inceptionv3': 'https://download.pytorch.org/models/inception_v3_google-1a9a5a14.pth', 39 | 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 40 | 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', 41 | 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', 42 | 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', 43 | 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', 44 | 'squeezenet1_0': 'https://download.pytorch.org/models/squeezenet1_0-a815701f.pth', 45 | 'squeezenet1_1': 'https://download.pytorch.org/models/squeezenet1_1-f364aa15.pth', 46 | 'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth', 47 | 'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth', 48 | 'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth', 49 | 'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth', 50 | 'vgg11_bn': 'https://download.pytorch.org/models/vgg11_bn-6002323d.pth', 51 | 'vgg13_bn': 'https://download.pytorch.org/models/vgg13_bn-abd245e5.pth', 52 | 'vgg16_bn': 'https://download.pytorch.org/models/vgg16_bn-6c64b313.pth', 53 | 'vgg19_bn': 'https://download.pytorch.org/models/vgg19_bn-c79401a0.pth', 54 | # 'vgg16_caffe': 'https://s3-us-west-2.amazonaws.com/jcjohns-models/vgg16-00b39a1b.pth', 55 | # 'vgg19_caffe': 'https://s3-us-west-2.amazonaws.com/jcjohns-models/vgg19-d01eb7cb.pth' 56 | } 57 | 58 | input_sizes = {} 59 | means = {} 60 | stds = {} 61 | 62 | for model_name in __all__: 63 | input_sizes[model_name] = [3, 224, 224] 64 | means[model_name] = [0.485, 0.456, 0.406] 65 | stds[model_name] = [0.229, 0.224, 0.225] 66 | 67 | for model_name in ['inceptionv3']: 68 | input_sizes[model_name] = [3, 299, 299] 69 | means[model_name] = [0.5, 0.5, 0.5] 70 | stds[model_name] = [0.5, 0.5, 0.5] 71 | 72 | pretrained_settings = {} 73 | 74 | for model_name in __all__: 75 | pretrained_settings[model_name] = { 76 | 'imagenet': { 77 | 'url': model_urls[model_name], 78 | 'input_space': 'RGB', 79 | 'input_size': input_sizes[model_name], 80 | 'input_range': [0, 1], 81 | 'mean': means[model_name], 82 | 'std': stds[model_name], 83 | 'num_classes': 1000 84 | } 85 | } 86 | 87 | # for model_name in ['vgg16', 'vgg19']: 88 | # pretrained_settings[model_name]['imagenet_caffe'] = { 89 | # 'url': model_urls[model_name + '_caffe'], 90 | # 'input_space': 'BGR', 91 | # 'input_size': input_sizes[model_name], 92 | # 'input_range': [0, 255], 93 | # 'mean': [103.939, 116.779, 123.68], 94 | # 'std': [1., 1., 1.], 95 | # 'num_classes': 1000 96 | # } 97 | 98 | def update_state_dict(state_dict): 99 | # '.'s are no longer allowed in module names, but pervious _DenseLayer 100 | # has keys 'norm.1', 'relu.1', 'conv.1', 'norm.2', 'relu.2', 'conv.2'. 101 | # They are also in the checkpoints in model_urls. This pattern is used 102 | # to find such keys. 103 | pattern = re.compile( 104 | r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$') 105 | for key in list(state_dict.keys()): 106 | res = pattern.match(key) 107 | if res: 108 | new_key = res.group(1) + res.group(2) 109 | state_dict[new_key] = state_dict[key] 110 | del state_dict[key] 111 | return state_dict 112 | 113 | def load_pretrained(model, num_classes, settings): 114 | assert num_classes == settings['num_classes'], \ 115 | "num_classes should be {}, but is {}".format(settings['num_classes'], num_classes) 116 | state_dict = model_zoo.load_url(settings['url']) 117 | state_dict = update_state_dict(state_dict) 118 | model.load_state_dict(state_dict) 119 | model.input_space = settings['input_space'] 120 | model.input_size = settings['input_size'] 121 | model.input_range = settings['input_range'] 122 | model.mean = settings['mean'] 123 | model.std = settings['std'] 124 | return model 125 | 126 | ################################################################# 127 | # AlexNet 128 | 129 | def modify_alexnet(model): 130 | # Modify attributs 131 | model._features = model.features 132 | del model.features 133 | model.dropout0 = model.classifier[0] 134 | model.linear0 = model.classifier[1] 135 | model.relu0 = model.classifier[2] 136 | model.dropout1 = model.classifier[3] 137 | model.linear1 = model.classifier[4] 138 | model.relu1 = model.classifier[5] 139 | model.last_linear = model.classifier[6] 140 | del model.classifier 141 | 142 | def features(self, input): 143 | x = self._features(input) 144 | x = x.view(x.size(0), 256 * 6 * 6) 145 | x = self.dropout0(x) 146 | x = self.linear0(x) 147 | x = self.relu0(x) 148 | x = self.dropout1(x) 149 | x = self.linear1(x) 150 | return x 151 | 152 | def logits(self, features): 153 | x = self.relu1(features) 154 | x = self.last_linear(x) 155 | return x 156 | 157 | def forward(self, input): 158 | x = self.features(input) 159 | x = self.logits(x) 160 | return x 161 | 162 | # Modify methods 163 | model.features = types.MethodType(features, model) 164 | model.logits = types.MethodType(logits, model) 165 | model.forward = types.MethodType(forward, model) 166 | return model 167 | 168 | def alexnet(num_classes=1000, pretrained='imagenet'): 169 | r"""AlexNet model architecture from the 170 | `"One weird trick..." `_ paper. 171 | """ 172 | # https://github.com/pytorch/vision/blob/master/torchvision/models/alexnet.py 173 | model = models.alexnet(pretrained=False) 174 | if pretrained is not None: 175 | settings = pretrained_settings['alexnet'][pretrained] 176 | model = load_pretrained(model, num_classes, settings) 177 | model = modify_alexnet(model) 178 | return model 179 | 180 | ############################################################### 181 | #DenseNets 182 | 183 | def modify_densenets(model): 184 | # Modify attributs 185 | model.last_linear = model.classifier 186 | del model.classifier 187 | 188 | def logits(self, features): 189 | x = F.relu(features, inplace=True) 190 | x = F.avg_pool2d(x, kernel_size=7, stride=1) 191 | x = x.view(x.size(0), -1) 192 | x = self.last_linear(x) 193 | return x 194 | 195 | def forward(self, input): 196 | x = self.features(input) 197 | #x = self.logits(x) 198 | return x 199 | # Modify methods 200 | model.logits = types.MethodType(logits, model) 201 | model.forward = types.MethodType(forward, model) 202 | return model 203 | 204 | def densenet121(num_classes=1000, pretrained='imagenet'): 205 | r"""Densenet-121 model from 206 | `"Densely Connected Convolutional Networks" ` 207 | """ 208 | model = models.densenet121(pretrained=True) 209 | 210 | if pretrained is not None: 211 | settings = pretrained_settings['densenet121'][pretrained] 212 | model = load_pretrained(model, num_classes, settings) 213 | 214 | model = modify_densenets(model) 215 | return model 216 | 217 | 218 | 219 | 220 | def densenet169(num_classes=1000, pretrained='imagenet'): 221 | r"""Densenet-169 model from 222 | `"Densely Connected Convolutional Networks" ` 223 | """ 224 | model = models.densenet169(pretrained=False) 225 | if pretrained is not None: 226 | settings = pretrained_settings['densenet169'][pretrained] 227 | model = load_pretrained(model, num_classes, settings) 228 | model = modify_densenets(model) 229 | return model 230 | 231 | def densenet201(num_classes=1000, pretrained='imagenet'): 232 | r"""Densenet-201 model from 233 | `"Densely Connected Convolutional Networks" ` 234 | """ 235 | model = models.densenet201(pretrained=False) 236 | if pretrained is not None: 237 | settings = pretrained_settings['densenet201'][pretrained] 238 | model = load_pretrained(model, num_classes, settings) 239 | model = modify_densenets(model) 240 | return model 241 | 242 | def densenet161(num_classes=1000, pretrained='imagenet'): 243 | r"""Densenet-161 model from 244 | `"Densely Connected Convolutional Networks" ` 245 | """ 246 | model = models.densenet161(pretrained=False) 247 | if pretrained is not None: 248 | settings = pretrained_settings['densenet161'][pretrained] 249 | model = load_pretrained(model, num_classes, settings) 250 | model = modify_densenets(model) 251 | return model 252 | 253 | ############################################################### 254 | # InceptionV3 255 | 256 | def inceptionv3(num_classes=1000, pretrained='imagenet'): 257 | r"""Inception v3 model architecture from 258 | `"Rethinking the Inception Architecture for Computer Vision" `_. 259 | """ 260 | model = models.inception_v3(pretrained=False) 261 | if pretrained is not None: 262 | settings = pretrained_settings['inceptionv3'][pretrained] 263 | model = load_pretrained(model, num_classes, settings) 264 | 265 | # Modify attributs 266 | model.last_linear = model.fc 267 | del model.fc 268 | 269 | def features(self, input): 270 | # 299 x 299 x 3 271 | x = self.Conv2d_1a_3x3(input) # 149 x 149 x 32 272 | x = self.Conv2d_2a_3x3(x) # 147 x 147 x 32 273 | x = self.Conv2d_2b_3x3(x) # 147 x 147 x 64 274 | x = F.max_pool2d(x, kernel_size=3, stride=2) # 73 x 73 x 64 275 | x = self.Conv2d_3b_1x1(x) # 73 x 73 x 80 276 | x = self.Conv2d_4a_3x3(x) # 71 x 71 x 192 277 | x = F.max_pool2d(x, kernel_size=3, stride=2) # 35 x 35 x 192 278 | x = self.Mixed_5b(x) # 35 x 35 x 256 279 | x = self.Mixed_5c(x) # 35 x 35 x 288 280 | x = self.Mixed_5d(x) # 35 x 35 x 288 281 | x = self.Mixed_6a(x) # 17 x 17 x 768 282 | x = self.Mixed_6b(x) # 17 x 17 x 768 283 | x = self.Mixed_6c(x) # 17 x 17 x 768 284 | x = self.Mixed_6d(x) # 17 x 17 x 768 285 | x = self.Mixed_6e(x) # 17 x 17 x 768 286 | if self.training and self.aux_logits: 287 | self._out_aux = self.AuxLogits(x) # 17 x 17 x 768 288 | x = self.Mixed_7a(x) # 8 x 8 x 1280 289 | x = self.Mixed_7b(x) # 8 x 8 x 2048 290 | x = self.Mixed_7c(x) # 8 x 8 x 2048 291 | return x 292 | 293 | def logits(self, features): 294 | x = F.avg_pool2d(features, kernel_size=8) # 1 x 1 x 2048 295 | x = F.dropout(x, training=self.training) # 1 x 1 x 2048 296 | x = x.view(x.size(0), -1) # 2048 297 | x = self.last_linear(x) # 1000 (num_classes) 298 | if self.training and self.aux_logits: 299 | aux = self._out_aux 300 | self._out_aux = None 301 | return x, aux 302 | return x 303 | 304 | def forward(self, input): 305 | x = self.features(input) 306 | x = self.logits(x) 307 | return x 308 | 309 | # Modify methods 310 | model.features = types.MethodType(features, model) 311 | model.logits = types.MethodType(logits, model) 312 | model.forward = types.MethodType(forward, model) 313 | return model 314 | 315 | ############################################################### 316 | # ResNets 317 | 318 | def modify_resnets(model): 319 | # Modify attributs 320 | model.last_linear = model.fc 321 | model.fc = None 322 | 323 | def features(self, input): 324 | x = self.conv1(input) 325 | x = self.bn1(x) 326 | x = self.relu(x) 327 | x = self.maxpool(x) 328 | 329 | x = self.layer1(x) 330 | x = self.layer2(x) 331 | x = self.layer3(x) 332 | x = self.layer4(x) 333 | return x 334 | 335 | def logits(self, features): 336 | x = self.avgpool(features) 337 | x = x.view(x.size(0), -1) 338 | x = self.last_linear(x) 339 | return x 340 | 341 | def forward(self, input): 342 | x = self.features(input) 343 | #x = self.logits(x) 344 | return x 345 | 346 | # Modify methods 347 | model.features = types.MethodType(features, model) 348 | model.logits = types.MethodType(logits, model) 349 | model.forward = types.MethodType(forward, model) 350 | return model 351 | 352 | def resnet18(num_classes=1000, pretrained='imagenet'): 353 | """Constructs a ResNet-18 model. 354 | """ 355 | model = models.resnet18(pretrained=False) 356 | if pretrained is not None: 357 | settings = pretrained_settings['resnet18'][pretrained] 358 | model = load_pretrained(model, num_classes, settings) 359 | model = modify_resnets(model) 360 | return model 361 | 362 | def resnet34(num_classes=1000, pretrained='imagenet'): 363 | """Constructs a ResNet-34 model. 364 | """ 365 | model = models.resnet34(pretrained=False) 366 | if pretrained is not None: 367 | settings = pretrained_settings['resnet34'][pretrained] 368 | model = load_pretrained(model, num_classes, settings) 369 | model = modify_resnets(model) 370 | return model 371 | 372 | def resnet50(num_classes=1000, pretrained='imagenet'): 373 | """Constructs a ResNet-50 model. 374 | """ 375 | model = models.resnet50(pretrained=False) 376 | if pretrained is not None: 377 | settings = pretrained_settings['resnet50'][pretrained] 378 | model = load_pretrained(model, num_classes, settings) 379 | model = modify_resnets(model) 380 | return model 381 | 382 | def resnet101(num_classes=1000, pretrained='imagenet'): 383 | """Constructs a ResNet-101 model. 384 | """ 385 | model = models.resnet101(pretrained=False) 386 | if pretrained is not None: 387 | settings = pretrained_settings['resnet101'][pretrained] 388 | model = load_pretrained(model, num_classes, settings) 389 | model = modify_resnets(model) 390 | return model 391 | 392 | def resnet152(num_classes=1000, pretrained='imagenet'): 393 | """Constructs a ResNet-152 model. 394 | """ 395 | model = models.resnet152(pretrained=False) 396 | #if pretrained is not None: 397 | # settings = pretrained_settings['resnet152'][pretrained] 398 | # model = load_pretrained(model, num_classes, settings) 399 | model = modify_resnets(model) 400 | return model 401 | 402 | ############################################################### 403 | # SqueezeNets 404 | 405 | def modify_squeezenets(model): 406 | # /!\ Beware squeezenets do not have any last_linear module 407 | 408 | # Modify attributs 409 | model.dropout = model.classifier[0] 410 | model.last_conv = model.classifier[1] 411 | model.relu = model.classifier[2] 412 | model.avgpool = model.classifier[3] 413 | del model.classifier 414 | 415 | def logits(self, features): 416 | x = self.dropout(features) 417 | x = self.last_conv(x) 418 | x = self.relu(x) 419 | x = self.avgpool(x) 420 | return x 421 | 422 | def forward(self, input): 423 | x = self.features(input) 424 | x = self.logits(x) 425 | return x 426 | 427 | # Modify methods 428 | model.logits = types.MethodType(logits, model) 429 | model.forward = types.MethodType(forward, model) 430 | return model 431 | 432 | def squeezenet1_0(num_classes=1000, pretrained='imagenet'): 433 | r"""SqueezeNet model architecture from the `"SqueezeNet: AlexNet-level 434 | accuracy with 50x fewer parameters and <0.5MB model size" 435 | `_ paper. 436 | """ 437 | model = models.squeezenet1_0(pretrained=False) 438 | if pretrained is not None: 439 | settings = pretrained_settings['squeezenet1_0'][pretrained] 440 | model = load_pretrained(model, num_classes, settings) 441 | model = modify_squeezenets(model) 442 | return model 443 | 444 | def squeezenet1_1(num_classes=1000, pretrained='imagenet'): 445 | r"""SqueezeNet 1.1 model from the `official SqueezeNet repo 446 | `_. 447 | SqueezeNet 1.1 has 2.4x less computation and slightly fewer parameters 448 | than SqueezeNet 1.0, without sacrificing accuracy. 449 | """ 450 | model = models.squeezenet1_1(pretrained=False) 451 | if pretrained is not None: 452 | settings = pretrained_settings['squeezenet1_1'][pretrained] 453 | model = load_pretrained(model, num_classes, settings) 454 | model = modify_squeezenets(model) 455 | return model 456 | 457 | ############################################################### 458 | # VGGs 459 | 460 | def modify_vggs(model): 461 | # Modify attributs 462 | model._features = model.features 463 | del model.features 464 | model.linear0 = model.classifier[0] 465 | model.relu0 = model.classifier[1] 466 | model.dropout0 = model.classifier[2] 467 | model.linear1 = model.classifier[3] 468 | model.relu1 = model.classifier[4] 469 | model.dropout1 = model.classifier[5] 470 | model.last_linear = model.classifier[6] 471 | del model.classifier 472 | 473 | def features(self, input): 474 | x = self._features(input) 475 | x = x.view(x.size(0), -1) 476 | x = self.linear0(x) 477 | x = self.relu0(x) 478 | x = self.dropout0(x) 479 | x = self.linear1(x) 480 | return x 481 | 482 | def logits(self, features): 483 | x = self.relu1(features) 484 | x = self.dropout1(x) 485 | x = self.last_linear(x) 486 | return x 487 | 488 | def forward(self, input): 489 | x = self.features(input) 490 | x = self.logits(x) 491 | return x 492 | 493 | # Modify methods 494 | model.features = types.MethodType(features, model) 495 | model.logits = types.MethodType(logits, model) 496 | model.forward = types.MethodType(forward, model) 497 | return model 498 | 499 | def vgg11(num_classes=1000, pretrained='imagenet'): 500 | """VGG 11-layer model (configuration "A") 501 | """ 502 | model = models.vgg11(pretrained=False) 503 | if pretrained is not None: 504 | settings = pretrained_settings['vgg11'][pretrained] 505 | model = load_pretrained(model, num_classes, settings) 506 | model = modify_vggs(model) 507 | return model 508 | 509 | def vgg11_bn(num_classes=1000, pretrained='imagenet'): 510 | """VGG 11-layer model (configuration "A") with batch normalization 511 | """ 512 | model = models.vgg11_bn(pretrained=False) 513 | if pretrained is not None: 514 | settings = pretrained_settings['vgg11_bn'][pretrained] 515 | model = load_pretrained(model, num_classes, settings) 516 | model = modify_vggs(model) 517 | return model 518 | 519 | def vgg13(num_classes=1000, pretrained='imagenet'): 520 | """VGG 13-layer model (configuration "B") 521 | """ 522 | model = models.vgg13(pretrained=False) 523 | if pretrained is not None: 524 | settings = pretrained_settings['vgg13'][pretrained] 525 | model = load_pretrained(model, num_classes, settings) 526 | model = modify_vggs(model) 527 | return model 528 | 529 | def vgg13_bn(num_classes=1000, pretrained='imagenet'): 530 | """VGG 13-layer model (configuration "B") with batch normalization 531 | """ 532 | model = models.vgg13_bn(pretrained=False) 533 | if pretrained is not None: 534 | settings = pretrained_settings['vgg13_bn'][pretrained] 535 | model = load_pretrained(model, num_classes, settings) 536 | model = modify_vggs(model) 537 | return model 538 | 539 | def vgg16(num_classes=1000, pretrained='imagenet'): 540 | """VGG 16-layer model (configuration "D") 541 | """ 542 | model = models.vgg16(pretrained=False) 543 | if pretrained is not None: 544 | settings = pretrained_settings['vgg16'][pretrained] 545 | model = load_pretrained(model, num_classes, settings) 546 | model = modify_vggs(model) 547 | return model 548 | 549 | def vgg16_bn(num_classes=1000, pretrained='imagenet'): 550 | """VGG 16-layer model (configuration "D") with batch normalization 551 | """ 552 | model = models.vgg16_bn(pretrained=False) 553 | if pretrained is not None: 554 | settings = pretrained_settings['vgg16_bn'][pretrained] 555 | model = load_pretrained(model, num_classes, settings) 556 | model = modify_vggs(model) 557 | return model 558 | 559 | def vgg19(num_classes=1000, pretrained='imagenet'): 560 | """VGG 19-layer model (configuration "E") 561 | """ 562 | model = models.vgg19(pretrained=False) 563 | if pretrained is not None: 564 | settings = pretrained_settings['vgg19'][pretrained] 565 | model = load_pretrained(model, num_classes, settings) 566 | model = modify_vggs(model) 567 | return model 568 | 569 | def vgg19_bn(num_classes=1000, pretrained='imagenet'): 570 | """VGG 19-layer model (configuration 'E') with batch normalization 571 | """ 572 | model = models.vgg19_bn(pretrained=False) 573 | if pretrained is not None: 574 | settings = pretrained_settings['vgg19_bn'][pretrained] 575 | model = load_pretrained(model, num_classes, settings) 576 | model = modify_vggs(model) 577 | return model 578 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/modeling/baseline_de.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: weijian 4 | @contact: dengwj16@gmail.com 5 | """ 6 | 7 | from torch import nn 8 | 9 | from .backbones.torchvision_models import densenet121 10 | 11 | def weights_init_kaiming(m): 12 | classname = m.__class__.__name__ 13 | if classname.find('Linear') != -1: 14 | nn.init.kaiming_normal_(m.weight, a=0, mode='fan_out') 15 | nn.init.constant_(m.bias, 0.0) 16 | elif classname.find('Conv') != -1: 17 | nn.init.kaiming_normal_(m.weight, a=0, mode='fan_in') 18 | if m.bias is not None: 19 | nn.init.constant_(m.bias, 0.0) 20 | elif classname.find('BatchNorm') != -1: 21 | if m.affine: 22 | nn.init.constant_(m.weight, 1.0) 23 | nn.init.constant_(m.bias, 0.0) 24 | 25 | 26 | def weights_init_classifier(m): 27 | classname = m.__class__.__name__ 28 | if classname.find('Linear') != -1: 29 | nn.init.normal_(m.weight, std=0.001) 30 | if m.bias: 31 | nn.init.constant_(m.bias, 0.0) 32 | 33 | def freeze_bn(modules): 34 | for m in modules: 35 | if isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): 36 | m.eval() 37 | m.weight.requires_grad = False 38 | m.bias.requires_grad = False 39 | 40 | class Baseline_de(nn.Module): 41 | in_planes = 1024 42 | 43 | def __init__(self, num_classes, last_stride, model_path): 44 | super(Baseline_de, self).__init__() 45 | self.base = densenet121() 46 | self.base.features.transition3.pool=nn.Sequential()# stride 47 | 48 | self.gap = nn.AdaptiveAvgPool2d(1) 49 | self.num_bottleneck = 1024 50 | self.num_classes = num_classes 51 | 52 | add_block = [] 53 | add_block += [nn.Linear(self.in_planes, self.num_bottleneck)] 54 | add_block += [nn.BatchNorm1d(self.num_bottleneck)] 55 | add_block = nn.Sequential(*add_block) 56 | self.bottleneck = add_block 57 | self.bottleneck.apply(weights_init_kaiming) 58 | 59 | self.classifier = nn.Linear(self.num_bottleneck, self.num_classes, bias=False) 60 | self.classifier.apply(weights_init_classifier) 61 | 62 | def forward(self, x): 63 | global_feat = self.gap(self.base(x)) # (b, 1024, 1, 1) 64 | global_feat = global_feat.view(global_feat.shape[0], -1) # flatten to (bs, 1024) 65 | feat = self.bottleneck(global_feat) # normalize for angular softmax 66 | if self.training: 67 | cls_score = self.classifier(feat) 68 | return cls_score, feat #feat or global_feat for triplet loss 69 | else: 70 | return global_feat ## feat 71 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import make_optimizer 8 | from .lr_scheduler import WarmupMultiStepLR -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/solver/__init__.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/solver/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/__pycache__/build.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/solver/__pycache__/build.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/__pycache__/lr_scheduler.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/solver/__pycache__/lr_scheduler.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | 9 | 10 | def make_optimizer(cfg, model): 11 | params = [] 12 | #ignored_params = list(map(id, model.classifier.parameters() )) + list(map(id, model.bottleneck.parameters() )) 13 | #base_params = filter(lambda p: id(p) not in ignored_params, model.parameters()) 14 | 15 | for key, value in model.base.named_parameters(): 16 | if not value.requires_grad: 17 | continue 18 | lr = cfg.SOLVER.BASE_LR 19 | weight_decay = cfg.SOLVER.WEIGHT_DECAY 20 | if "bias" in key: 21 | lr = cfg.SOLVER.BASE_LR * cfg.SOLVER.BIAS_LR_FACTOR 22 | weight_decay = cfg.SOLVER.WEIGHT_DECAY_BIAS 23 | params += [{"params": [value], "lr": lr, "weight_decay": weight_decay}] 24 | 25 | for key, value in model.classifier.named_parameters(): 26 | if not value.requires_grad: 27 | continue 28 | lr = cfg.SOLVER.BASE_LR 29 | weight_decay = cfg.SOLVER.WEIGHT_DECAY 30 | if "bias" in key: 31 | lr = cfg.SOLVER.BASE_LR * cfg.SOLVER.BIAS_LR_FACTOR 32 | weight_decay = cfg.SOLVER.WEIGHT_DECAY_BIAS 33 | params += [{"params": [value], "lr": lr*2, "weight_decay": weight_decay}] 34 | 35 | for key, value in model.bottleneck.named_parameters(): 36 | if not value.requires_grad: 37 | continue 38 | lr = cfg.SOLVER.BASE_LR 39 | weight_decay = cfg.SOLVER.WEIGHT_DECAY 40 | if "bias" in key: 41 | lr = cfg.SOLVER.BASE_LR * cfg.SOLVER.BIAS_LR_FACTOR 42 | weight_decay = cfg.SOLVER.WEIGHT_DECAY_BIAS 43 | params += [{"params": [value], "lr": lr*2, "weight_decay": weight_decay}] 44 | 45 | if cfg.SOLVER.OPTIMIZER_NAME == 'SGD': 46 | optimizer = getattr(torch.optim, cfg.SOLVER.OPTIMIZER_NAME)(params, momentum=cfg.SOLVER.MOMENTUM) 47 | else: 48 | optimizer = getattr(torch.optim, cfg.SOLVER.OPTIMIZER_NAME)(params) 49 | return optimizer 50 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/build.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/solver/build.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/lr_scheduler.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | from bisect import bisect_right 7 | import torch 8 | 9 | 10 | # FIXME ideally this would be achieved with a CombinedLRScheduler, 11 | # separating MultiStepLR with WarmupLR 12 | # but the current LRScheduler design doesn't allow it 13 | 14 | class WarmupMultiStepLR(torch.optim.lr_scheduler._LRScheduler): 15 | def __init__( 16 | self, 17 | optimizer, 18 | milestones, 19 | gamma=0.1, 20 | warmup_factor=1.0 / 3, 21 | warmup_iters=500, 22 | warmup_method="linear", 23 | last_epoch=-1, 24 | ): 25 | if not list(milestones) == sorted(milestones): 26 | raise ValueError( 27 | "Milestones should be a list of" " increasing integers. Got {}", 28 | milestones, 29 | ) 30 | 31 | if warmup_method not in ("constant", "linear"): 32 | raise ValueError( 33 | "Only 'constant' or 'linear' warmup_method accepted" 34 | "got {}".format(warmup_method) 35 | ) 36 | self.milestones = milestones 37 | self.gamma = gamma 38 | self.warmup_factor = warmup_factor 39 | self.warmup_iters = warmup_iters 40 | self.warmup_method = warmup_method 41 | super(WarmupMultiStepLR, self).__init__(optimizer, last_epoch) 42 | 43 | def get_lr(self): 44 | warmup_factor = 1 45 | if self.last_epoch < self.warmup_iters: 46 | if self.warmup_method == "constant": 47 | warmup_factor = self.warmup_factor 48 | elif self.warmup_method == "linear": 49 | alpha = self.last_epoch / self.warmup_iters 50 | warmup_factor = self.warmup_factor * (1 - alpha) + alpha 51 | return [ 52 | base_lr 53 | * warmup_factor 54 | * self.gamma ** bisect_right(self.milestones, self.last_epoch) 55 | for base_lr in self.base_lrs 56 | ] 57 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/solver/lr_scheduler.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/solver/lr_scheduler.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/tests/lr_scheduler_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | import torch 5 | from torch import nn 6 | 7 | sys.path.append('.') 8 | from solver.lr_scheduler import WarmupMultiStepLR 9 | from solver.build import make_optimizer 10 | from config import cfg 11 | 12 | 13 | class MyTestCase(unittest.TestCase): 14 | def test_something(self): 15 | net = nn.Linear(10, 10) 16 | optimizer = make_optimizer(cfg, net) 17 | lr_scheduler = WarmupMultiStepLR(optimizer, [20, 40], warmup_iters=10) 18 | for i in range(50): 19 | lr_scheduler.step() 20 | for j in range(3): 21 | print(i, lr_scheduler.get_lr()[0]) 22 | optimizer.step() 23 | 24 | 25 | if __name__ == '__main__': 26 | unittest.main() 27 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/tools/test.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import argparse 8 | import os 9 | import sys 10 | from os import mkdir 11 | 12 | import torch 13 | from torch.backends import cudnn 14 | 15 | sys.path.append('.') 16 | from config import cfg 17 | from data import make_data_loader 18 | from engine.inference import inference 19 | from modeling import build_model 20 | from utils.logger import setup_logger 21 | 22 | 23 | def main(): 24 | parser = argparse.ArgumentParser(description="ReID Baseline Inference") 25 | parser.add_argument( 26 | "--config_file", default="", help="path to config file", type=str 27 | ) 28 | parser.add_argument("opts", help="Modify config options using the command-line", default=None, 29 | nargs=argparse.REMAINDER) 30 | 31 | args = parser.parse_args() 32 | 33 | num_gpus = int(os.environ["WORLD_SIZE"]) if "WORLD_SIZE" in os.environ else 1 34 | 35 | if args.config_file != "": 36 | cfg.merge_from_file(args.config_file) 37 | cfg.merge_from_list(args.opts) 38 | cfg.freeze() 39 | 40 | output_dir = cfg.OUTPUT_DIR 41 | if output_dir and not os.path.exists(output_dir): 42 | mkdir(output_dir) 43 | 44 | logger = setup_logger("reid_baseline", output_dir, 0) 45 | logger.info("Using {} GPUS".format(num_gpus)) 46 | logger.info(args) 47 | 48 | if args.config_file != "": 49 | logger.info("Loaded configuration file {}".format(args.config_file)) 50 | with open(args.config_file, 'r') as cf: 51 | config_str = "\n" + cf.read() 52 | logger.info(config_str) 53 | logger.info("Running with config:\n{}".format(cfg)) 54 | 55 | cudnn.benchmark = True 56 | 57 | train_loader, val_loader, num_query, num_classes = make_data_loader(cfg) 58 | model = build_model(cfg, num_classes) 59 | model.load_state_dict(torch.load(cfg.TEST.WEIGHT)) 60 | 61 | inference(cfg, model, val_loader, num_query) 62 | 63 | 64 | if __name__ == '__main__': 65 | main() 66 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/tools/train.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import argparse 8 | import os 9 | import sys 10 | import torch.nn as nn 11 | 12 | from torch.backends import cudnn 13 | 14 | sys.path.append('.') 15 | from config import cfg 16 | from data import make_data_loader 17 | from engine.trainer import do_train 18 | from modeling import build_model 19 | from layers import make_loss 20 | from solver import make_optimizer, WarmupMultiStepLR 21 | 22 | from utils.logger import setup_logger 23 | 24 | 25 | def train(cfg): 26 | # prepare dataset 27 | train_loader, val_loader, num_query, num_classes = make_data_loader(cfg) 28 | # prepare model 29 | model = build_model(cfg, num_classes) 30 | 31 | optimizer = make_optimizer(cfg, model) 32 | scheduler = WarmupMultiStepLR(optimizer, cfg.SOLVER.STEPS, cfg.SOLVER.GAMMA, cfg.SOLVER.WARMUP_FACTOR, 33 | cfg.SOLVER.WARMUP_ITERS, cfg.SOLVER.WARMUP_METHOD) 34 | 35 | ## multi-gpu 36 | #model = nn.DataParallel(model, device_ids=[0,1]) 37 | 38 | loss_func = make_loss(cfg) 39 | 40 | arguments = {} 41 | 42 | do_train( 43 | cfg, 44 | model, 45 | train_loader, 46 | val_loader, 47 | optimizer, 48 | scheduler, 49 | loss_func, 50 | num_query 51 | ) 52 | 53 | 54 | def main(): 55 | parser = argparse.ArgumentParser(description="ReID Baseline Training") 56 | parser.add_argument( 57 | "--config_file", default="", help="path to config file", type=str 58 | ) 59 | parser.add_argument("opts", help="Modify config options using the command-line", default=None, 60 | nargs=argparse.REMAINDER) 61 | 62 | args = parser.parse_args() 63 | 64 | num_gpus = int(os.environ["WORLD_SIZE"]) if "WORLD_SIZE" in os.environ else 1 65 | 66 | if args.config_file != "": 67 | cfg.merge_from_file(args.config_file) 68 | cfg.merge_from_list(args.opts) 69 | cfg.freeze() 70 | 71 | output_dir = cfg.OUTPUT_DIR 72 | if output_dir and not os.path.exists(output_dir): 73 | os.makedirs(output_dir) 74 | 75 | logger = setup_logger("reid_baseline", output_dir, 0) 76 | logger.info("Using {} GPUS".format(num_gpus)) 77 | logger.info(args) 78 | 79 | if args.config_file != "": 80 | logger.info("Loaded configuration file {}".format(args.config_file)) 81 | with open(args.config_file, 'r') as cf: 82 | config_str = "\n" + cf.read() 83 | logger.info(config_str) 84 | logger.info("Running with config:\n{}".format(cfg)) 85 | 86 | cudnn.benchmark = True 87 | train(cfg) 88 | 89 | 90 | if __name__ == '__main__': 91 | main() 92 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/tools/update.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: weijian 4 | @contact: dengwj16@gmail.com 5 | """ 6 | 7 | import argparse 8 | import os 9 | import sys 10 | from os import mkdir 11 | 12 | import torch 13 | from torch.backends import cudnn 14 | 15 | sys.path.append('.') 16 | from config import cfg 17 | from data import make_data_loader 18 | from engine.inference import inference 19 | from modeling import build_model 20 | from utils.logger import setup_logger 21 | from data.datasets.eval_reid import eval_func 22 | import logging 23 | import numpy as np 24 | 25 | from utils.re_ranking import re_ranking 26 | 27 | def fliplr(img): 28 | '''flip horizontal''' 29 | inv_idx = torch.arange(img.size(3)-1,-1,-1).long() # N x C x H x W 30 | img_flip = img.index_select(3,inv_idx) 31 | return img_flip 32 | 33 | def get_id(img_path): 34 | camera_id = [] 35 | labels = [] 36 | for path, v in img_path: 37 | filename = os.path.basename(path) 38 | label = filename[0:4] 39 | camera = filename.split('c')[1] 40 | if label[0:2]=='-1': 41 | labels.append(-1) 42 | else: 43 | labels.append(int(label)) 44 | camera_id.append(int(camera[0])) 45 | return camera_id, labels 46 | 47 | def extract_feature(model, dataloaders, num_query): 48 | features = [] 49 | count = 0 50 | img_path = [] 51 | 52 | for data in dataloaders: 53 | img, _, _ = data 54 | n, c, h, w = img.size() 55 | count += n 56 | ff = torch.FloatTensor(n, 1024).zero_().cuda() # 2048 is pool5 of resnet 57 | for i in range(2): 58 | if(i==1): 59 | img = fliplr(img) 60 | input_img = img.cuda() 61 | outputs = model(input_img) 62 | f = outputs.float() 63 | ff = ff+f 64 | # norm feature 65 | fnorm = torch.norm(ff, p=2, dim=1, keepdim=True) 66 | ff = ff.div(fnorm.expand_as(ff)) 67 | features.append(ff) 68 | features = torch.cat(features, 0) 69 | 70 | # query 71 | qf = features[:num_query] 72 | # gallery 73 | gf = features[num_query:] 74 | return qf, gf 75 | 76 | def main(): 77 | parser = argparse.ArgumentParser(description="ReID Baseline Inference") 78 | parser.add_argument( 79 | "--config_file", default="", help="path to config file", type=str 80 | ) 81 | parser.add_argument("opts", help="Modify config options using the command-line", default=None, 82 | nargs=argparse.REMAINDER) 83 | 84 | args = parser.parse_args() 85 | 86 | num_gpus = int(os.environ["WORLD_SIZE"]) if "WORLD_SIZE" in os.environ else 1 87 | 88 | if args.config_file != "": 89 | cfg.merge_from_file(args.config_file) 90 | cfg.merge_from_list(args.opts) 91 | cfg.freeze() 92 | 93 | output_dir = cfg.OUTPUT_DIR 94 | if output_dir and not os.path.exists(output_dir): 95 | mkdir(output_dir) 96 | 97 | logger = setup_logger("reid_baseline", output_dir, 0) 98 | logger.info("Using {} GPUS".format(num_gpus)) 99 | logger.info(args) 100 | 101 | if args.config_file != "": 102 | logger.info("Loaded configuration file {}".format(args.config_file)) 103 | with open(args.config_file, 'r') as cf: 104 | config_str = "\n" + cf.read() 105 | logger.info(config_str) 106 | logger.info("Running with config:\n{}".format(cfg)) 107 | 108 | cudnn.benchmark = True 109 | 110 | train_loader, val_loader, num_query, num_classes = make_data_loader(cfg) 111 | model = build_model(cfg, num_classes) 112 | model.load_state_dict(torch.load(cfg.TEST.WEIGHT)) 113 | model = model.cuda() 114 | model = model.eval() 115 | 116 | logger = logging.getLogger("reid_baseline.inference") 117 | logger.info("Start inferencing") 118 | with torch.no_grad(): 119 | qf, gf = extract_feature(model, val_loader, num_query) 120 | 121 | # save feature 122 | np.save('../data/feature_expansion/' + cfg.TEST.QF_NAME, qf.cpu().numpy()) 123 | np.save('../data/feature_expansion/' + cfg.TEST.GF_NAME, gf.cpu().numpy()) 124 | 125 | ''' 126 | q_g_dist = np.dot(qf.cpu().numpy(), np.transpose(gf.cpu().numpy())) 127 | q_q_dist = np.dot(qf.cpu().numpy(), np.transpose(qf.cpu().numpy())) 128 | g_g_dist = np.dot(gf.cpu().numpy(), np.transpose(gf.cpu().numpy())) 129 | 130 | re_rank_dist = re_ranking(q_g_dist, q_q_dist, g_g_dist) 131 | ''' 132 | 133 | if __name__ == '__main__': 134 | main() 135 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/utils/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__pycache__/iotools.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/utils/__pycache__/iotools.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__pycache__/logger.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/utils/__pycache__/logger.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__pycache__/model_loder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/utils/__pycache__/model_loder.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__pycache__/re_ranking.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/utils/__pycache__/re_ranking.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/__pycache__/reid_metric.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon4Yan/feature_learning/23ecc1bb2ce3ce4bece9159ca4ecc420e3e8f34c/Feature_Learning/learning_model/utils/__pycache__/reid_metric.cpython-37.pyc -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/iotools.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import errno 8 | import json 9 | import os 10 | 11 | import os.path as osp 12 | 13 | 14 | def mkdir_if_missing(directory): 15 | if not osp.exists(directory): 16 | try: 17 | os.makedirs(directory) 18 | except OSError as e: 19 | if e.errno != errno.EEXIST: 20 | raise 21 | 22 | 23 | def check_isfile(path): 24 | isfile = osp.isfile(path) 25 | if not isfile: 26 | print("=> Warning: no file found at '{}' (ignored)".format(path)) 27 | return isfile 28 | 29 | 30 | def read_json(fpath): 31 | with open(fpath, 'r') as f: 32 | obj = json.load(f) 33 | return obj 34 | 35 | 36 | def write_json(obj, fpath): 37 | mkdir_if_missing(osp.dirname(fpath)) 38 | with open(fpath, 'w') as f: 39 | json.dump(obj, f, indent=4, separators=(',', ': ')) 40 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/logger.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import logging 8 | import os 9 | import sys 10 | 11 | 12 | def setup_logger(name, save_dir, distributed_rank): 13 | logger = logging.getLogger(name) 14 | logger.setLevel(logging.DEBUG) 15 | # don't log results for the non-master process 16 | if distributed_rank > 0: 17 | return logger 18 | ch = logging.StreamHandler(stream=sys.stdout) 19 | ch.setLevel(logging.DEBUG) 20 | formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s") 21 | ch.setFormatter(formatter) 22 | logger.addHandler(ch) 23 | 24 | if save_dir: 25 | fh = logging.FileHandler(os.path.join(save_dir, "log.txt"), mode='w') 26 | fh.setLevel(logging.DEBUG) 27 | fh.setFormatter(formatter) 28 | logger.addHandler(fh) 29 | 30 | return logger 31 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/model_loder.py: -------------------------------------------------------------------------------- 1 | from torch import nn 2 | import torch 3 | import os.path as osp 4 | 5 | 6 | def load_checkpoint(fpath): 7 | if osp.isfile(fpath): 8 | checkpoint = torch.load(fpath) 9 | print("=> Loaded checkpoint '{}'".format(fpath)) 10 | return checkpoint 11 | else: 12 | raise ValueError("=> No checkpoint found at '{}'".format(fpath)) 13 | 14 | def checkpoint_loader(model, path, eval_only=False): 15 | checkpoint = load_checkpoint(path) 16 | pretrained_dict = checkpoint['state_dict'] 17 | if isinstance(model, nn.DataParallel): 18 | Parallel = 1 19 | model = model.module.cpu() 20 | else: 21 | Parallel = 0 22 | 23 | model_dict = model.state_dict() 24 | # 1. filter out unnecessary keys 25 | pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict} 26 | if eval_only: 27 | keys_to_del = [] 28 | for key in pretrained_dict.keys(): 29 | if 'classifier' in key: 30 | keys_to_del.append(key) 31 | for key in keys_to_del: 32 | del pretrained_dict[key] 33 | pass 34 | # 2. overwrite entries in the existing state dict 35 | model_dict.update(pretrained_dict) 36 | # 3. load the new state dict 37 | model.load_state_dict(model_dict) 38 | 39 | start_epoch = checkpoint['epoch'] 40 | best_top1 = checkpoint['best_top1'] 41 | 42 | if Parallel: 43 | model = nn.DataParallel(model).cuda() 44 | 45 | return model, start_epoch, best_top1 -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/re_ranking.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2/python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Mon Jun 26 14:46:56 2017 5 | @author: luohao 6 | Modified by Houjing Huang, 2017-12-22. 7 | - This version accepts distance matrix instead of raw features. 8 | - The difference of `/` division between python 2 and 3 is handled. 9 | - numpy.float16 is replaced by numpy.float32 for numerical precision. 10 | 11 | Modified by Zhedong Zheng, 2018-1-12. 12 | - replace sort with topK, which save about 30s. 13 | """ 14 | 15 | """ 16 | CVPR2017 paper:Zhong Z, Zheng L, Cao D, et al. Re-ranking Person Re-identification with k-reciprocal Encoding[J]. 2017. 17 | url:http://openaccess.thecvf.com/content_cvpr_2017/papers/Zhong_Re-Ranking_Person_Re-Identification_CVPR_2017_paper.pdf 18 | Matlab version: https://github.com/zhunzhong07/person-re-ranking 19 | """ 20 | 21 | """ 22 | API 23 | q_g_dist: query-gallery distance matrix, numpy array, shape [num_query, num_gallery] 24 | q_q_dist: query-query distance matrix, numpy array, shape [num_query, num_query] 25 | g_g_dist: gallery-gallery distance matrix, numpy array, shape [num_gallery, num_gallery] 26 | k1, k2, lambda_value: parameters, the original paper is (k1=20, k2=6, lambda_value=0.3) 27 | Returns: 28 | final_dist: re-ranked distance, numpy array, shape [num_query, num_gallery] 29 | """ 30 | 31 | 32 | import numpy as np 33 | 34 | def k_reciprocal_neigh( initial_rank, i, k1): 35 | forward_k_neigh_index = initial_rank[i,:k1+1] 36 | backward_k_neigh_index = initial_rank[forward_k_neigh_index,:k1+1] 37 | fi = np.where(backward_k_neigh_index==i)[0] 38 | return forward_k_neigh_index[fi] 39 | 40 | def re_ranking(q_g_dist, q_q_dist, g_g_dist, k1=20, k2=6, lambda_value=0.3): 41 | # The following naming, e.g. gallery_num, is different from outer scope. 42 | # Don't care about it. 43 | original_dist = np.concatenate( 44 | [np.concatenate([q_q_dist, q_g_dist], axis=1), 45 | np.concatenate([q_g_dist.T, g_g_dist], axis=1)], 46 | axis=0) 47 | original_dist = 2. - 2 * original_dist # change the cosine similarity metric to euclidean similarity metric 48 | original_dist = np.power(original_dist, 2).astype(np.float32) 49 | original_dist = np.transpose(1. * original_dist/np.max(original_dist,axis = 0)) 50 | V = np.zeros_like(original_dist).astype(np.float32) 51 | #initial_rank = np.argsort(original_dist).astype(np.int32) 52 | # top K1+1 53 | initial_rank = np.argpartition( original_dist, range(1,k1+1) ) 54 | 55 | query_num = q_g_dist.shape[0] 56 | all_num = original_dist.shape[0] 57 | 58 | for i in range(all_num): 59 | # k-reciprocal neighbors 60 | k_reciprocal_index = k_reciprocal_neigh( initial_rank, i, k1) 61 | k_reciprocal_expansion_index = k_reciprocal_index 62 | for j in range(len(k_reciprocal_index)): 63 | candidate = k_reciprocal_index[j] 64 | candidate_k_reciprocal_index = k_reciprocal_neigh( initial_rank, candidate, int(np.around(k1/2))) 65 | if len(np.intersect1d(candidate_k_reciprocal_index,k_reciprocal_index))> 2./3*len(candidate_k_reciprocal_index): 66 | k_reciprocal_expansion_index = np.append(k_reciprocal_expansion_index,candidate_k_reciprocal_index) 67 | 68 | k_reciprocal_expansion_index = np.unique(k_reciprocal_expansion_index) 69 | weight = np.exp(-original_dist[i,k_reciprocal_expansion_index]) 70 | V[i,k_reciprocal_expansion_index] = 1.*weight/np.sum(weight) 71 | 72 | original_dist = original_dist[:query_num,] 73 | if k2 != 1: 74 | V_qe = np.zeros_like(V,dtype=np.float32) 75 | for i in range(all_num): 76 | V_qe[i,:] = np.mean(V[initial_rank[i,:k2],:],axis=0) 77 | V = V_qe 78 | del V_qe 79 | del initial_rank 80 | invIndex = [] 81 | for i in range(all_num): 82 | invIndex.append(np.where(V[:,i] != 0)[0]) 83 | 84 | jaccard_dist = np.zeros_like(original_dist,dtype = np.float32) 85 | 86 | for i in range(query_num): 87 | temp_min = np.zeros(shape=[1,all_num],dtype=np.float32) 88 | indNonZero = np.where(V[i,:] != 0)[0] 89 | indImages = [] 90 | indImages = [invIndex[ind] for ind in indNonZero] 91 | for j in range(len(indNonZero)): 92 | temp_min[0,indImages[j]] = temp_min[0,indImages[j]]+ np.minimum(V[i,indNonZero[j]],V[indImages[j],indNonZero[j]]) 93 | jaccard_dist[i] = 1-temp_min/(2.-temp_min) 94 | 95 | final_dist = jaccard_dist*(1-lambda_value) + original_dist*lambda_value 96 | del original_dist 97 | del V 98 | del jaccard_dist 99 | final_dist = final_dist[:query_num,query_num:] 100 | return final_dist 101 | -------------------------------------------------------------------------------- /Feature_Learning/learning_model/utils/reid_metric.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import numpy as np 8 | import torch 9 | from ignite.metrics import Metric 10 | 11 | from data.datasets.eval_reid import eval_func 12 | from data.datasets.ranking import cmc 13 | from data.datasets.ranking import mean_ap 14 | import scipy.io 15 | 16 | class R1_mAP(Metric): 17 | def __init__(self, num_query, max_rank=50): 18 | super(R1_mAP, self).__init__() 19 | self.num_query = num_query 20 | self.max_rank = max_rank 21 | 22 | def reset(self): 23 | self.feats = [] 24 | self.pids = [] 25 | self.camids = [] 26 | 27 | def update(self, output): 28 | feat, pid, camid = output 29 | self.feats.append(feat) 30 | self.pids.extend(np.asarray(pid)) 31 | self.camids.extend(np.asarray(camid)) 32 | 33 | def compute(self): 34 | feats = torch.cat(self.feats, dim=0) 35 | # query 36 | qf = feats[:self.num_query] 37 | q_pids = np.asarray(self.pids[:self.num_query]) 38 | q_camids = np.asarray(self.camids[:self.num_query]) 39 | # gallery 40 | gf = feats[self.num_query:] 41 | g_pids = np.asarray(self.pids[self.num_query:]) 42 | g_camids = np.asarray(self.camids[self.num_query:]) 43 | m, n = qf.shape[0], gf.shape[0] 44 | distmat = torch.pow(qf, 2).sum(dim=1, keepdim=True).expand(m, n) + \ 45 | torch.pow(gf, 2).sum(dim=1, keepdim=True).expand(n, m).t() 46 | distmat.addmm_(1, -2, qf, gf.t()) 47 | distmat = distmat.cpu().numpy() 48 | #dis_save = {'dis': distmat} 49 | #scipy.io.savemat('distmat_ori.mat', dis_save) 50 | cmc1, mAP = eval_func(distmat, q_pids, g_pids, q_camids, g_camids) 51 | #cmc0 = cmc(distmat, q_pids, g_pids, q_camids, g_camids) 52 | #mAP = mean_ap(distmat, q_pids, g_pids, q_camids, g_camids) 53 | return cmc1, mAP 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Simon4Yan 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 | # Vehicle ReID_baseline 2 | Baseline model (with bottleneck) for vehicle ReID (using softmax and triplet loss). 3 | 4 | ## Feature_Learning 5 | This part is for learning feature extractor. The code is modified form [reid_baseline](https://github.com/L1aoXingyu/reid_baseline), you can check each folder's purpose by yourself. 6 | 7 | ### REFERENCE 8 | Lv, K., Deng, W., Hou, Y., Du, H., Sheng, H., Jiao, J., & Zheng, L. (2019). Vehicle reidentification with the location and time stamp. In Proc. CVPR Workshops. 9 | ``` 10 | @inproceedings{lv2019vehicle, 11 | title={Vehicle reidentification with the location and time stamp}, 12 | author={Lv, Kai and Deng, Weijian and Hou, Yunzhong and Du, Heming and Sheng, Hao and Jiao, Jianbin and Zheng, Liang}, 13 | booktitle={Proc. CVPR Workshops}, 14 | year={2019} 15 | } 16 | ``` 17 | #### If you have any question, please contact us by E-mail (dengwj16@gmail.com) or open an issue in this project. Thanks. 18 | --------------------------------------------------------------------------------