├── yolov5 ├── displacement_images │ ├── train │ │ ├── labels │ │ │ ├── 0.txt │ │ │ ├── 1.txt │ │ │ ├── 10.txt │ │ │ ├── 11.txt │ │ │ ├── 12.txt │ │ │ ├── 13.txt │ │ │ ├── 14.txt │ │ │ ├── 15.txt │ │ │ ├── 16.txt │ │ │ ├── 17.txt │ │ │ ├── 18.txt │ │ │ ├── 19.txt │ │ │ ├── 2.txt │ │ │ ├── 20.txt │ │ │ ├── 21.txt │ │ │ ├── 22.txt │ │ │ ├── 23.txt │ │ │ ├── 24.txt │ │ │ ├── 25.txt │ │ │ ├── 26.txt │ │ │ ├── 27.txt │ │ │ ├── 28.txt │ │ │ ├── 29.txt │ │ │ ├── 3.txt │ │ │ ├── 30.txt │ │ │ ├── 31.txt │ │ │ ├── 32.txt │ │ │ ├── 33.txt │ │ │ ├── 34.txt │ │ │ ├── 35.txt │ │ │ ├── 36.txt │ │ │ ├── 37.txt │ │ │ ├── 38.txt │ │ │ ├── 39.txt │ │ │ ├── 4.txt │ │ │ ├── 40.txt │ │ │ ├── 41.txt │ │ │ ├── 42.txt │ │ │ ├── 43.txt │ │ │ ├── 44.txt │ │ │ ├── 45.txt │ │ │ ├── 46.txt │ │ │ ├── 47.txt │ │ │ ├── 48.txt │ │ │ ├── 49.txt │ │ │ ├── 5.txt │ │ │ ├── 50.txt │ │ │ ├── 51.txt │ │ │ ├── 52.txt │ │ │ ├── 53.txt │ │ │ ├── 54.txt │ │ │ ├── 55.txt │ │ │ ├── 56.txt │ │ │ ├── 57.txt │ │ │ ├── 58.txt │ │ │ ├── 59.txt │ │ │ ├── 6.txt │ │ │ ├── 60.txt │ │ │ ├── 61.txt │ │ │ ├── 62.txt │ │ │ ├── 63.txt │ │ │ ├── 64.txt │ │ │ ├── 65.txt │ │ │ ├── 66.txt │ │ │ ├── 67.txt │ │ │ ├── 68.txt │ │ │ ├── 69.txt │ │ │ ├── 7.txt │ │ │ ├── 70.txt │ │ │ ├── 71.txt │ │ │ ├── 72.txt │ │ │ ├── 73.txt │ │ │ ├── 74.txt │ │ │ ├── 75.txt │ │ │ ├── 76.txt │ │ │ ├── 77.txt │ │ │ ├── 78.txt │ │ │ ├── 79.txt │ │ │ ├── 8.txt │ │ │ ├── 80.txt │ │ │ ├── 81.txt │ │ │ ├── 82.txt │ │ │ ├── 83.txt │ │ │ ├── 84.txt │ │ │ ├── 85.txt │ │ │ ├── 86.txt │ │ │ ├── 87.txt │ │ │ ├── 88.txt │ │ │ ├── 89.txt │ │ │ └── 9.txt │ │ └── images │ │ │ ├── 0.jpeg │ │ │ ├── 1.jpeg │ │ │ ├── 2.jpeg │ │ │ ├── 3.jpeg │ │ │ ├── 4.jpeg │ │ │ ├── 5.jpeg │ │ │ ├── 6.jpeg │ │ │ ├── 7.jpeg │ │ │ ├── 8.jpeg │ │ │ ├── 9.jpeg │ │ │ ├── 10.jpeg │ │ │ ├── 11.jpeg │ │ │ ├── 12.jpeg │ │ │ ├── 13.jpeg │ │ │ ├── 14.jpeg │ │ │ ├── 15.jpeg │ │ │ ├── 16.jpeg │ │ │ ├── 17.jpeg │ │ │ ├── 18.jpeg │ │ │ ├── 19.jpeg │ │ │ ├── 20.jpeg │ │ │ ├── 21.jpeg │ │ │ ├── 22.jpeg │ │ │ ├── 23.jpeg │ │ │ ├── 24.jpeg │ │ │ ├── 25.jpeg │ │ │ ├── 26.jpeg │ │ │ ├── 27.jpeg │ │ │ ├── 28.jpeg │ │ │ ├── 29.jpeg │ │ │ ├── 30.jpeg │ │ │ ├── 31.jpeg │ │ │ ├── 32.jpeg │ │ │ ├── 33.jpeg │ │ │ ├── 34.jpeg │ │ │ ├── 35.jpeg │ │ │ ├── 36.jpeg │ │ │ ├── 37.jpeg │ │ │ ├── 38.jpeg │ │ │ ├── 39.jpeg │ │ │ ├── 40.jpeg │ │ │ ├── 41.jpeg │ │ │ ├── 42.jpeg │ │ │ ├── 43.jpeg │ │ │ ├── 44.jpeg │ │ │ ├── 45.jpeg │ │ │ ├── 46.jpeg │ │ │ ├── 47.jpeg │ │ │ ├── 48.jpeg │ │ │ ├── 49.jpeg │ │ │ ├── 50.jpeg │ │ │ ├── 51.jpeg │ │ │ ├── 52.jpeg │ │ │ ├── 53.jpeg │ │ │ ├── 54.jpeg │ │ │ ├── 55.jpeg │ │ │ ├── 56.jpeg │ │ │ ├── 57.jpeg │ │ │ ├── 58.jpeg │ │ │ ├── 59.jpeg │ │ │ ├── 60.jpeg │ │ │ ├── 61.jpeg │ │ │ ├── 62.jpeg │ │ │ ├── 63.jpeg │ │ │ ├── 64.jpeg │ │ │ ├── 65.jpeg │ │ │ ├── 66.jpeg │ │ │ ├── 67.jpeg │ │ │ ├── 68.jpeg │ │ │ ├── 69.jpeg │ │ │ ├── 70.jpeg │ │ │ ├── 71.jpeg │ │ │ ├── 72.jpeg │ │ │ ├── 73.jpeg │ │ │ ├── 74.jpeg │ │ │ ├── 75.jpeg │ │ │ ├── 76.jpeg │ │ │ ├── 77.jpeg │ │ │ ├── 78.jpeg │ │ │ ├── 79.jpeg │ │ │ ├── 80.jpeg │ │ │ ├── 81.jpeg │ │ │ ├── 82.jpeg │ │ │ ├── 83.jpeg │ │ │ ├── 84.jpeg │ │ │ ├── 85.jpeg │ │ │ ├── 86.jpeg │ │ │ ├── 87.jpeg │ │ │ ├── 88.jpeg │ │ │ └── 89.jpeg │ └── val │ │ ├── labels │ │ ├── 90.txt │ │ ├── 91.txt │ │ ├── 92.txt │ │ ├── 93.txt │ │ ├── 94.txt │ │ ├── 95.txt │ │ ├── 96.txt │ │ ├── 97.txt │ │ ├── 98.txt │ │ └── 99.txt │ │ └── images │ │ ├── 90.jpeg │ │ ├── 91.jpeg │ │ ├── 92.jpeg │ │ ├── 93.jpeg │ │ ├── 94.jpeg │ │ ├── 95.jpeg │ │ ├── 96.jpeg │ │ ├── 97.jpeg │ │ ├── 98.jpeg │ │ └── 99.jpeg └── displacement.yaml ├── images ├── gp4o-1.png ├── gpt40-2.png ├── gpt40-3.png ├── gpt4o-4.png ├── gpt4o-5.png ├── gpt4o-6.png ├── rotate.jpeg ├── 4x4e_11039.png ├── claudehard.png ├── llm_hanzi.png ├── llm_hanzi1.png ├── llm_simple.png ├── sameobject.jpg ├── textclick.png ├── claudehard1.png ├── llm_simple1.png ├── yolodetect.jpeg ├── claudesimple1.png ├── claudesimple2.png ├── matchtemplate.png ├── sameobjecttrain.jpg ├── bingcopilot_hanzi.png ├── sameobjectresult.jpg ├── bingcopilot_simple.png ├── bingcopilot_simple1.png └── bingcopilot_simple2.png ├── requirements.txt ├── fixed_length_captcha.json ├── split_data.py ├── image_utils.py ├── .gitignore ├── labelme_json_to_yolov5_format.py ├── sameobject_captcha.py ├── slide_captcha.py ├── README.md ├── rotate_captcha.py ├── LICENSE └── fixed_length_captcha.py /yolov5/displacement_images/train/labels/0.txt: -------------------------------------------------------------------------------- 1 | 0 0.496377 0.478198 0.159420 0.276163 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/1.txt: -------------------------------------------------------------------------------- 1 | 0 0.759058 0.367733 0.163043 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/10.txt: -------------------------------------------------------------------------------- 1 | 0 0.789855 0.428779 0.188406 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/11.txt: -------------------------------------------------------------------------------- 1 | 0 0.532609 0.457849 0.177536 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/12.txt: -------------------------------------------------------------------------------- 1 | 0 0.675725 0.530523 0.159420 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/13.txt: -------------------------------------------------------------------------------- 1 | 0 0.396739 0.508721 0.155797 0.267442 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/14.txt: -------------------------------------------------------------------------------- 1 | 0 0.649457 0.390988 0.172101 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/15.txt: -------------------------------------------------------------------------------- 1 | 0 0.584239 0.491279 0.179348 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/16.txt: -------------------------------------------------------------------------------- 1 | 0 0.574275 0.290698 0.163043 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/17.txt: -------------------------------------------------------------------------------- 1 | 0 0.479167 0.646802 0.153986 0.270349 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/18.txt: -------------------------------------------------------------------------------- 1 | 0 0.714674 0.651163 0.175725 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/19.txt: -------------------------------------------------------------------------------- 1 | 0 0.692935 0.312500 0.164855 0.276163 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/2.txt: -------------------------------------------------------------------------------- 1 | 0 0.793478 0.335756 0.177536 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/20.txt: -------------------------------------------------------------------------------- 1 | 0 0.638587 0.709302 0.161232 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/21.txt: -------------------------------------------------------------------------------- 1 | 0 0.677536 0.380814 0.159420 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/22.txt: -------------------------------------------------------------------------------- 1 | 0 0.612319 0.627907 0.166667 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/23.txt: -------------------------------------------------------------------------------- 1 | 0 0.621377 0.370640 0.159420 0.276163 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/24.txt: -------------------------------------------------------------------------------- 1 | 0 0.640399 0.566860 0.164855 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/25.txt: -------------------------------------------------------------------------------- 1 | 0 0.510870 0.521802 0.166667 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/26.txt: -------------------------------------------------------------------------------- 1 | 0 0.394928 0.654070 0.166667 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/27.txt: -------------------------------------------------------------------------------- 1 | 0 0.682971 0.327035 0.177536 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/28.txt: -------------------------------------------------------------------------------- 1 | 0 0.506341 0.609012 0.164855 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/29.txt: -------------------------------------------------------------------------------- 1 | 0 0.441123 0.286337 0.164855 0.270349 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/3.txt: -------------------------------------------------------------------------------- 1 | 0 0.547101 0.534884 0.173913 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/30.txt: -------------------------------------------------------------------------------- 1 | 0 0.799819 0.594477 0.179348 0.293605 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/31.txt: -------------------------------------------------------------------------------- 1 | 0 0.604167 0.293605 0.164855 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/32.txt: -------------------------------------------------------------------------------- 1 | 0 0.707428 0.498547 0.179348 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/33.txt: -------------------------------------------------------------------------------- 1 | 0 0.707428 0.587209 0.157609 0.267442 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/34.txt: -------------------------------------------------------------------------------- 1 | 0 0.459239 0.497093 0.161232 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/35.txt: -------------------------------------------------------------------------------- 1 | 0 0.778986 0.411337 0.159420 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/36.txt: -------------------------------------------------------------------------------- 1 | 0 0.559783 0.658430 0.181159 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/37.txt: -------------------------------------------------------------------------------- 1 | 0 0.526268 0.401163 0.175725 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/38.txt: -------------------------------------------------------------------------------- 1 | 0 0.376812 0.713663 0.163043 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/39.txt: -------------------------------------------------------------------------------- 1 | 0 0.509964 0.492733 0.161232 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/4.txt: -------------------------------------------------------------------------------- 1 | 0 0.652174 0.588663 0.163043 0.276163 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/40.txt: -------------------------------------------------------------------------------- 1 | 0 0.608696 0.382267 0.159420 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/41.txt: -------------------------------------------------------------------------------- 1 | 0 0.633152 0.434593 0.164855 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/42.txt: -------------------------------------------------------------------------------- 1 | 0 0.469203 0.340116 0.163043 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/43.txt: -------------------------------------------------------------------------------- 1 | 0 0.684783 0.398256 0.173913 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/44.txt: -------------------------------------------------------------------------------- 1 | 0 0.469203 0.703488 0.181159 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/45.txt: -------------------------------------------------------------------------------- 1 | 0 0.715580 0.454942 0.163043 0.276163 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/46.txt: -------------------------------------------------------------------------------- 1 | 0 0.815217 0.681686 0.177536 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/47.txt: -------------------------------------------------------------------------------- 1 | 0 0.718297 0.305233 0.164855 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/48.txt: -------------------------------------------------------------------------------- 1 | 0 0.375906 0.521802 0.164855 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/49.txt: -------------------------------------------------------------------------------- 1 | 0 0.723732 0.635174 0.179348 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/5.txt: -------------------------------------------------------------------------------- 1 | 0 0.602355 0.690407 0.179348 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/50.txt: -------------------------------------------------------------------------------- 1 | 0 0.442029 0.623547 0.173913 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/51.txt: -------------------------------------------------------------------------------- 1 | 0 0.740942 0.527616 0.166667 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/52.txt: -------------------------------------------------------------------------------- 1 | 0 0.773551 0.700581 0.173913 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/53.txt: -------------------------------------------------------------------------------- 1 | 0 0.510870 0.683140 0.166667 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/54.txt: -------------------------------------------------------------------------------- 1 | 0 0.810688 0.500000 0.175725 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/55.txt: -------------------------------------------------------------------------------- 1 | 0 0.503623 0.495640 0.163043 0.258721 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/56.txt: -------------------------------------------------------------------------------- 1 | 0 0.729167 0.526163 0.161232 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/57.txt: -------------------------------------------------------------------------------- 1 | 0 0.553442 0.531977 0.161232 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/58.txt: -------------------------------------------------------------------------------- 1 | 0 0.455616 0.495640 0.164855 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/59.txt: -------------------------------------------------------------------------------- 1 | 0 0.491848 0.459302 0.168478 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/6.txt: -------------------------------------------------------------------------------- 1 | 0 0.495471 0.354651 0.175725 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/60.txt: -------------------------------------------------------------------------------- 1 | 0 0.769928 0.319767 0.177536 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/61.txt: -------------------------------------------------------------------------------- 1 | 0 0.551630 0.565407 0.179348 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/62.txt: -------------------------------------------------------------------------------- 1 | 0 0.685688 0.433140 0.164855 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/63.txt: -------------------------------------------------------------------------------- 1 | 0 0.441123 0.688953 0.161232 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/64.txt: -------------------------------------------------------------------------------- 1 | 0 0.693841 0.409884 0.163043 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/65.txt: -------------------------------------------------------------------------------- 1 | 0 0.766304 0.620640 0.177536 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/66.txt: -------------------------------------------------------------------------------- 1 | 0 0.470109 0.643895 0.175725 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/67.txt: -------------------------------------------------------------------------------- 1 | 0 0.665761 0.456395 0.161232 0.267442 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/68.txt: -------------------------------------------------------------------------------- 1 | 0 0.702899 0.427326 0.173913 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/69.txt: -------------------------------------------------------------------------------- 1 | 0 0.440217 0.625000 0.166667 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/7.txt: -------------------------------------------------------------------------------- 1 | 0 0.514493 0.399709 0.166667 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/70.txt: -------------------------------------------------------------------------------- 1 | 0 0.413043 0.600291 0.166667 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/71.txt: -------------------------------------------------------------------------------- 1 | 0 0.563406 0.585756 0.173913 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/72.txt: -------------------------------------------------------------------------------- 1 | 0 0.509058 0.718023 0.177536 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/73.txt: -------------------------------------------------------------------------------- 1 | 0 0.567935 0.614826 0.157609 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/74.txt: -------------------------------------------------------------------------------- 1 | 0 0.653080 0.299419 0.168478 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/75.txt: -------------------------------------------------------------------------------- 1 | 0 0.706522 0.456395 0.159420 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/76.txt: -------------------------------------------------------------------------------- 1 | 0 0.432065 0.380814 0.164855 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/77.txt: -------------------------------------------------------------------------------- 1 | 0 0.562500 0.361919 0.164855 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/78.txt: -------------------------------------------------------------------------------- 1 | 0 0.730978 0.505814 0.164855 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/79.txt: -------------------------------------------------------------------------------- 1 | 0 0.800725 0.357558 0.177536 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/8.txt: -------------------------------------------------------------------------------- 1 | 0 0.554348 0.308140 0.163043 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/80.txt: -------------------------------------------------------------------------------- 1 | 0 0.789855 0.309593 0.163043 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/81.txt: -------------------------------------------------------------------------------- 1 | 0 0.562500 0.427326 0.175725 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/82.txt: -------------------------------------------------------------------------------- 1 | 0 0.375000 0.430233 0.159420 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/83.txt: -------------------------------------------------------------------------------- 1 | 0 0.643116 0.470930 0.166667 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/84.txt: -------------------------------------------------------------------------------- 1 | 0 0.745471 0.398256 0.179348 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/85.txt: -------------------------------------------------------------------------------- 1 | 0 0.584239 0.328488 0.161232 0.279070 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/86.txt: -------------------------------------------------------------------------------- 1 | 0 0.484601 0.700581 0.164855 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/87.txt: -------------------------------------------------------------------------------- 1 | 0 0.503623 0.693314 0.163043 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/88.txt: -------------------------------------------------------------------------------- 1 | 0 0.482790 0.287791 0.168478 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/89.txt: -------------------------------------------------------------------------------- 1 | 0 0.421196 0.609012 0.179348 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/train/labels/9.txt: -------------------------------------------------------------------------------- 1 | 0 0.651268 0.479651 0.161232 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/90.txt: -------------------------------------------------------------------------------- 1 | 0 0.588768 0.436047 0.177536 0.290698 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/91.txt: -------------------------------------------------------------------------------- 1 | 0 0.535326 0.373547 0.161232 0.287791 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/92.txt: -------------------------------------------------------------------------------- 1 | 0 0.655797 0.482558 0.159420 0.273256 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/93.txt: -------------------------------------------------------------------------------- 1 | 0 0.816123 0.434593 0.153986 0.276163 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/94.txt: -------------------------------------------------------------------------------- 1 | 0 0.600543 0.619186 0.175725 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/95.txt: -------------------------------------------------------------------------------- 1 | 0 0.730978 0.674419 0.164855 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/96.txt: -------------------------------------------------------------------------------- 1 | 0 0.559783 0.664244 0.159420 0.281977 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/97.txt: -------------------------------------------------------------------------------- 1 | 0 0.393116 0.372093 0.170290 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/98.txt: -------------------------------------------------------------------------------- 1 | 0 0.473732 0.715116 0.164855 0.284884 2 | -------------------------------------------------------------------------------- /yolov5/displacement_images/val/labels/99.txt: -------------------------------------------------------------------------------- 1 | 0 0.403986 0.443314 0.159420 0.276163 2 | -------------------------------------------------------------------------------- /images/gp4o-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/gp4o-1.png -------------------------------------------------------------------------------- /images/gpt40-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/gpt40-2.png -------------------------------------------------------------------------------- /images/gpt40-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/gpt40-3.png -------------------------------------------------------------------------------- /images/gpt4o-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/gpt4o-4.png -------------------------------------------------------------------------------- /images/gpt4o-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/gpt4o-5.png -------------------------------------------------------------------------------- /images/gpt4o-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/gpt4o-6.png -------------------------------------------------------------------------------- /images/rotate.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/rotate.jpeg -------------------------------------------------------------------------------- /images/4x4e_11039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/4x4e_11039.png -------------------------------------------------------------------------------- /images/claudehard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/claudehard.png -------------------------------------------------------------------------------- /images/llm_hanzi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/llm_hanzi.png -------------------------------------------------------------------------------- /images/llm_hanzi1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/llm_hanzi1.png -------------------------------------------------------------------------------- /images/llm_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/llm_simple.png -------------------------------------------------------------------------------- /images/sameobject.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/sameobject.jpg -------------------------------------------------------------------------------- /images/textclick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/textclick.png -------------------------------------------------------------------------------- /images/claudehard1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/claudehard1.png -------------------------------------------------------------------------------- /images/llm_simple1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/llm_simple1.png -------------------------------------------------------------------------------- /images/yolodetect.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/yolodetect.jpeg -------------------------------------------------------------------------------- /images/claudesimple1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/claudesimple1.png -------------------------------------------------------------------------------- /images/claudesimple2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/claudesimple2.png -------------------------------------------------------------------------------- /images/matchtemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/matchtemplate.png -------------------------------------------------------------------------------- /images/sameobjecttrain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/sameobjecttrain.jpg -------------------------------------------------------------------------------- /images/bingcopilot_hanzi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/bingcopilot_hanzi.png -------------------------------------------------------------------------------- /images/sameobjectresult.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/sameobjectresult.jpg -------------------------------------------------------------------------------- /images/bingcopilot_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/bingcopilot_simple.png -------------------------------------------------------------------------------- /images/bingcopilot_simple1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/bingcopilot_simple1.png -------------------------------------------------------------------------------- /images/bingcopilot_simple2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/images/bingcopilot_simple2.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflow==2.9.1 2 | Pillow==9.1.1 3 | requests 4 | numpy==1.23.2 5 | opencv-python >=4.5.4, <4.6 6 | torch==1.10.0 -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/0.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/0.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/1.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/2.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/3.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/4.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/5.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/6.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/7.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/8.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/9.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/90.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/90.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/91.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/91.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/92.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/92.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/93.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/93.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/94.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/94.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/95.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/95.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/96.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/96.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/97.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/97.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/98.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/98.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/val/images/99.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/val/images/99.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/10.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/11.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/12.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/13.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/13.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/14.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/14.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/15.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/16.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/16.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/17.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/17.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/18.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/18.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/19.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/19.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/20.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/20.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/21.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/21.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/22.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/22.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/23.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/23.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/24.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/24.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/25.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/25.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/26.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/26.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/27.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/27.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/28.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/28.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/29.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/29.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/30.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/30.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/31.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/31.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/32.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/32.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/33.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/33.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/34.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/34.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/35.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/35.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/36.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/36.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/37.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/37.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/38.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/38.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/39.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/39.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/40.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/40.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/41.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/41.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/42.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/42.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/43.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/43.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/44.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/44.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/45.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/45.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/46.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/46.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/47.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/47.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/48.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/48.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/49.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/49.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/50.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/50.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/51.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/51.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/52.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/52.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/53.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/53.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/54.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/54.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/55.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/55.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/56.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/56.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/57.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/57.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/58.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/58.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/59.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/59.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/60.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/60.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/61.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/61.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/62.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/62.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/63.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/63.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/64.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/64.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/65.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/65.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/66.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/66.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/67.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/67.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/68.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/68.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/69.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/69.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/70.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/70.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/71.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/71.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/72.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/72.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/73.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/73.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/74.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/74.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/75.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/75.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/76.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/76.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/77.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/77.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/78.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/78.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/79.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/79.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/80.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/80.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/81.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/81.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/82.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/82.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/83.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/83.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/84.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/84.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/85.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/85.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/86.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/86.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/87.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/87.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/88.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/88.jpeg -------------------------------------------------------------------------------- /yolov5/displacement_images/train/images/89.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anexplore/cnn_for_captcha/HEAD/yolov5/displacement_images/train/images/89.jpeg -------------------------------------------------------------------------------- /yolov5/displacement.yaml: -------------------------------------------------------------------------------- 1 | path: displacement_images # dataset root dir 2 | train: train/images # train images (relative to 'path') 3 | val: val/images # val images (relative to 'path') 4 | test: # test images (optional) 5 | 6 | # Classes 7 | nc: 1 # number of classes 8 | names: ['displacement'] # class names 9 | 10 | 11 | # Download script/URL (optional) 12 | download: 13 | -------------------------------------------------------------------------------- /fixed_length_captcha.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_height": 53, 3 | "image_width": 130, 4 | "fixed_length": 4, 5 | "batch_size": 128, 6 | "save_path": "model/model.dat", 7 | "labels": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 8 | "train_image_dir": "train_images", 9 | "validation_image_dir": "validation_images", 10 | "learning_rate": 0.0001, 11 | "dropout_rate": 0.25, 12 | "epochs": 100 13 | } -------------------------------------------------------------------------------- /split_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 将图片分隔成训练集和验证集 4 | """ 5 | import os.path 6 | import random 7 | import shutil 8 | import sys 9 | 10 | 11 | all_image_dir = sys.argv[1] 12 | train_image_dir = sys.argv[2] 13 | validation_image_dir = sys.argv[3] 14 | 15 | if len(sys.argv) > 4: 16 | train_image_ratio = float(sys.argv[4]) 17 | else: 18 | train_image_ratio = 0.95 19 | 20 | all_images = os.listdir(all_image_dir) 21 | all_image_count = len(all_images) 22 | train_image_count = int(all_image_count * train_image_ratio) 23 | validation_image_count = all_image_count - train_image_count 24 | print('total has %s images, %s train images, %s validation images' % (all_image_count, train_image_count, validation_image_count)) 25 | 26 | random = random.Random() 27 | 28 | target_list = all_images[0: validation_image_count] 29 | index = validation_image_count 30 | while index < all_image_count: 31 | r = random.randint(0, validation_image_count - 1) 32 | if r < validation_image_count: 33 | target_list[r] = all_images[index] 34 | index += 1 35 | 36 | for image_name in all_images: 37 | if image_name in target_list: 38 | shutil.copy(os.path.join(all_image_dir, image_name), os.path.join(validation_image_dir, image_name)) 39 | else: 40 | shutil.copy(os.path.join(all_image_dir, image_name), os.path.join(train_image_dir, image_name)) 41 | 42 | print('all done') -------------------------------------------------------------------------------- /image_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 图片处理相关函数 4 | """ 5 | import cv2 6 | 7 | 8 | def rotate_image(src, dst, angle): 9 | """ 10 | 旋转图片 11 | :param src: 原始图片路径 12 | :param dst: 目标存储路径 13 | :param angle: 旋转角度 14 | """ 15 | src_image = cv2.imread(src) 16 | # 获取图像尺寸 17 | (h, w) = src_image.shape[:2] 18 | center = (w / 2, h / 2) 19 | # 执行旋转 20 | M = cv2.getRotationMatrix2D(center, angle, 1.0) 21 | rotated = cv2.warpAffine(src_image, M, (w, h), borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255)) 22 | cv2.imwrite(dst, rotated) 23 | 24 | 25 | def image2jpeg(src, dst): 26 | """ 27 | 将图片转换成jpeg图片 28 | :param src: png图片路径 29 | :param dst: dst图片路径 30 | """ 31 | image = cv2.imread(src) 32 | cv2.imwrite(dst, image, [int(cv2.IMWRITE_JPEG_QUALITY), 100]) 33 | 34 | 35 | def image2gray(src, dst): 36 | """ 37 | 将图片转换成灰度图 38 | """ 39 | image = cv2.imread(src) 40 | image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) 41 | cv2.imwrite(dst, image) 42 | 43 | 44 | def wait_for_destroy_windows(): 45 | cv2.waitKey(0) 46 | cv2.destroyAllWindows() 47 | 48 | 49 | def show_image(name, image): 50 | """ 51 | 展示图片 52 | :param name: window name 53 | :param image: image mat 54 | """ 55 | cv2.namedWindow(name, cv2.WINDOW_NORMAL | cv2.WINDOW_GUI_EXPANDED) 56 | cv2.imshow(name, image) 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .idea -------------------------------------------------------------------------------- /labelme_json_to_yolov5_format.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 将labelme的标注数据转换成yolov5格式 4 | """ 5 | import json 6 | import sys 7 | import os.path 8 | 9 | 10 | def build_class_map(classes_file_path, dest_classes_file_path): 11 | class_index_map = dict() 12 | dest = open(dest_classes_file_path, 'w', encoding='utf8') 13 | with open(classes_file_path, 'r', encoding='utf8') as fd: 14 | index = 0 15 | for line in fd: 16 | line = line.strip() 17 | if line in class_index_map: 18 | continue 19 | class_index_map[line] = index 20 | index += 1 21 | dest.write('%s\n' % line) 22 | dest.close() 23 | return class_index_map 24 | 25 | 26 | labelme_annotation_dir = sys.argv[1] 27 | yolov5_annotation_dir = sys.argv[2] 28 | 29 | if not os.path.exists(yolov5_annotation_dir): 30 | os.mkdir(yolov5_annotation_dir) 31 | 32 | 33 | class_index_map = build_class_map(os.path.join(labelme_annotation_dir, 'classes.txt'), 34 | os.path.join(yolov5_annotation_dir, 'classes.txt')) 35 | file_number = 0 36 | print('...start...') 37 | for filename in os.listdir(labelme_annotation_dir): 38 | if not filename.endswith('.json'): 39 | print('ignore file: %s' % filename) 40 | continue 41 | file_number += 1 42 | fullpath = os.path.join(labelme_annotation_dir, filename) 43 | destpath = os.path.join(yolov5_annotation_dir, '%s.txt' % filename[0:filename.rindex('.')]) 44 | with open(fullpath, 'r', encoding='utf8') as fd: 45 | content = fd.read() 46 | destfile = open(destpath, 'w', encoding='utf8') 47 | data_dict = json.loads(content, encoding='utf8') 48 | shapes = data_dict['shapes'] 49 | image_height = data_dict['imageHeight'] 50 | image_width = data_dict['imageWidth'] 51 | for shape in shapes: 52 | label = shape['label'] 53 | label_index = class_index_map.get(label) 54 | if label_index is None: 55 | raise Exception('%s cannot find index, please check classes.txt' % label) 56 | points = shape['points'] 57 | topx, topy = points[0] 58 | bottomx, bottomy = points[1] 59 | # 归一化到 0-1 xy为边框中心点 wh为边框宽高 60 | x = (topx + bottomx) / 2 / image_width 61 | y = (topy + bottomy) / 2 / image_height 62 | w = (bottomx - topx) / image_width 63 | h = (bottomy - topy) / image_height 64 | destfile.write('%s\t%s\t%s\t%s\t%s\n' % (label_index, x, y, w, h)) 65 | destfile.close() 66 | 67 | print('class number: %s, file number: %s' % (len(class_index_map), file_number)) 68 | print('...end...') -------------------------------------------------------------------------------- /sameobject_captcha.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 |

点击图片相似物体类验证码

4 |
 5 | * 基于YoloV5 object detection实现
 6 | * 需要标注训练数据
 7 | 
8 | """ 9 | import torch 10 | from yolov5.models import experimental 11 | from yolov5.utils import torch_utils 12 | from yolov5.utils import datasets 13 | from yolov5.utils import general 14 | 15 | 16 | class ObjectFinderByYolo(object): 17 | 18 | def __init__(self, confidence_threshold=0.25, iou_threshold=0.4): 19 | """ 20 | :param confidence_threshold: 用于非极大值抑制时的置信度阈值 21 | :param iou_threshold: iou 阈值 22 | """ 23 | self.model = None 24 | self.device = None 25 | self.labels = None 26 | self.confidence_threshold = confidence_threshold 27 | self.iou_threshold = iou_threshold 28 | 29 | def load_models(self, weights_file_path, **kwargs): 30 | """ 31 | 加载模型 32 | 33 | :param weights_file_path: weights文件路径 34 | :param kwargs: 其它控制参数 35 | """ 36 | # 选择设备 37 | device = torch_utils.select_device() 38 | # 加载模型 39 | model = experimental.attempt_load([weights_file_path,], map_location=device) 40 | self.labels = model.names 41 | model.float().eval() 42 | self.model = model 43 | self.device = device 44 | 45 | def detect_object(self, img_path, img_size=None): 46 | """ 47 | 找到提供的所有图片中的符合一定阈值的Object 48 | 49 | :param img_path: 图片路径 可以为单张图片 也可以图片目录 50 | :param img_size: 图片(高 宽) 这里需要和训练模型时传递的图片size参数一致, 可以为单个int 或者 tuple int, 比如 344, (344, 344) 51 | :return: dict() path -> [类别索引,类别,置信度,边框(x, y, w, h)] xy为左上角坐标 52 | """ 53 | stride = max(int(self.model.stride.max()), 32) 54 | imgsz = general.check_img_size(img_size, s=stride) 55 | dataset = datasets.LoadImages(img_path, img_size=imgsz, stride=stride, auto=True) 56 | result = dict() 57 | for path, im, im0s, vid_cap, s in dataset: 58 | im = torch.from_numpy(im).to(self.device) 59 | # uint8 to fp16/32 60 | im = im.float() 61 | # 0 - 255 to 0.0 - 1.0 62 | im /= 255 63 | if len(im.shape) == 3: 64 | # expand for batch 4-dim 65 | im = im[None] 66 | pred = self.model(im, augment=False, visualize=False) 67 | # 非极大值抑制 68 | pred = general.non_max_suppression(pred[0], self.confidence_threshold, self.iou_threshold, None, False, max_det=1000) 69 | # Process predictions 70 | for _, det in enumerate(pred): 71 | if len(det): 72 | # 转换回原始图片尺度 73 | det[:, :4] = general.scale_coords(im.shape[2:], det[:, :4], im0s.shape).round() 74 | for *xyxy, conf, cls in reversed(det): 75 | box = torch.tensor(xyxy).view(1, 4).view(-1).tolist() 76 | box[2] = (box[2] - box[0]) 77 | box[3] = (box[3] - box[1]) 78 | confidence_value = conf.item() 79 | class_index = int(cls.item()) 80 | result[path] = [class_index, self.labels[class_index], confidence_value, box] 81 | """ 82 | print('conf %s, class %s, box: %s' % (confidence_value, class_index, box)) 83 | ann = plots.Annotator(im0s.copy()) 84 | ann.box_label(xyxy, label) 85 | im0 = ann.result() 86 | cv2.imshow('label', im0) 87 | cv2.waitKey(5000) 88 | """ 89 | return result 90 | -------------------------------------------------------------------------------- /slide_captcha.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 滑动验证码相关 4 | """ 5 | import cv2 6 | import numpy as np 7 | 8 | # yolov5 project 9 | try: 10 | import torch 11 | from yolov5.models import experimental 12 | from yolov5.utils import torch_utils 13 | from yolov5.utils import datasets 14 | from yolov5.utils import general 15 | from yolov5.utils import plots 16 | except: 17 | pass 18 | 19 | 20 | def wait_for_destroy_windows(): 21 | cv2.waitKey(0) 22 | cv2.destroyAllWindows() 23 | 24 | 25 | def show_image(name, image): 26 | """ 27 | 展示图片 28 | :param name: window name 29 | :param image: image mat 30 | """ 31 | cv2.namedWindow(name, cv2.WINDOW_NORMAL | cv2.WINDOW_GUI_EXPANDED) 32 | cv2.imshow(name, image) 33 | 34 | 35 | def _process_image(image, blur=False): 36 | """ 37 | 预处理; 部分网站的图片先模糊再求边缘的匹配效果 不如直接求边缘的匹配效果好 38 |
 39 |         模糊
 40 |         求边界
 41 |     
 42 |     :param image: image mat
 43 |     :param blur 是否模糊
 44 |     :return: handle image mat
 45 |     """
 46 |     if blur:
 47 |         image = cv2.GaussianBlur(image, (5, 5), 0)
 48 |     return cv2.Canny(image, 50, 150)
 49 | 
 50 | 
 51 | def _read_image_from_local_file(image_path, image_scale=cv2.IMREAD_GRAYSCALE):
 52 |     with open(image_path, 'rb') as fd:
 53 |         content = fd.read()
 54 |     return cv2.imdecode(np.frombuffer(content, dtype=np.uint8), image_scale)
 55 | 
 56 | 
 57 | def _read_image_from_bytes(image_bytes, image_scale=cv2.IMREAD_GRAYSCALE):
 58 |     if not isinstance(image_bytes, bytes):
 59 |         raise RuntimeError('image bytes must be bytes type')
 60 |     return cv2.imdecode(image_bytes, image_scale)
 61 | 
 62 | 
 63 | def detect_displacement(image_slider, image_background, blur=False, display_image=True):
 64 |     """
 65 |     探测缺口偏移量
 66 |     :param image_slider: 缺口图 numpy.ndarray or image file path
 67 |     :param image_background: 底图 numpy.ndarray or image file path
 68 |     :param blur: 预处理时是否模糊图片
 69 |     :param display_image: 展示图片
 70 |     :return: top_left_x, top_left_y, width, height
 71 |     """
 72 |     if isinstance(image_slider, str):
 73 |         image_slider = _read_image_from_local_file(image_slider)
 74 |     if isinstance(image_background, str):
 75 |         image_background = _read_image_from_local_file(image_background)
 76 |     processed_image_slider = _process_image(image_slider, blur=blur)
 77 |     processed_image_background = _process_image(image_background, blur=blur)
 78 |     # match
 79 |     res = cv2.matchTemplate(processed_image_slider, processed_image_background, cv2.TM_CCOEFF_NORMED)
 80 |     _, _, _, max_location = cv2.minMaxLoc(res)
 81 |     # pos
 82 |     x, y = max_location
 83 |     # height width
 84 |     h, w = image_slider.shape
 85 |     # draw match
 86 |     cv2.rectangle(image_background, (x, y), (x + w, y + h), (255, 255, 255), 2)
 87 |     if display_image:
 88 |         show_image("processed_image_slider", processed_image_slider)
 89 |         show_image("processed_image_background", processed_image_background)
 90 |         show_image("match", image_background)
 91 |         wait_for_destroy_windows()
 92 |     return x, y, w, h
 93 | 
 94 | """
 95 | 基于YOLO的方法定位缺口需要标注数据且进行训练 时间成本相对较高
 96 | 一般情况下使用cv2.matchTemplate基本就能框定出缺口的位置
 97 | 使用 [yolo v5](https://github.com/ultralytics/yolov5) 进行训练
 98 | 基于yolo v5中的detect.py改造自己的检查函数即可
 99 | """
100 | 
101 | 
102 | class DisplacementFinderByYolo(object):
103 | 
104 |     def __init__(self):
105 |         self.model = None
106 |         self.device = None
107 | 
108 |     def load_models(self, weights_file_path, **kwargs):
109 |         """
110 |         加载模型
111 |         :param weights_file_path: weights文件路径
112 |         :param kwargs: 其它控制参数
113 |         """
114 |         # 选择设备
115 |         device = torch_utils.select_device()
116 |         # 加载模型
117 |         model = experimental.attempt_load([weights_file_path,], map_location=device)
118 |         model.float().eval()
119 |         self.model = model
120 |         self.device = device
121 | 
122 |     def detect_displacement(self, img_path, img_size=None):
123 |         """
124 |         基于yolo v5中detect.py的run函数改造此函数即可
125 |         方法一: 直接调用detect.run()方法并设置结果写出到txt文件 通过读取txt文件解析结果(此方法每次调用都需要重新加载模型 适合一次性大批量处理)
126 |         方法二: 复用detect.run中代码,将模型加载放到 self._load_models中 将探测代码放到 detect_displacement中
127 |         :param img_path: 图片路径
128 |         :param img_size: 图片(高 宽) 这里需要和训练模型时传递的图片size参数一致
129 |         :return: 类别,置信度,边框(x, y, w, h) x,y是左上角坐标
130 |         """
131 |         stride = max(int(self.model.stride.max()), 32)
132 |         imgsz = general.check_img_size(img_size, s=stride)
133 |         dataset = datasets.LoadImages(img_path, img_size=imgsz, stride=stride, auto=True)
134 | 
135 |         for path, im, im0s, vid_cap, s in dataset:
136 |             im = torch.from_numpy(im).to(self.device)
137 |             # uint8 to fp16/32
138 |             im = im.float()
139 |             # 0 - 255 to 0.0 - 1.0
140 |             im /= 255
141 |             if len(im.shape) == 3:
142 |                 # expand for batch 4-dim
143 |                 im = im[None]
144 |             pred = self.model(im, augment=False, visualize=False)
145 |             # 非极大值抑制
146 |             pred = general.non_max_suppression(pred[0], 0.25, 0.4, None, False, max_det=1000)
147 |             # Process predictions
148 |             # per image
149 |             for _, det in enumerate(pred):
150 |                 if len(det):
151 |                     # process result
152 |                     # 转换回原始图片尺度
153 |                     det[:, :4] = general.scale_coords(im.shape[2:], det[:, :4], im0s.shape).round()
154 |                     for *xyxy, conf, cls in reversed(det):
155 |                         box = torch.tensor(xyxy).view(1, 4).view(-1).tolist()
156 |                         box[2] = (box[2] - box[0])
157 |                         box[3] = (box[3] - box[1])
158 |                         confidence_value = conf.item()
159 |                         class_index = cls.item()
160 |                         return int(class_index), confidence_value, box
161 | 
162 |                     #print('conf %s, class %s, box: %s' % (confidence_value, class_index, box))
163 |                     """
164 |                     ann = plots.Annotator(im0s.copy())
165 |                     ann.box_label(xyxy, 'dis')
166 |                     im0 = ann.result()
167 |                     cv2.imshow('dis', im0)
168 |                     cv2.waitKey(5000)
169 |                     """
170 |         return None, None, None
171 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | # cnn_for_captcha
  2 | 
  3 | ~~~diff
  4 | - 随着文本、语音、视觉等大模型能力的迭代更新,机器理解能力突飞猛进,希望能够加速验证码技术的消亡
  5 | + 希望大模型的能力不要助长设计出更加反人类的验证码
  6 | ~~~
  7 | 
  8 | **基于深度学习的图片验证码识别**
  9 | 
 10 | **建议1: 尝试此项目中的方法之前,首先确认能不能规避验证码,如果大部分情况下都能规避,就别看此项目了**
 11 | 
 12 | **建议2: 尝试此项目中的方法之前,首先确认验证码是不是暴力枚举就能把所有情况都列举出来**
 13 | 
 14 | ****
 15 | ## 1. 固定长度的文字验证码识别 
 16 | [fixed_length_captcha.py](fixed_length_captcha.py)
 17 | 
 18 | ### 依赖
 19 | _**requirements.txt 这里面比较全可按需安装**_
 20 | 
 21 | ### 1.1 输入要求
 22 | * 将训练集和验证集分别放到配置文件指定的目录中
 23 | * 目录中所有图片尺寸相同
 24 | * 图片命名规则 验证码_编号.图片格式, 举例 **abce_012312.jpg**
 25 | 
 26 | ### 1.2 配置文件
 27 | * 默认文件 fixed_length_captcha.json
 28 | * 字段见文知义
 29 | 
 30 | ### 1.3 训练
 31 | ~~~ python
 32 | python fixed_length_captcha.py
 33 | ~~~
 34 | 
 35 | ### 1.4 预测
 36 | ~~~ python
 37 | predictor = Predictor()
 38 | # 预测本地磁盘文件
 39 | predictor.predict('xxx.jpg')
 40 | # 直接二进制内容预测
 41 | predictor.predict_single_image_content(b'PNGxxxxx')
 42 | # 预测远程图片
 43 | predictor.predict_remote_image('http://xxxxxx/xx.jpg', save_image_to_file='remote.jpg')
 44 | ~~~
 45 | 
 46 | ### 1.5 效果
 47 | * 根训练集样本大小有关
 48 | * ![image](images/4x4e_11039.png) 这种图片2w张左右的训练集训练后实际能达到90%以上的准确率
 49 | 
 50 | ****
 51 | 
 52 | ## 2. 滑动验证码 
 53 | [slide_captcha.py](slide_captcha.py)
 54 | 
 55 | 提供滑动验证码相关解决方法与思路
 56 | ### 2.1 基于opencv2的match template
 57 | 此方法简单易于验证, 配合一些规则即可达到满意效果
 58 | ~~~python
 59 | import slide_captcha
 60 | slide_captcha.detect_displacement('image_slider.jpg', 'image_background.jpg')
 61 | ~~~
 62 | 以下是match template的效果
 63 | 
 64 | ![match template](images/matchtemplate.png)
 65 | 
 66 | ### 2.2 基于YOLOv5的检测
 67 | 此方法需要标注数据以及进行训练但相对于模板探测是更稳定的通用方案
 68 | 
 69 | 基于 [yolo v5](https://github.com/ultralytics/yolov5) 进行模型的训练与缺口探测
 70 | 
 71 | 训练方法参考 [train custom data](https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data)
 72 | ~~~text
 73 | 本项目中yolov5中提供了100张标注好的图片;
 74 | 参数:
 75 | batch-size 根据可用内存或者显存以及训练效果进行调整
 76 | epochs 根据训练效果来定
 77 | yolov5s.yaml在yolov5项目的models下面
 78 | img 图片缩放基准 建议用图片的宽或者高即可(需要考虑图片大小 如果图片过大建议调低此值)
 79 | weights 设置预训练模型 没有则为空即可 推荐使用预训练yolov5s.pt
 80 | 
 81 | python train.py --batch-size 4 --epochs 200 --img 344 --data displacement.yaml --weights '' --cfg yolov5s.yaml
 82 | ~~~
 83 | 
 84 | 探测方法参考 [detect.py](https://github.com/ultralytics/yolov5/blob/master/detect.py) 中run方法 或者 使用slide_captcha.py中方法
 85 | ~~~python
 86 | import slide_captcha
 87 | detector = slide_captcha.DisplacementFinderByYolo()
 88 | detector.load_models('best.pt')
 89 | detector.detect_displacement('image.jpg', 344)
 90 | ~~~
 91 | 
 92 | 
 93 | 下面是通过标注100张图片并经过训练得到的模型的探测效果
 94 | 
 95 | ![yolov5](images/yolodetect.jpeg)
 96 | 
 97 | ****
 98 | 
 99 | ## 3. 点选文字验证码
100 | 文字点选类验证码一般是从图片中按照指定顺序选择目标文字
101 | 
102 | ![textclick](images/textclick.png)
103 | 
104 | 目标文字一般通过 "文字" 或者 "文字图片" 的方式给出
105 | 
106 | ### 3.1 位置标定
107 | 1) 使用yolo类目标识别算法从图片中将候选文字所在的位置框标定出来 
108 | 2) 如果目标文字是通过"文字图片"的方式给出那么也需要使用yolo等将其从图片中标定出来
109 | 
110 | ### 3.2 匹配
111 | 匹配是相对比较难的地方,需要根据不同情况进行实际的分析
112 | * 如果图片中的文字能够再标定后通过ocr识别出来那么比较容易解决,直接用识别后的字符匹配即可。可用的ocr识别
113 | [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR) /
114 | [tesseract](https://github.com/tesseract-ocr/tesseract) /
115 | [cnocr](https://github.com/breezedeus/cnocr)
116 | 
117 | * 很多验证码文字都进行的变形、加粗等处理,因此OCR不能有效的进行识别
118 |    - 1 如果目标文字也是以图片形式提供,可以考虑使用CNN训练一个模型用来判定两张输入图片是否为同一汉字。可以参考[孪生神经网络 Siamese Neural Networks](https://www.cs.cmu.edu/~rsalakhu/papers/oneshot1.pdf)
119 |    - 2 如果候选文字的范围有限,比如几百或者几千,可以考虑直接使用yolo等给出类别
120 |    - 3 如果目标文字是通过文本的形式给出,那么可以考虑将文本转成图片,然后用 1 中的方式训练判别模型
121 | 
122 | ****
123 | 
124 | ## 4. 旋转类验证码 rotate_captcha.py
125 | 旋转类验证码要求将经过旋转后的图片旋转会正确的位置;
126 | 
127 | ![rotate](images/rotate.jpeg)
128 | 
129 | ### 4.1 思路一 暴力破解 (某些网站验证码原图就百张左右)
130 | 如果能够"脱库"(图库非无限大),那么可以人肉标定“正”的图片;然后一般旋转类图片旋转角度单为10-30度左右,可以自己旋转生成不同角度的图片;
131 | 
132 | 拿到待旋转图片以后,通过图片相似度找到最相似图片得到其角度,从而计算出需要旋转的角度
133 | 
134 | 图片相似度计算可以使用ResNet等网络提取图片特征,计算余弦距离来对比
135 | 
136 | ### 4.2 思路二 深度学习训练
137 | 深度学习训练有两种思路,分别是回归和分类;
138 | 
139 | 看作回归问题,那么就是训练模型的产出是一个旋转角度;
140 | 
141 | 看作分类问题,训练输出就是一个旋转角度类型预测(比如按照30度进行旋转,分为12类,预测属于哪一类),
142 | 或者看作0/1分类, 0就是"不正图片", 1就是"正"图片 
143 | 
144 | **验证0/1分类:** 此方法对一张图片来说正的图片只有一张,而其它旋转角度有N张,造成验证的训练数据不均衡问题,实际效果较差
145 | 
146 | **验证回归测算旋转角度:** 此方法优于0/1分类
147 | 
148 | **验证角度分类** 未尝试
149 | 
150 | **rotate_captcha.py** 中将旋转看作了回归问题, 使用ResNet50进行特征的提取;
151 | 
152 | **[PULC 含文字图像方向分类模型](https://github.com/PaddlePaddle/PaddleClas/blob/develop/docs/zh_CN/PULC/PULC_text_image_orientation.md)** 这个paddlepaddle中实现文字图片旋转矫正的模型或许对我们能够有所启发
153 | 
154 | 
155 | ### 4.x tips
156 | * 从目标网站验证码界面下载旋转验证码图片,这时会遇到同一张图片的N中不同旋转角度的图片,此时可以基于
157 | [imagededup](https://github.com/idealo/imagededup) 模块中的基于CNN特征的find_duplicates方法实现相似图片的匹配
158 | 
159 | * 图片特征的提取可以尝试keras.application下面的其它模型; 可尝试自定义loss函数替代MSE
160 | 
161 | ****
162 | 
163 | ## 5. 点击相似物体验证码
164 | 此类验证码一般是让点击图片中相同或者相似的物体,一般物体的种类在可控范围内,因此可以尝试使用物体识别算法将图片中物体种类识别出来
165 | 然后进行比较
166 | 
167 | [sameobject_captcha.py](sameobject_captcha.py) 基于yolov5实现物体探测
168 | 
169 | **原图:**
170 | 
171 | ![sameobject](images/sameobject.jpg)
172 | 
173 | **结果:**
174 | 
175 | ![sameobjectresult](images/sameobjectresult.jpg)
176 | 
177 | ### 5.1 训练
178 | 至少准备200+张标注过的图片, 图片越多越准确
179 | 
180 | 训练方法参考  [yolov5 train custom data](https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data)
181 | 
182 | > 如果用labelme标注可以使用[labelme_json_to_yolov5_format.py](labelme_json_to_yolov5_format.py)转换格式
183 | 
184 | ### 5.2 结果
185 | 此项目中效果是300张标注数据在训练100轮后的结果
186 | 
187 | 此项目中的物体是部分字母、数字、三角锥等,从结果中可以看到h/r, C/G, U和圆柱体等识别混乱或者有的识别错误,增加训练集数量应该可以降低错误率
188 | 
189 | ![trainresult](images/sameobjecttrain.jpg)
190 | 
191 | ****
192 | 
193 | ## 6. 大模型的识别
194 | 尝试考虑使用图片大模型进行识别。
195 | 
196 | **个人感觉:使用视觉等多模态大模型经过指令微调后应该会取得比较好的识别效果,可以做学术研究探讨。但是考虑到大模型本身对算力资源、训练物料等要求,实际成本会比较高。**
197 | **另外,目前识别速度普遍偏慢。**
198 | **大模型是一种可能方案,但不一定是最优的方案。需要综合考虑成本、速度等方面。**
199 | 
200 | **虽然现在大模型OCR识别能力有些弱,相信最终还是大模型吊打牛鬼蛇神**
201 | 
202 | > Prompt提示优化
203 | > 
204 | > 指令微调
205 | 
206 | 提示词:
207 | >you are an ocr tool. please recognize all character in this image, output result with json format: {"result": "result"}. 
208 | >Pay attention if you cannot find any character in this image, you should output {"result": ""}.
209 | >Please just output json format, no explains. 
210 | 
211 | ### 通用非OCR专用大模型识别能力论文
212 | [Exploring OCR Capabilities of GPT-4V(ision) : A Quantitative and In-depth Evaluation](https://arxiv.org/abs/2401.00028)
213 | 
214 | [CogAgent: A Visual Language Model for GUI Agents](https://arxiv.org/abs/2312.08914)
215 | 
216 | 
217 | ### 6.1 Google Gemini
218 | #### 简单验证码
219 | 
220 | ![simple](images/llm_simple.png)
221 | 
222 | ![simple1](images/llm_simple1.png)
223 | 
224 | #### 复杂验证码
225 | 对复杂文字验证码的识别效果不佳,可能经过微调训练能够实现更好的效果,但是需要考虑训练、部署、时间等成本投入以及识别速度
226 | 
227 | ![hanzi](images/llm_hanzi.png)
228 | 
229 | ![hanzi1](images/llm_hanzi1.png)
230 | 
231 | ### 6.2 Bing Copilot
232 | 
233 | **简单验证码**
234 | 
235 | ![bing copilot simple](images/bingcopilot_simple.png)
236 | 
237 | ![bing copilot simple1](images/bingcopilot_simple1.png)
238 | 
239 | ![bing copilot simple2](images/bingcopilot_simple2.png)
240 | 
241 | **复杂验证码**
242 | 
243 | ![bing copilot hangzi](images/bingcopilot_hanzi.png)
244 | 
245 | ### 6.3 OpenAI Gpt4o
246 | **目前表现最好的多模态模型**
247 | #### 实体识别
248 | ![gpt4o-6](images/gpt4o-6.png)
249 | 
250 | #### 文字验证码
251 | ![gpt4-4](images/gpt4o-4.png)
252 | 
253 | ![gpt42](images/gpt40-2.png)
254 | 
255 | ![gpt4o-5](images/gpt4o-5.png)
256 | 
257 | #### 简单验证码
258 | ![gpt41](images/gp4o-1.png)
259 | 
260 | ![gpt40-3](images/gpt40-3.png)
261 | 
262 | 
263 | ### 6.4 Claude 3.5
264 | #### 实体识别
265 | 
266 | ![claudeidentity](images/claudehard.png)
267 | 
268 | #### 文字验证码
269 | 
270 | ![claudehard](images/claudehard1.png)
271 | 
272 | #### 简单验证码
273 | ![claudesimple1](images/claudesimple1.png)
274 | 
275 | ![claudesimple2](images/claudesimple2.png)
276 | 
277 | ## X.其它
278 | ### X.1 图片数据切分
279 | 将准备好的图片按照比例切分成训练集和验证集
280 | ~~~ python
281 | python split_data.py all_image_dir train_image_dir validation_image_dir 0.9
282 | ~~~
283 | 参数:
284 | * all_image_dir 准备好的图片目录
285 | * train_image_dir 训练集图片目录
286 | * validation_image_dir 验证集图片目录
287 | * 0.9 训练集比例
288 | 
289 | 以上目录需要提前创建
290 | 
291 | ### X.3 深度学习训练资源
292 | * 推荐直接使用阿里云/腾讯云等平台上的GPU按量计费资源,可在有限时间和费用的前提大最大化机器配置加快训练
293 | 


--------------------------------------------------------------------------------
/rotate_captcha.py:
--------------------------------------------------------------------------------
  1 | # -*- coding: utf-8 -*-
  2 | """
  3 | 图片旋转验证码识别
  4 | 训练集图片命名格式: index_angle.jpeg/index.jpeg
  5 | """
  6 | import os
  7 | import os.path
  8 | import time
  9 | 
 10 | import cv2
 11 | 
 12 | import numpy as np
 13 | import keras_preprocessing.image
 14 | import tensorflow as tf
 15 | from tensorflow import keras
 16 | from keras import backend
 17 | from keras import layers
 18 | from keras import applications
 19 | from keras import models
 20 | 
 21 | 
 22 | def rotate_image(src, dst, angle):
 23 |     """
 24 |     旋转图片
 25 |     :param src: 原始图片路径
 26 |     :param dst: 目标存储路径
 27 |     :param angle: 旋转角度
 28 |     """
 29 |     src_image = cv2.imread(src)
 30 |     # 获取图像尺寸
 31 |     (h, w) = src_image.shape[:2]
 32 |     center = (w / 2, h / 2)
 33 |     # 执行旋转
 34 |     M = cv2.getRotationMatrix2D(center, angle, 1.0)
 35 |     rotated = cv2.warpAffine(src_image, M, (w, h), borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255))
 36 |     cv2.imwrite(dst, rotated)
 37 | 
 38 | 
 39 | def load_image_data(image_dir_path, image_height, image_width, image_channel=3):
 40 |     """
 41 |     加载图片数据
 42 |     图片标签从图片文件名中读取 图片文件名应该符合 index_angle.jpg(png)格式
 43 |     RGB图片将会转换成灰度图片
 44 |     :param image_dir_path: 图片路径
 45 |     :param image_height: 目标图片高度
 46 |     :param image_width: 目标图片宽度
 47 |     :param image_channel 目标图片通道
 48 |     :return: image_data, data_label
 49 |     """
 50 |     image_name_list = os.listdir(image_dir_path)
 51 |     image_data = np.zeros(shape=(len(image_name_list), image_height, image_width, image_channel))
 52 |     label_data = np.zeros(shape=(len(image_name_list), 1))
 53 |     color_mode = 'rgb' if image_channel == 3 else 'grayscale'
 54 |     for index, image_name in enumerate(image_name_list):
 55 |         img = keras_preprocessing.image.utils.load_img(os.path.join(image_dir_path, image_name), color_mode=color_mode,
 56 |                                                        target_size=(image_height, image_width))
 57 |         x = keras_preprocessing.image.utils.img_to_array(img)
 58 |         if hasattr(img, 'close'):
 59 |             img.close()
 60 |         image_data[index] = x
 61 |         image_name_with_suffix = image_name[0:image_name.rfind('.')]
 62 |         fields = image_name_with_suffix.split('_')
 63 |         if len(fields) == 1:
 64 |             label_data[index] = 0.0
 65 |         elif len(fields) == 2:
 66 |             label_data[index] = float(fields[1]) / 360
 67 |     return image_data, label_data
 68 | 
 69 | 
 70 | class DirectoryImageGenerator(keras_preprocessing.image.Iterator):
 71 | 
 72 |     def _get_batches_of_transformed_samples(self, index_array):
 73 |         image_data = np.zeros(shape=(len(index_array), self.image_height, self.image_width, self.image_channel))
 74 |         label_data = np.zeros(shape=(len(index_array), 1))
 75 |         color_mode = 'rgb' if self.image_channel == 3 else 'grayscale'
 76 |         for index, image_index in enumerate(index_array):
 77 |             image_path = self.filter_images[image_index]
 78 |             image = keras_preprocessing.image.utils.load_img(image_path, color_mode=color_mode,
 79 |                                                              target_size=(self.image_height, self.image_width))
 80 |             x = keras_preprocessing.image.utils.img_to_array(image)
 81 |             if hasattr(image, 'close'):
 82 |                 image.close()
 83 |             image_data[index] = x
 84 |             image_name = image_path.split(os.sep)[-1]
 85 |             image_name = image_name[0: image_name.rfind('.')]
 86 |             name_fields = image_name.split('_')
 87 |             if len(name_fields) == 1:
 88 |                 label_data[index] = 0.0
 89 |             elif len(name_fields) == 2:
 90 |                 label_data[index] = float(name_fields[1]) / 360
 91 |             else:
 92 |                 raise RuntimeError('image name must in formats base_angle.suffix')
 93 |         return image_data, label_data
 94 | 
 95 |     def __init__(self, directory_path, image_height, image_width, image_channel, batch_size, shuffle=True, seed=0, image_suffix=None):
 96 |         if not image_suffix:
 97 |             image_suffix = ['.png', '.jpg', '.jpeg', '.bmp', '.ppm', '.tif', '.tiff']
 98 |         if not os.path.exists(directory_path) or not os.path.isdir(directory_path):
 99 |             raise RuntimeError('directory must exist')
100 |         if image_channel not in (1, 3):
101 |             raise RuntimeError('image channel must be 1 or 3')
102 |         names = os.listdir(directory_path)
103 |         self.filter_images = []
104 |         for name in names:
105 |             name_lower = name.lower()
106 |             for suffix in image_suffix:
107 |                 if name_lower.endswith(suffix):
108 |                     self.filter_images.append(os.path.join(directory_path, name))
109 |                     break
110 |         self.size = len(self.filter_images)
111 |         if self.size == 0:
112 |             raise RuntimeError('there is no image in %s' % directory_path)
113 |         self.image_height = image_height
114 |         self.image_width = image_width
115 |         self.image_channel = image_channel
116 |         super(DirectoryImageGenerator, self).__init__(self.size, batch_size, shuffle, seed)
117 | 
118 | 
119 | def angle_difference(x, y):
120 |     """
121 |     Calculate minimum difference between two angles.
122 |     """
123 |     return 180 - abs(abs(x - y) - 180)
124 | 
125 | 
126 | def angle_error_regression(y_true, y_pred):
127 |     """
128 |     Calculate the mean difference between the true angles
129 |     and the predicted angles.
130 |     """
131 |     return backend.mean(angle_difference(y_true * 360, y_pred * 360))
132 | 
133 | 
134 | class RotateImageCaptcha(object):
135 | 
136 |     def __init__(self, image_height, image_width):
137 |         self.image_height = image_height
138 |         self.image_width = image_width
139 |         self.image_channel = 3
140 |         self.learning_rate = 0.001
141 | 
142 |     def model(self, model_path=None):
143 |         """
144 |         ResNet50 + Flatten + 1 FC
145 |         """
146 |         input_tensor = keras.Input(shape=(self.image_height, self.image_width, self.image_channel), batch_size=None)
147 |         input_tensor = applications.resnet.preprocess_input(input_tensor, data_format='channels_last')
148 |         net = applications.resnet.ResNet50(include_top=False, weights='imagenet', input_tensor=input_tensor, pooling='max')
149 |         x = net.output
150 |         # flatten
151 |         x = layers.Flatten()(x)
152 |         x = layers.Dense(units=36, activation='relu')(x)
153 |         # full connection
154 |         x = layers.Dense(units=1, activation="sigmoid")(x)
155 |         model = models.Model(inputs=net.inputs, outputs=x, name="rotateresnet50")
156 |         model.compile(optimizer=keras.optimizers.Adam(learning_rate=self.learning_rate),
157 |                       loss=angle_error_regression,
158 |                       metrics=["mean_squared_error"])
159 |         if model_path and os.path.exists(model_path):
160 |             model.load_weights(model_path)
161 |         return model
162 | 
163 | 
164 | def test(image_path, model_path='rotatemodel/model.h5'):
165 |     image_height = 224
166 |     image_width = 224
167 |     image_channel = 3
168 |     color_mode = 'rgb' if image_channel == 3 else 'grayscale'
169 |     model = RotateImageCaptcha(image_height, image_width).model(model_path)
170 |     data = np.full(shape=(1, image_height, image_width, image_channel), fill_value=-1, dtype='float32')
171 |     img = keras_preprocessing.image.utils.load_img(image_path, color_mode=color_mode, target_size=(image_height, image_width))
172 |     image_array = keras_preprocessing.image.utils.img_to_array(img)
173 |     data[0] = image_array
174 |     if hasattr(img, 'close'):
175 |         img.close()
176 |     data = applications.resnet.preprocess_input(data, data_format='channels_last')
177 |     result = model.predict(data)
178 |     result = tf.reshape(result, [-1])
179 |     np_result = keras.backend.eval(result)
180 |     print(np_result)
181 | 
182 | 
183 | def train(train_data_dir, validation_data_dir, epochs=10, model_path='rotatemodel/model.h5'):
184 |     # load data
185 |     image_height = 224
186 |     image_width = 224
187 |     image_channel = 3
188 |     # 根据图片大小 可用显存调整
189 |     batch_size = 72
190 |     callbacks = [
191 |         keras.callbacks.ModelCheckpoint(filepath=model_path)
192 |     ]
193 |     captcha = RotateImageCaptcha(image_height, image_width)
194 |     captcha.image_channel = image_channel
195 |     model = captcha.model(model_path)
196 |     model.summary()
197 |     # generator
198 |     generator = DirectoryImageGenerator(train_data_dir, image_height, image_width, image_channel,
199 |                                         batch_size=batch_size,
200 |                                         seed=int(time.monotonic()))
201 |     validation_generator = DirectoryImageGenerator(validation_data_dir, image_height, image_width, image_channel,
202 |                                         batch_size=batch_size,
203 |                                         seed=int(time.monotonic()) + 1)
204 |     # train
205 |     model.fit(x=generator, validation_data=validation_generator, epochs=epochs,
206 |               callbacks=callbacks, steps_per_epoch=generator.size//batch_size, validation_steps=2)
207 | 
208 | 
209 | if __name__ == '__main__':
210 |     #test('rotate_data/2002_60.jpeg')
211 |     train('train/', 'test/')
212 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
  1 |                                  Apache License
  2 |                            Version 2.0, January 2004
  3 |                         http://www.apache.org/licenses/
  4 | 
  5 |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 | 
  7 |    1. Definitions.
  8 | 
  9 |       "License" shall mean the terms and conditions for use, reproduction,
 10 |       and distribution as defined by Sections 1 through 9 of this document.
 11 | 
 12 |       "Licensor" shall mean the copyright owner or entity authorized by
 13 |       the copyright owner that is granting the License.
 14 | 
 15 |       "Legal Entity" shall mean the union of the acting entity and all
 16 |       other entities that control, are controlled by, or are under common
 17 |       control with that entity. For the purposes of this definition,
 18 |       "control" means (i) the power, direct or indirect, to cause the
 19 |       direction or management of such entity, whether by contract or
 20 |       otherwise, or (ii) ownership of fifty percent (50%) or more of the
 21 |       outstanding shares, or (iii) beneficial ownership of such entity.
 22 | 
 23 |       "You" (or "Your") shall mean an individual or Legal Entity
 24 |       exercising permissions granted by this License.
 25 | 
 26 |       "Source" form shall mean the preferred form for making modifications,
 27 |       including but not limited to software source code, documentation
 28 |       source, and configuration files.
 29 | 
 30 |       "Object" form shall mean any form resulting from mechanical
 31 |       transformation or translation of a Source form, including but
 32 |       not limited to compiled object code, generated documentation,
 33 |       and conversions to other media types.
 34 | 
 35 |       "Work" shall mean the work of authorship, whether in Source or
 36 |       Object form, made available under the License, as indicated by a
 37 |       copyright notice that is included in or attached to the work
 38 |       (an example is provided in the Appendix below).
 39 | 
 40 |       "Derivative Works" shall mean any work, whether in Source or Object
 41 |       form, that is based on (or derived from) the Work and for which the
 42 |       editorial revisions, annotations, elaborations, or other modifications
 43 |       represent, as a whole, an original work of authorship. For the purposes
 44 |       of this License, Derivative Works shall not include works that remain
 45 |       separable from, or merely link (or bind by name) to the interfaces of,
 46 |       the Work and Derivative Works thereof.
 47 | 
 48 |       "Contribution" shall mean any work of authorship, including
 49 |       the original version of the Work and any modifications or additions
 50 |       to that Work or Derivative Works thereof, that is intentionally
 51 |       submitted to Licensor for inclusion in the Work by the copyright owner
 52 |       or by an individual or Legal Entity authorized to submit on behalf of
 53 |       the copyright owner. For the purposes of this definition, "submitted"
 54 |       means any form of electronic, verbal, or written communication sent
 55 |       to the Licensor or its representatives, including but not limited to
 56 |       communication on electronic mailing lists, source code control systems,
 57 |       and issue tracking systems that are managed by, or on behalf of, the
 58 |       Licensor for the purpose of discussing and improving the Work, but
 59 |       excluding communication that is conspicuously marked or otherwise
 60 |       designated in writing by the copyright owner as "Not a Contribution."
 61 | 
 62 |       "Contributor" shall mean Licensor and any individual or Legal Entity
 63 |       on behalf of whom a Contribution has been received by Licensor and
 64 |       subsequently incorporated within the Work.
 65 | 
 66 |    2. Grant of Copyright License. Subject to the terms and conditions of
 67 |       this License, each Contributor hereby grants to You a perpetual,
 68 |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 69 |       copyright license to reproduce, prepare Derivative Works of,
 70 |       publicly display, publicly perform, sublicense, and distribute the
 71 |       Work and such Derivative Works in Source or Object form.
 72 | 
 73 |    3. Grant of Patent License. Subject to the terms and conditions of
 74 |       this License, each Contributor hereby grants to You a perpetual,
 75 |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 76 |       (except as stated in this section) patent license to make, have made,
 77 |       use, offer to sell, sell, import, and otherwise transfer the Work,
 78 |       where such license applies only to those patent claims licensable
 79 |       by such Contributor that are necessarily infringed by their
 80 |       Contribution(s) alone or by combination of their Contribution(s)
 81 |       with the Work to which such Contribution(s) was submitted. If You
 82 |       institute patent litigation against any entity (including a
 83 |       cross-claim or counterclaim in a lawsuit) alleging that the Work
 84 |       or a Contribution incorporated within the Work constitutes direct
 85 |       or contributory patent infringement, then any patent licenses
 86 |       granted to You under this License for that Work shall terminate
 87 |       as of the date such litigation is filed.
 88 | 
 89 |    4. Redistribution. You may reproduce and distribute copies of the
 90 |       Work or Derivative Works thereof in any medium, with or without
 91 |       modifications, and in Source or Object form, provided that You
 92 |       meet the following conditions:
 93 | 
 94 |       (a) You must give any other recipients of the Work or
 95 |           Derivative Works a copy of this License; and
 96 | 
 97 |       (b) You must cause any modified files to carry prominent notices
 98 |           stating that You changed the files; and
 99 | 
100 |       (c) You must retain, in the Source form of any Derivative Works
101 |           that You distribute, all copyright, patent, trademark, and
102 |           attribution notices from the Source form of the Work,
103 |           excluding those notices that do not pertain to any part of
104 |           the Derivative Works; and
105 | 
106 |       (d) If the Work includes a "NOTICE" text file as part of its
107 |           distribution, then any Derivative Works that You distribute must
108 |           include a readable copy of the attribution notices contained
109 |           within such NOTICE file, excluding those notices that do not
110 |           pertain to any part of the Derivative Works, in at least one
111 |           of the following places: within a NOTICE text file distributed
112 |           as part of the Derivative Works; within the Source form or
113 |           documentation, if provided along with the Derivative Works; or,
114 |           within a display generated by the Derivative Works, if and
115 |           wherever such third-party notices normally appear. The contents
116 |           of the NOTICE file are for informational purposes only and
117 |           do not modify the License. You may add Your own attribution
118 |           notices within Derivative Works that You distribute, alongside
119 |           or as an addendum to the NOTICE text from the Work, provided
120 |           that such additional attribution notices cannot be construed
121 |           as modifying the License.
122 | 
123 |       You may add Your own copyright statement to Your modifications and
124 |       may provide additional or different license terms and conditions
125 |       for use, reproduction, or distribution of Your modifications, or
126 |       for any such Derivative Works as a whole, provided Your use,
127 |       reproduction, and distribution of the Work otherwise complies with
128 |       the conditions stated in this License.
129 | 
130 |    5. Submission of Contributions. Unless You explicitly state otherwise,
131 |       any Contribution intentionally submitted for inclusion in the Work
132 |       by You to the Licensor shall be under the terms and conditions of
133 |       this License, without any additional terms or conditions.
134 |       Notwithstanding the above, nothing herein shall supersede or modify
135 |       the terms of any separate license agreement you may have executed
136 |       with Licensor regarding such Contributions.
137 | 
138 |    6. Trademarks. This License does not grant permission to use the trade
139 |       names, trademarks, service marks, or product names of the Licensor,
140 |       except as required for reasonable and customary use in describing the
141 |       origin of the Work and reproducing the content of the NOTICE file.
142 | 
143 |    7. Disclaimer of Warranty. Unless required by applicable law or
144 |       agreed to in writing, Licensor provides the Work (and each
145 |       Contributor provides its Contributions) on an "AS IS" BASIS,
146 |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 |       implied, including, without limitation, any warranties or conditions
148 |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 |       PARTICULAR PURPOSE. You are solely responsible for determining the
150 |       appropriateness of using or redistributing the Work and assume any
151 |       risks associated with Your exercise of permissions under this License.
152 | 
153 |    8. Limitation of Liability. In no event and under no legal theory,
154 |       whether in tort (including negligence), contract, or otherwise,
155 |       unless required by applicable law (such as deliberate and grossly
156 |       negligent acts) or agreed to in writing, shall any Contributor be
157 |       liable to You for damages, including any direct, indirect, special,
158 |       incidental, or consequential damages of any character arising as a
159 |       result of this License or out of the use or inability to use the
160 |       Work (including but not limited to damages for loss of goodwill,
161 |       work stoppage, computer failure or malfunction, or any and all
162 |       other commercial damages or losses), even if such Contributor
163 |       has been advised of the possibility of such damages.
164 | 
165 |    9. Accepting Warranty or Additional Liability. While redistributing
166 |       the Work or Derivative Works thereof, You may choose to offer,
167 |       and charge a fee for, acceptance of support, warranty, indemnity,
168 |       or other liability obligations and/or rights consistent with this
169 |       License. However, in accepting such obligations, You may act only
170 |       on Your own behalf and on Your sole responsibility, not on behalf
171 |       of any other Contributor, and only if You agree to indemnify,
172 |       defend, and hold each Contributor harmless for any liability
173 |       incurred by, or claims asserted against, such Contributor by reason
174 |       of your accepting any such warranty or additional liability.
175 | 
176 |    END OF TERMS AND CONDITIONS
177 | 
178 |    APPENDIX: How to apply the Apache License to your work.
179 | 
180 |       To apply the Apache License to your work, attach the following
181 |       boilerplate notice, with the fields enclosed by brackets "[]"
182 |       replaced with your own identifying information. (Don't include
183 |       the brackets!)  The text should be enclosed in the appropriate
184 |       comment syntax for the file format. We also recommend that a
185 |       file or class name and description of purpose be included on the
186 |       same "printed page" as the copyright notice for easier
187 |       identification within third-party archives.
188 | 
189 |    Copyright [yyyy] [name of copyright owner]
190 | 
191 |    Licensed under the Apache License, Version 2.0 (the "License");
192 |    you may not use this file except in compliance with the License.
193 |    You may obtain a copy of the License at
194 | 
195 |        http://www.apache.org/licenses/LICENSE-2.0
196 | 
197 |    Unless required by applicable law or agreed to in writing, software
198 |    distributed under the License is distributed on an "AS IS" BASIS,
199 |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 |    See the License for the specific language governing permissions and
201 |    limitations under the License.
202 | 


--------------------------------------------------------------------------------
/fixed_length_captcha.py:
--------------------------------------------------------------------------------
  1 | # -*- coding: utf-8 -*-
  2 | """
  3 | CNN训练定长字符验证码识别模型
  4 | """
  5 | import json
  6 | import io
  7 | import os
  8 | import os.path
  9 | 
 10 | import keras_preprocessing.image
 11 | import numpy as np
 12 | import PIL.Image
 13 | import requests
 14 | import tensorflow as tf
 15 | from tensorflow import keras
 16 | 
 17 | 
 18 | def label_to_array(text, labels):
 19 |     """
 20 |     转换成向量
 21 |     :param text: 验证码
 22 |     :param labels: 验证码所有可能字符集合
 23 |     :return: numpy array
 24 |     """
 25 |     hots = np.zeros(shape=(len(labels) * len(text)))
 26 |     for i, char in enumerate(text):
 27 |         index = i * len(labels) + labels.index(char)
 28 |         hots[index] = 1
 29 |     return hots
 30 | 
 31 | 
 32 | def array_to_label(array, labels):
 33 |     """
 34 |     向量转换成label
 35 |     :param array: numpy array
 36 |     :param labels: label
 37 |     :return: label string
 38 |     """
 39 |     text = []
 40 |     for index in array:
 41 |         text.append(labels[index])
 42 |     return ''.join(text)
 43 | 
 44 | 
 45 | def load_image_data(image_dir_path, image_height, image_width, labels, target_label_length):
 46 |     """
 47 |     加载图片数据
 48 |     图片标签从图片文件名中读取 图片文件名应该符合 label_xxxx.jpg(png)格式
 49 |     RGB图片将会转换成灰度图片
 50 |     :param image_dir_path: 图片路径
 51 |     :param image_height: 图片高度
 52 |     :param image_width: 图片宽度
 53 |     :param labels: 所有标签
 54 |     :param target_label_length: 图片标签固定长度
 55 |     :return: image_data, data_label
 56 |     """
 57 |     image_name_list = os.listdir(image_dir_path)
 58 |     image_data = np.zeros(shape=(len(image_name_list), image_height, image_width, 1))
 59 |     label_data = np.zeros(shape=(len(image_name_list), len(labels) * target_label_length))
 60 | 
 61 |     for index, image_name in enumerate(image_name_list):
 62 |         img = keras_preprocessing.image.utils.load_img(os.path.join(image_dir_path, image_name), color_mode='grayscale')
 63 |         x = keras_preprocessing.image.utils.img_to_array(img)
 64 |         y = label_to_array(image_name.split('_')[0], labels)
 65 |         if hasattr(img, 'close'):
 66 |             img.close()
 67 |         image_data[index] = x
 68 |         label_data[index] = y
 69 |     return image_data, label_data
 70 | 
 71 | 
 72 | class FixCaptchaLengthModel(object):
 73 |     """
 74 |     定长验证码模型
 75 |     Attributes:
 76 |         image_height: 高度
 77 |         image_width: 宽度
 78 |         learning_rate: 学习率
 79 |         dropout: dropout比例
 80 |         label_number: 所有可能字符的种类数量
 81 |         fixed_length: 验证码的固定长度
 82 |     """
 83 | 
 84 |     def __init__(self, image_height, image_width, label_number, fixed_length,
 85 |                  learning_rate=0.0001, dropout=0.25):
 86 |         self.image_height = image_height
 87 |         self.image_width = image_width
 88 |         # 这里固定转化为灰度图像
 89 |         self.image_channel = 1
 90 |         self.learning_rate = learning_rate
 91 |         self.dropout = dropout
 92 |         self.label_number = label_number
 93 |         self.fixed_length = fixed_length
 94 |         self.kernel_size = (3, 3)
 95 |         self.pool_size = (2, 2)
 96 |         self.padding = 'valid'
 97 |         self.activation = 'relu'
 98 | 
 99 |     def model(self):
100 |         """
101 |         :return: keras.Sequential instance
102 |         """
103 |         model = keras.Sequential()
104 |         # 输入层
105 |         input = keras.Input(shape=(self.image_height, self.image_width, self.image_channel), batch_size=None)
106 |         model.add(input)
107 |         # 第一层 卷积
108 |         model.add(keras.layers.Convolution2D(filters=32, kernel_size=self.kernel_size, strides=1, padding=self.padding,
109 |                                        activation=self.activation))
110 |         model.add(keras.layers.MaxPooling2D(pool_size=self.pool_size, strides=self.pool_size))
111 |         model.add(keras.layers.Dropout(rate=self.dropout))
112 |         # 第二层 卷积
113 |         model.add(keras.layers.Convolution2D(filters=64, kernel_size=self.kernel_size, strides=1, padding=self.padding,
114 |                                        activation=self.activation))
115 |         model.add(keras.layers.MaxPooling2D(pool_size=self.pool_size, strides=self.pool_size))
116 |         model.add(keras.layers.Dropout(rate=self.dropout))
117 |         # 第三层 卷积
118 |         model.add(keras.layers.Convolution2D(filters=128, kernel_size=self.kernel_size, strides=1, padding=self.padding,
119 |                                        activation=self.activation))
120 |         model.add(keras.layers.MaxPooling2D(pool_size=self.pool_size, strides=self.pool_size))
121 |         model.add(keras.layers.Dropout(rate=self.dropout))
122 |         model.add(keras.layers.Flatten())
123 |         # 第四层 全连接
124 |         model.add(keras.layers.Dense(units=1024, activation=self.activation))
125 |         model.add(keras.layers.Dropout(rate=self.dropout))
126 |         # 第五层 全连接
127 |         model.add(keras.layers.Dense(units=self.fixed_length * self.label_number, activation="sigmoid"))
128 |         model.compile(optimizer=keras.optimizers.Adam(learning_rate=self.learning_rate), loss="binary_crossentropy",
129 |                       metrics=["binary_accuracy"])
130 |         return model
131 | 
132 |     def load_from_disk(self, model_file_path):
133 |         """
134 |         从磁盘加载已经训练好的模型
135 |         :param model_file_path: 模型文件路径
136 |         :return: keras.Sequential
137 |         """
138 |         if not os.path.exists(model_file_path):
139 |             raise Exception('%s do not exists' % model_file_path)
140 |         model = self.model()
141 |         model.load_weights(model_file_path)
142 |         return model
143 | 
144 | 
145 | class CheckAccuracyCallback(keras.callbacks.Callback):
146 |     """
147 |     检查上一轮的训练准确率
148 |     """
149 | 
150 |     def __init__(self, train_x, train_y, validation_x, validation_y, label_number, fixed_label_length, batch_size=128):
151 |         super(CheckAccuracyCallback, self).__init__()
152 |         self.train_x = train_x
153 |         self.train_y = train_y
154 |         self.validation_x = validation_x
155 |         self.validation_y = validation_y
156 |         self.label_number = label_number
157 |         self.fixed_label_length = fixed_label_length
158 |         self.batch_size = batch_size
159 | 
160 |     def _compare_accuracy(self, data_x, data_y):
161 |         predict_y = self.model.predict_on_batch(data_x)
162 |         predict_y = keras.backend.reshape(predict_y, [len(data_x), self.fixed_label_length, self.label_number])
163 |         data_y = keras.backend.reshape(data_y, [len(data_y), self.fixed_label_length, self.label_number])
164 |         equal_result = keras.backend.equal(keras.backend.argmax(predict_y, axis=2),
165 |                                            keras.backend.argmax(data_y, axis=2))
166 |         return keras.backend.mean(keras.backend.min(keras.backend.cast(equal_result, tf.float32), axis=1))
167 | 
168 |     def on_epoch_end(self, epoch, logs=None):
169 |         print('\nEpoch %s with logs: %s' % (epoch, logs))
170 |         # 选择一个batch并计算准确率
171 |         batches = (len(self.train_x) + self.batch_size - 1) / self.batch_size
172 |         target_batch = (epoch + 1) % batches
173 |         batch_start = int((target_batch - 1) * self.batch_size)
174 |         batch_x = self.train_x[batch_start: batch_start + self.batch_size]
175 |         batch_y = self.train_y[batch_start: batch_start + self.batch_size]
176 |         on_train_batch_acc = self._compare_accuracy(batch_x, batch_y)
177 |         print('Epoch %s with image accuracy on train batch: %s' % (epoch, keras.backend.eval(on_train_batch_acc)))
178 |         on_test_batch_acc = self._compare_accuracy(self.validation_x, self.validation_y)
179 |         print('Epoch %s with image accuracy on validation: %s\n' % (epoch, keras.backend.eval(on_test_batch_acc)))
180 | 
181 | 
182 | class Config(object):
183 | 
184 |     def __init__(self, **kwargs):
185 |         self.image_height = kwargs['image_height']
186 |         self.image_width = kwargs['image_width']
187 |         self.fixed_length = kwargs['fixed_length']
188 |         self.train_batch_size = kwargs['batch_size']
189 |         self.model_save_path = kwargs['save_path']
190 |         self.labels = kwargs['labels']
191 |         self.train_image_dir = kwargs['train_image_dir']
192 |         self.validation_image_dir = kwargs['validation_image_dir']
193 |         self.learning_rate = kwargs['learning_rate']
194 |         self.dropout_rate = kwargs['dropout_rate']
195 |         self.epochs = kwargs['epochs']
196 | 
197 |     @staticmethod
198 |     def load_configs_from_json_file(file_path='fixed_length_captcha.json'):
199 |         """
200 |         :param file_path: file path
201 |         :return: dict instance
202 |         """
203 |         with open(file_path, 'r') as fd:
204 |             config_content = fd.read()
205 |         return Config(**json.loads(config_content))
206 | 
207 | 
208 | class Predictor(object):
209 |     """
210 |     预测器
211 |     """
212 | 
213 |     def __init__(self, config_file_path='fixed_length_captcha.json'):
214 |         self.config = Config.load_configs_from_json_file(config_file_path)
215 |         self.model = FixCaptchaLengthModel(self.config.image_height, self.config.image_width, len(self.config.labels),
216 |                                            self.config.fixed_length, learning_rate=self.config.learning_rate,
217 |                                            dropout=self.config.dropout_rate).load_from_disk(self.config.model_save_path)
218 |         self.label_number = len(self.config.labels)
219 | 
220 |     def predict(self, image_file_path):
221 |         """
222 |         预测单张图片
223 |         :param image_file_path: 单张图片文件路径
224 |         :return: predict text
225 |         """
226 |         with open(image_file_path, 'rb') as f:
227 |             return self.predict_single_image_content(f.read())
228 | 
229 |     def predict_remote_image(self, remote_image_url, headers=None, timeout=30, save_image_to_file=None):
230 |         """
231 |         预测远程图片
232 |         :param remote_image_url: 远程图片URL
233 |         :param headers: 请求头
234 |         :param timeout: 超时时间
235 |         :param save_image_to_file: 是否保存图片到文件
236 |         :return: predict text
237 |         """
238 |         response = requests.get(remote_image_url, headers=headers, timeout=timeout, stream=True)
239 |         content = response.content
240 |         if save_image_to_file is not None:
241 |             with open(save_image_to_file, 'wb') as fd:
242 |                 fd.write(content)
243 |         return self.predict_single_image_content(content)
244 | 
245 |     def predict_single_image_content(self, image_content):
246 |         """
247 |         预测单张图片
248 |         :param image_content: byte content
249 |         :return: predict text
250 |         """
251 |         p_image = PIL.Image.open(io.BytesIO(image_content))
252 |         if p_image.mode not in ('L', 'I;16', 'I'):
253 |             p_image = p_image.convert('L')
254 |         image_data = np.zeros(shape=(1, self.config.image_height, self.config.image_width, 1))
255 |         image_data[0] = keras_preprocessing.image.img_to_array(p_image)
256 |         if hasattr(p_image, 'close'):
257 |             p_image.close()
258 |         result = self.model.predict_on_batch(image_data)
259 |         result = keras.backend.reshape(result, [1, self.config.fixed_length, self.label_number])
260 |         result = keras.backend.argmax(result, axis=2)
261 |         return array_to_label(keras.backend.eval(result)[0], self.config.labels)
262 | 
263 | 
264 | def train():
265 |     """
266 |     训练
267 |     """
268 |     config = Config.load_configs_from_json_file()
269 |     train_x, train_y = load_image_data(config.train_image_dir, config.image_height, config.image_width,
270 |                                        config.labels, config.fixed_length)
271 |     validation_x, validation_y = load_image_data(config.validation_image_dir, config.image_height, config.image_width,
272 |                                                  config.labels, config.fixed_length)
273 |     print('total train image number: %s' % len(train_x))
274 |     print('total validation image number: %s' % len(train_y))
275 |     model = FixCaptchaLengthModel(config.image_height, config.image_width, len(config.labels), config.fixed_length,
276 |                                   learning_rate=config.learning_rate, dropout=config.dropout_rate)
277 |     if os.path.exists(config.model_save_path):
278 |         model = model.load_from_disk(config.model_save_path)
279 |     else:
280 |         model = model.model()
281 |     callbacks = [
282 |         keras.callbacks.ModelCheckpoint(filepath=config.model_save_path, save_weights_only=True, save_best_only=True),
283 |         CheckAccuracyCallback(train_x, train_y, validation_x, validation_y, len(config.labels), config.fixed_length,
284 |                               batch_size=config.train_batch_size)
285 |     ]
286 |     model.fit(train_x, train_y, batch_size=config.train_batch_size, epochs=config.epochs,
287 |               validation_data=(validation_x, validation_y), callbacks=callbacks)
288 | 
289 | 
290 | if __name__ == '__main__':
291 |     train()
292 |     """
293 |     predictor = Predictor()
294 |     print(predictor.predict_remote_image('url', save_image_to_file='test.jpg'))
295 |     """
296 | 


--------------------------------------------------------------------------------