mblt-vision-python 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mblt_vision/__init__.py +86 -0
- mblt_vision/_api.py +53 -0
- mblt_vision/_compat.py +227 -0
- mblt_vision/_model_paths.py +137 -0
- mblt_vision/_tasks.py +30 -0
- mblt_vision/benchmark/__init__.py +1 -0
- mblt_vision/benchmark/argparse_utils.py +106 -0
- mblt_vision/benchmark/chart_utils.py +146 -0
- mblt_vision/benchmark/io_utils.py +64 -0
- mblt_vision/benchmark/summary_utils.py +770 -0
- mblt_vision/cli/__init__.py +5 -0
- mblt_vision/cli/__main__.py +5 -0
- mblt_vision/cli/_vision.py +337 -0
- mblt_vision/cli/compile.py +117 -0
- mblt_vision/cli/main.py +35 -0
- mblt_vision/cli/predict.py +64 -0
- mblt_vision/cli/val.py +562 -0
- mblt_vision/compile/__init__.py +21 -0
- mblt_vision/compile/vision.py +1120 -0
- mblt_vision/datasets/__init__.py +15 -0
- mblt_vision/datasets/ade20k.yaml +164 -0
- mblt_vision/datasets/cityscapes.yaml +54 -0
- mblt_vision/datasets/coco.yaml +178 -0
- mblt_vision/datasets/dotav1.yaml +28 -0
- mblt_vision/datasets/imagenet.yaml +1019 -0
- mblt_vision/datasets/imagenet_synsets.txt +1000 -0
- mblt_vision/datasets/nyu-depth.yaml +7 -0
- mblt_vision/datasets/registry.py +158 -0
- mblt_vision/datasets/widerface.yaml +9 -0
- mblt_vision/depth_estimation/__init__.py +19 -0
- mblt_vision/face_detection/__init__.py +43 -0
- mblt_vision/image_classification/__init__.py +311 -0
- mblt_vision/instance_segmentation/__init__.py +65 -0
- mblt_vision/models/AlexNet.yaml +23 -0
- mblt_vision/models/CAFormer_B36.yaml +22 -0
- mblt_vision/models/CAFormer_M36.yaml +22 -0
- mblt_vision/models/CAFormer_S18.yaml +22 -0
- mblt_vision/models/CAFormer_S36.yaml +22 -0
- mblt_vision/models/CoAtNet_0_RW_224.yaml +22 -0
- mblt_vision/models/CoAtNet_1_RW_224.yaml +22 -0
- mblt_vision/models/CoAtNet_2_RW_224.yaml +22 -0
- mblt_vision/models/ConvFormer_B36.yaml +22 -0
- mblt_vision/models/ConvFormer_M36.yaml +22 -0
- mblt_vision/models/ConvFormer_S18.yaml +22 -0
- mblt_vision/models/ConvFormer_S36.yaml +22 -0
- mblt_vision/models/ConvNext_Base.yaml +23 -0
- mblt_vision/models/ConvNext_Large.yaml +23 -0
- mblt_vision/models/ConvNext_Small.yaml +23 -0
- mblt_vision/models/ConvNext_Tiny.yaml +23 -0
- mblt_vision/models/DeiT3_Base_Patch16_224.yaml +22 -0
- mblt_vision/models/DeiT3_Base_Patch16_384.yaml +22 -0
- mblt_vision/models/DeiT3_Large_Patch16_224.yaml +22 -0
- mblt_vision/models/DeiT3_Large_Patch16_384.yaml +22 -0
- mblt_vision/models/DeiT3_Medium_Patch16_224.yaml +22 -0
- mblt_vision/models/DeiT3_Small_Patch16_224.yaml +22 -0
- mblt_vision/models/DeiT3_Small_Patch16_384.yaml +22 -0
- mblt_vision/models/DeiT_Base_Patch16_224.yaml +22 -0
- mblt_vision/models/DeiT_Base_Patch16_384.yaml +22 -0
- mblt_vision/models/DeiT_Small_Patch16_224.yaml +22 -0
- mblt_vision/models/DeiT_Tiny_Patch16_224.yaml +22 -0
- mblt_vision/models/DenseNet121.yaml +23 -0
- mblt_vision/models/DenseNet161.yaml +23 -0
- mblt_vision/models/DenseNet169.yaml +23 -0
- mblt_vision/models/DenseNet201.yaml +23 -0
- mblt_vision/models/EfficientFormer_L1.yaml +22 -0
- mblt_vision/models/EfficientFormer_L3.yaml +22 -0
- mblt_vision/models/EfficientFormer_L7.yaml +22 -0
- mblt_vision/models/EfficientNet_B0.yaml +23 -0
- mblt_vision/models/EfficientNet_B1.yaml +32 -0
- mblt_vision/models/EfficientNet_B2.yaml +23 -0
- mblt_vision/models/EfficientNet_B3.yaml +23 -0
- mblt_vision/models/EfficientNet_B4.yaml +23 -0
- mblt_vision/models/EfficientNet_B5.yaml +23 -0
- mblt_vision/models/EfficientNet_B6.yaml +23 -0
- mblt_vision/models/EfficientNet_B7.yaml +23 -0
- mblt_vision/models/EfficientNet_V2_L.yaml +23 -0
- mblt_vision/models/EfficientNet_V2_M.yaml +23 -0
- mblt_vision/models/EfficientNet_V2_S.yaml +23 -0
- mblt_vision/models/FlexiVit_Base.yaml +22 -0
- mblt_vision/models/FlexiVit_Large.yaml +22 -0
- mblt_vision/models/FlexiVit_Small.yaml +22 -0
- mblt_vision/models/GELANc-seg.yaml +29 -0
- mblt_vision/models/GELANc.yaml +28 -0
- mblt_vision/models/GELANe.yaml +28 -0
- mblt_vision/models/GELANm.yaml +28 -0
- mblt_vision/models/GELANs.yaml +28 -0
- mblt_vision/models/GoogLeNet.yaml +23 -0
- mblt_vision/models/Inception_V3.yaml +23 -0
- mblt_vision/models/LeViT_Conv_128.yaml +22 -0
- mblt_vision/models/LeViT_Conv_128S.yaml +22 -0
- mblt_vision/models/LeViT_Conv_192.yaml +22 -0
- mblt_vision/models/LeViT_Conv_256.yaml +22 -0
- mblt_vision/models/LeViT_Conv_384.yaml +22 -0
- mblt_vision/models/MNASNet0_5.yaml +23 -0
- mblt_vision/models/MNASNet0_75.yaml +23 -0
- mblt_vision/models/MNASNet1_0.yaml +23 -0
- mblt_vision/models/MNASNet1_3.yaml +23 -0
- mblt_vision/models/MobileNet_V2.yaml +32 -0
- mblt_vision/models/MobileNet_V3_Large.yaml +32 -0
- mblt_vision/models/MobileNet_V3_Small.yaml +23 -0
- mblt_vision/models/RegNet_X_16GF.yaml +32 -0
- mblt_vision/models/RegNet_X_1_6GF.yaml +32 -0
- mblt_vision/models/RegNet_X_32GF.yaml +32 -0
- mblt_vision/models/RegNet_X_3_2GF.yaml +32 -0
- mblt_vision/models/RegNet_X_400MF.yaml +32 -0
- mblt_vision/models/RegNet_X_800MF.yaml +32 -0
- mblt_vision/models/RegNet_X_8GF.yaml +32 -0
- mblt_vision/models/RegNet_Y_16GF.yaml +76 -0
- mblt_vision/models/RegNet_Y_1_6GF.yaml +32 -0
- mblt_vision/models/RegNet_Y_32GF.yaml +76 -0
- mblt_vision/models/RegNet_Y_3_2GF.yaml +32 -0
- mblt_vision/models/RegNet_Y_400MF.yaml +32 -0
- mblt_vision/models/RegNet_Y_800MF.yaml +32 -0
- mblt_vision/models/RegNet_Y_8GF.yaml +32 -0
- mblt_vision/models/RepViT_M0_9.yaml +22 -0
- mblt_vision/models/RepViT_M1.yaml +22 -0
- mblt_vision/models/RepViT_M1_0.yaml +22 -0
- mblt_vision/models/RepViT_M1_1.yaml +22 -0
- mblt_vision/models/RepViT_M1_5.yaml +22 -0
- mblt_vision/models/RepViT_M2.yaml +22 -0
- mblt_vision/models/RepViT_M2_3.yaml +22 -0
- mblt_vision/models/RepViT_M3.yaml +22 -0
- mblt_vision/models/ResNeXt101_32X8D.yaml +32 -0
- mblt_vision/models/ResNeXt101_64X4D.yaml +23 -0
- mblt_vision/models/ResNeXt50_32X4D.yaml +32 -0
- mblt_vision/models/ResNet101.yaml +32 -0
- mblt_vision/models/ResNet152.yaml +32 -0
- mblt_vision/models/ResNet18.yaml +23 -0
- mblt_vision/models/ResNet34.yaml +23 -0
- mblt_vision/models/ResNet50.yaml +32 -0
- mblt_vision/models/ShuffleNet_V2_X0_5.yaml +23 -0
- mblt_vision/models/ShuffleNet_V2_X1_0.yaml +23 -0
- mblt_vision/models/ShuffleNet_V2_X1_5.yaml +23 -0
- mblt_vision/models/ShuffleNet_V2_X2_0.yaml +23 -0
- mblt_vision/models/SqueezeNet1_0.yaml +23 -0
- mblt_vision/models/SqueezeNet1_1.yaml +23 -0
- mblt_vision/models/Swin_B.yaml +23 -0
- mblt_vision/models/Swin_S.yaml +23 -0
- mblt_vision/models/Swin_T.yaml +23 -0
- mblt_vision/models/VGG11.yaml +23 -0
- mblt_vision/models/VGG11_BN.yaml +23 -0
- mblt_vision/models/VGG13.yaml +23 -0
- mblt_vision/models/VGG13_BN.yaml +23 -0
- mblt_vision/models/VGG16.yaml +23 -0
- mblt_vision/models/VGG16_BN.yaml +23 -0
- mblt_vision/models/VGG19.yaml +23 -0
- mblt_vision/models/VGG19_BN.yaml +23 -0
- mblt_vision/models/ViT_B_16.yaml +67 -0
- mblt_vision/models/ViT_B_32.yaml +23 -0
- mblt_vision/models/ViT_Base_Patch16_224.yaml +22 -0
- mblt_vision/models/ViT_Base_Patch16_384.yaml +22 -0
- mblt_vision/models/ViT_Base_Patch32_224.yaml +22 -0
- mblt_vision/models/ViT_Base_Patch32_384.yaml +22 -0
- mblt_vision/models/ViT_Base_Patch8_224.yaml +22 -0
- mblt_vision/models/ViT_L_16.yaml +67 -0
- mblt_vision/models/ViT_L_32.yaml +23 -0
- mblt_vision/models/ViT_Large_Patch16_224.yaml +22 -0
- mblt_vision/models/ViT_Large_Patch16_384.yaml +22 -0
- mblt_vision/models/ViT_Large_Patch32_384.yaml +22 -0
- mblt_vision/models/ViT_Small_Patch16_224.yaml +22 -0
- mblt_vision/models/ViT_Small_Patch16_384.yaml +22 -0
- mblt_vision/models/ViT_Small_Patch32_224.yaml +22 -0
- mblt_vision/models/ViT_Small_Patch32_384.yaml +22 -0
- mblt_vision/models/ViT_Tiny_Patch16_224.yaml +22 -0
- mblt_vision/models/ViT_Tiny_Patch16_384.yaml +22 -0
- mblt_vision/models/VisFormer_Small.yaml +22 -0
- mblt_vision/models/VisFormer_Tiny.yaml +22 -0
- mblt_vision/models/Wide_ResNet101_2.yaml +32 -0
- mblt_vision/models/Wide_ResNet50_2.yaml +32 -0
- mblt_vision/models/YOLO11l-cls.yaml +23 -0
- mblt_vision/models/YOLO11l-face.yaml +27 -0
- mblt_vision/models/YOLO11l-obb.yaml +28 -0
- mblt_vision/models/YOLO11l-pose.yaml +28 -0
- mblt_vision/models/YOLO11l-seg.yaml +28 -0
- mblt_vision/models/YOLO11l.yaml +27 -0
- mblt_vision/models/YOLO11m-cls.yaml +23 -0
- mblt_vision/models/YOLO11m-face.yaml +27 -0
- mblt_vision/models/YOLO11m-obb.yaml +28 -0
- mblt_vision/models/YOLO11m-pose.yaml +28 -0
- mblt_vision/models/YOLO11m-seg.yaml +28 -0
- mblt_vision/models/YOLO11m.yaml +27 -0
- mblt_vision/models/YOLO11n-cls.yaml +23 -0
- mblt_vision/models/YOLO11n-face.yaml +27 -0
- mblt_vision/models/YOLO11n-obb.yaml +28 -0
- mblt_vision/models/YOLO11n-pose.yaml +28 -0
- mblt_vision/models/YOLO11n-seg.yaml +28 -0
- mblt_vision/models/YOLO11n.yaml +27 -0
- mblt_vision/models/YOLO11s-cls.yaml +23 -0
- mblt_vision/models/YOLO11s-face.yaml +27 -0
- mblt_vision/models/YOLO11s-obb.yaml +28 -0
- mblt_vision/models/YOLO11s-pose.yaml +28 -0
- mblt_vision/models/YOLO11s-seg.yaml +28 -0
- mblt_vision/models/YOLO11s.yaml +27 -0
- mblt_vision/models/YOLO11x-cls.yaml +23 -0
- mblt_vision/models/YOLO11x-obb.yaml +28 -0
- mblt_vision/models/YOLO11x-pose.yaml +28 -0
- mblt_vision/models/YOLO11x-seg.yaml +28 -0
- mblt_vision/models/YOLO11x.yaml +27 -0
- mblt_vision/models/YOLO12l-face.yaml +27 -0
- mblt_vision/models/YOLO12l-seg.yaml +28 -0
- mblt_vision/models/YOLO12l.yaml +27 -0
- mblt_vision/models/YOLO12m-face.yaml +27 -0
- mblt_vision/models/YOLO12m-seg.yaml +28 -0
- mblt_vision/models/YOLO12m.yaml +27 -0
- mblt_vision/models/YOLO12n-face.yaml +27 -0
- mblt_vision/models/YOLO12n-seg.yaml +28 -0
- mblt_vision/models/YOLO12n.yaml +27 -0
- mblt_vision/models/YOLO12s-face.yaml +27 -0
- mblt_vision/models/YOLO12s-seg.yaml +28 -0
- mblt_vision/models/YOLO12s.yaml +27 -0
- mblt_vision/models/YOLO12x-seg.yaml +28 -0
- mblt_vision/models/YOLO12x.yaml +27 -0
- mblt_vision/models/YOLO26l-cls.yaml +23 -0
- mblt_vision/models/YOLO26l-depth.yaml +17 -0
- mblt_vision/models/YOLO26l-distill.yaml +27 -0
- mblt_vision/models/YOLO26l-obb.yaml +28 -0
- mblt_vision/models/YOLO26l-pose.yaml +28 -0
- mblt_vision/models/YOLO26l-seg.yaml +28 -0
- mblt_vision/models/YOLO26l-sem-ade20k.yaml +17 -0
- mblt_vision/models/YOLO26l-sem.yaml +17 -0
- mblt_vision/models/YOLO26l.yaml +27 -0
- mblt_vision/models/YOLO26m-cls.yaml +23 -0
- mblt_vision/models/YOLO26m-depth.yaml +17 -0
- mblt_vision/models/YOLO26m-distill.yaml +27 -0
- mblt_vision/models/YOLO26m-obb.yaml +28 -0
- mblt_vision/models/YOLO26m-pose.yaml +28 -0
- mblt_vision/models/YOLO26m-seg.yaml +28 -0
- mblt_vision/models/YOLO26m-sem-ade20k.yaml +17 -0
- mblt_vision/models/YOLO26m-sem.yaml +17 -0
- mblt_vision/models/YOLO26m.yaml +27 -0
- mblt_vision/models/YOLO26n-cls.yaml +23 -0
- mblt_vision/models/YOLO26n-depth.yaml +17 -0
- mblt_vision/models/YOLO26n-distill.yaml +27 -0
- mblt_vision/models/YOLO26n-obb.yaml +28 -0
- mblt_vision/models/YOLO26n-pose.yaml +28 -0
- mblt_vision/models/YOLO26n-seg.yaml +28 -0
- mblt_vision/models/YOLO26n-sem-ade20k.yaml +17 -0
- mblt_vision/models/YOLO26n-sem.yaml +17 -0
- mblt_vision/models/YOLO26n.yaml +27 -0
- mblt_vision/models/YOLO26s-cls.yaml +23 -0
- mblt_vision/models/YOLO26s-depth.yaml +17 -0
- mblt_vision/models/YOLO26s-distill.yaml +27 -0
- mblt_vision/models/YOLO26s-obb.yaml +28 -0
- mblt_vision/models/YOLO26s-pose.yaml +28 -0
- mblt_vision/models/YOLO26s-seg.yaml +28 -0
- mblt_vision/models/YOLO26s-sem-ade20k.yaml +17 -0
- mblt_vision/models/YOLO26s-sem.yaml +17 -0
- mblt_vision/models/YOLO26s.yaml +27 -0
- mblt_vision/models/YOLO26x-cls.yaml +23 -0
- mblt_vision/models/YOLO26x-depth.yaml +17 -0
- mblt_vision/models/YOLO26x-distill.yaml +27 -0
- mblt_vision/models/YOLO26x-obb.yaml +28 -0
- mblt_vision/models/YOLO26x-pose.yaml +28 -0
- mblt_vision/models/YOLO26x-seg.yaml +28 -0
- mblt_vision/models/YOLO26x-sem-ade20k.yaml +17 -0
- mblt_vision/models/YOLO26x-sem.yaml +17 -0
- mblt_vision/models/YOLO26x.yaml +27 -0
- mblt_vision/models/YOLOv10b.yaml +28 -0
- mblt_vision/models/YOLOv10l-face.yaml +28 -0
- mblt_vision/models/YOLOv10l.yaml +28 -0
- mblt_vision/models/YOLOv10m-face.yaml +28 -0
- mblt_vision/models/YOLOv10m.yaml +28 -0
- mblt_vision/models/YOLOv10n-face.yaml +28 -0
- mblt_vision/models/YOLOv10n.yaml +28 -0
- mblt_vision/models/YOLOv10s-face.yaml +28 -0
- mblt_vision/models/YOLOv10s.yaml +28 -0
- mblt_vision/models/YOLOv10x.yaml +28 -0
- mblt_vision/models/YOLOv3-spp.yaml +44 -0
- mblt_vision/models/YOLOv3-sppu.yaml +27 -0
- mblt_vision/models/YOLOv3-tiny.yaml +38 -0
- mblt_vision/models/YOLOv3-tinyu.yaml +27 -0
- mblt_vision/models/YOLOv3.yaml +44 -0
- mblt_vision/models/YOLOv3u.yaml +27 -0
- mblt_vision/models/YOLOv5l-cls.yaml +22 -0
- mblt_vision/models/YOLOv5l-seg.yaml +45 -0
- mblt_vision/models/YOLOv5l.yaml +44 -0
- mblt_vision/models/YOLOv5l6.yaml +50 -0
- mblt_vision/models/YOLOv5l6u.yaml +27 -0
- mblt_vision/models/YOLOv5lu.yaml +27 -0
- mblt_vision/models/YOLOv5m-cls.yaml +22 -0
- mblt_vision/models/YOLOv5m-seg.yaml +45 -0
- mblt_vision/models/YOLOv5m.yaml +45 -0
- mblt_vision/models/YOLOv5m6.yaml +50 -0
- mblt_vision/models/YOLOv5m6u.yaml +27 -0
- mblt_vision/models/YOLOv5mu.yaml +27 -0
- mblt_vision/models/YOLOv5n-cls.yaml +22 -0
- mblt_vision/models/YOLOv5n-seg.yaml +45 -0
- mblt_vision/models/YOLOv5n.yaml +44 -0
- mblt_vision/models/YOLOv5n6.yaml +50 -0
- mblt_vision/models/YOLOv5n6u.yaml +27 -0
- mblt_vision/models/YOLOv5nu.yaml +27 -0
- mblt_vision/models/YOLOv5s-cls.yaml +22 -0
- mblt_vision/models/YOLOv5s-seg.yaml +45 -0
- mblt_vision/models/YOLOv5s.yaml +44 -0
- mblt_vision/models/YOLOv5s6.yaml +50 -0
- mblt_vision/models/YOLOv5s6u.yaml +27 -0
- mblt_vision/models/YOLOv5su.yaml +27 -0
- mblt_vision/models/YOLOv5x-cls.yaml +22 -0
- mblt_vision/models/YOLOv5x-seg.yaml +45 -0
- mblt_vision/models/YOLOv5x.yaml +44 -0
- mblt_vision/models/YOLOv5x6.yaml +50 -0
- mblt_vision/models/YOLOv5x6u.yaml +27 -0
- mblt_vision/models/YOLOv5xu.yaml +27 -0
- mblt_vision/models/YOLOv6m-face.yaml +27 -0
- mblt_vision/models/YOLOv6n-face.yaml +27 -0
- mblt_vision/models/YOLOv7.yaml +44 -0
- mblt_vision/models/YOLOv7d6.yaml +50 -0
- mblt_vision/models/YOLOv7e6.yaml +50 -0
- mblt_vision/models/YOLOv7e6e.yaml +50 -0
- mblt_vision/models/YOLOv7w6.yaml +50 -0
- mblt_vision/models/YOLOv7x.yaml +44 -0
- mblt_vision/models/YOLOv8l-cls.yaml +23 -0
- mblt_vision/models/YOLOv8l-face.yaml +27 -0
- mblt_vision/models/YOLOv8l-obb.yaml +28 -0
- mblt_vision/models/YOLOv8l-pose.yaml +28 -0
- mblt_vision/models/YOLOv8l-seg.yaml +28 -0
- mblt_vision/models/YOLOv8l.yaml +27 -0
- mblt_vision/models/YOLOv8m-cls.yaml +23 -0
- mblt_vision/models/YOLOv8m-face.yaml +27 -0
- mblt_vision/models/YOLOv8m-obb.yaml +28 -0
- mblt_vision/models/YOLOv8m-pose.yaml +28 -0
- mblt_vision/models/YOLOv8m-seg.yaml +28 -0
- mblt_vision/models/YOLOv8m.yaml +27 -0
- mblt_vision/models/YOLOv8n-cls.yaml +23 -0
- mblt_vision/models/YOLOv8n-face.yaml +27 -0
- mblt_vision/models/YOLOv8n-obb.yaml +28 -0
- mblt_vision/models/YOLOv8n-pose.yaml +28 -0
- mblt_vision/models/YOLOv8n-seg.yaml +28 -0
- mblt_vision/models/YOLOv8n.yaml +27 -0
- mblt_vision/models/YOLOv8s-cls.yaml +23 -0
- mblt_vision/models/YOLOv8s-obb.yaml +28 -0
- mblt_vision/models/YOLOv8s-pose.yaml +28 -0
- mblt_vision/models/YOLOv8s-seg.yaml +28 -0
- mblt_vision/models/YOLOv8s.yaml +27 -0
- mblt_vision/models/YOLOv8x-cls.yaml +23 -0
- mblt_vision/models/YOLOv8x-obb.yaml +28 -0
- mblt_vision/models/YOLOv8x-pose-p6.yaml +28 -0
- mblt_vision/models/YOLOv8x-pose.yaml +28 -0
- mblt_vision/models/YOLOv8x-seg.yaml +28 -0
- mblt_vision/models/YOLOv8x.yaml +27 -0
- mblt_vision/models/YOLOv9c-seg.yaml +28 -0
- mblt_vision/models/YOLOv9c.yaml +27 -0
- mblt_vision/models/YOLOv9e-seg.yaml +28 -0
- mblt_vision/models/YOLOv9e.yaml +27 -0
- mblt_vision/models/YOLOv9m.yaml +27 -0
- mblt_vision/models/YOLOv9s.yaml +27 -0
- mblt_vision/models/YOLOv9t.yaml +27 -0
- mblt_vision/obb/__init__.py +39 -0
- mblt_vision/object_detection/__init__.py +153 -0
- mblt_vision/pose_estimation/__init__.py +41 -0
- mblt_vision/py.typed +1 -0
- mblt_vision/semantic_segmentation/__init__.py +29 -0
- mblt_vision/utils/__init__.py +0 -0
- mblt_vision/utils/datasets/__init__.py +85 -0
- mblt_vision/utils/datasets/ade20k.py +41 -0
- mblt_vision/utils/datasets/cityscapes.py +66 -0
- mblt_vision/utils/datasets/coco.py +243 -0
- mblt_vision/utils/datasets/dataloader.py +1016 -0
- mblt_vision/utils/datasets/dotav1.py +59 -0
- mblt_vision/utils/datasets/imagenet.py +29 -0
- mblt_vision/utils/datasets/organizer.py +2145 -0
- mblt_vision/utils/datasets/readiness.py +1063 -0
- mblt_vision/utils/evaluation/__init__.py +51 -0
- mblt_vision/utils/evaluation/_result.py +22 -0
- mblt_vision/utils/evaluation/eval_ade20k.py +302 -0
- mblt_vision/utils/evaluation/eval_cityscapes.py +32 -0
- mblt_vision/utils/evaluation/eval_coco.py +465 -0
- mblt_vision/utils/evaluation/eval_dota.py +878 -0
- mblt_vision/utils/evaluation/eval_imagenet.py +151 -0
- mblt_vision/utils/evaluation/eval_nyu_depth.py +189 -0
- mblt_vision/utils/evaluation/eval_widerface.py +440 -0
- mblt_vision/utils/letterbox.py +118 -0
- mblt_vision/utils/postprocess/__init__.py +9 -0
- mblt_vision/utils/postprocess/_letterbox.py +102 -0
- mblt_vision/utils/postprocess/base.py +743 -0
- mblt_vision/utils/postprocess/build_post.py +120 -0
- mblt_vision/utils/postprocess/cls_post.py +117 -0
- mblt_vision/utils/postprocess/common.py +1571 -0
- mblt_vision/utils/postprocess/depth_post.py +106 -0
- mblt_vision/utils/postprocess/semantic_seg_post.py +192 -0
- mblt_vision/utils/postprocess/yolo_anchor_post.py +469 -0
- mblt_vision/utils/postprocess/yolo_anchorless_post.py +952 -0
- mblt_vision/utils/postprocess/yolo_dflfree_post.py +983 -0
- mblt_vision/utils/postprocess/yolo_nmsfree_post.py +176 -0
- mblt_vision/utils/preprocess/__init__.py +27 -0
- mblt_vision/utils/preprocess/_validation.py +66 -0
- mblt_vision/utils/preprocess/base.py +157 -0
- mblt_vision/utils/preprocess/build_pre.py +48 -0
- mblt_vision/utils/preprocess/center_crop.py +75 -0
- mblt_vision/utils/preprocess/letterbox.py +123 -0
- mblt_vision/utils/preprocess/normalize.py +94 -0
- mblt_vision/utils/preprocess/order.py +74 -0
- mblt_vision/utils/preprocess/reader.py +94 -0
- mblt_vision/utils/preprocess/resize.py +202 -0
- mblt_vision/utils/preprocess/yolo_pre.py +7 -0
- mblt_vision/utils/results.py +725 -0
- mblt_vision/utils/types.py +15 -0
- mblt_vision/wrapper.py +1121 -0
- mblt_vision_python-0.0.0.dist-info/METADATA +152 -0
- mblt_vision_python-0.0.0.dist-info/RECORD +404 -0
- mblt_vision_python-0.0.0.dist-info/WHEEL +5 -0
- mblt_vision_python-0.0.0.dist-info/entry_points.txt +2 -0
- mblt_vision_python-0.0.0.dist-info/licenses/LICENSE +28 -0
- mblt_vision_python-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
"""Identity and completeness checks for organized vision validation datasets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from importlib.resources import files
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from faster_coco_eval import mask as coco_mask
|
|
16
|
+
from PIL import Image
|
|
17
|
+
from scipy.io import loadmat
|
|
18
|
+
from scipy.io.matlab import MatReadError
|
|
19
|
+
|
|
20
|
+
from ..._tasks import normalize_vision_task
|
|
21
|
+
from ...datasets import get_dataset_category_ids
|
|
22
|
+
from .cityscapes import CITYSCAPES_SOURCE_TO_TRAIN_ID
|
|
23
|
+
|
|
24
|
+
IMAGE_SUFFIXES = {".bmp", ".jpeg", ".jpg", ".png", ".tif", ".tiff", ".webp"}
|
|
25
|
+
IMAGENET_CLASS_COUNT = 1000
|
|
26
|
+
IMAGENET_IMAGES_PER_CLASS = 50
|
|
27
|
+
COCO_VALIDATION_SAMPLE_COUNT = 5000
|
|
28
|
+
DOTAV1_VALIDATION_SAMPLE_COUNT = 458
|
|
29
|
+
WIDERFACE_EVENT_COUNT = 61
|
|
30
|
+
WIDERFACE_VALIDATION_SAMPLE_COUNT = 3226
|
|
31
|
+
NYU_DEPTH_VALIDATION_SAMPLE_COUNT = 654
|
|
32
|
+
ADE20K_VALIDATION_SAMPLE_COUNT = 2000
|
|
33
|
+
CITYSCAPES_VALIDATION_SAMPLE_COUNT = 500
|
|
34
|
+
ADE20K_METADATA_FILES = ("objectInfo150.txt", "sceneCategories.txt")
|
|
35
|
+
IMAGENET_CLASS_PATTERN = re.compile(r"n\d{8}")
|
|
36
|
+
IMAGENET_IMAGE_PATTERN = re.compile(r"ILSVRC2012_val_\d{8}")
|
|
37
|
+
COCO_IMAGE_PATTERN = re.compile(r"\d{12}")
|
|
38
|
+
WIDERFACE_EVENT_PATTERN = re.compile(r"\d+--\S.*")
|
|
39
|
+
CITYSCAPES_SAMPLE_ID_PATTERN = re.compile(
|
|
40
|
+
r"^(?P<city>[A-Za-z][A-Za-z0-9-]*)_\d{6}_\d{6}$"
|
|
41
|
+
)
|
|
42
|
+
CITYSCAPES_VALIDATION_CITY_COUNTS = {"frankfurt": 267, "lindau": 59, "munster": 174}
|
|
43
|
+
IMAGENET_SYNSET_ORDER = tuple(
|
|
44
|
+
files("mblt_vision.datasets")
|
|
45
|
+
.joinpath("imagenet_synsets.txt")
|
|
46
|
+
.read_text(encoding="utf-8")
|
|
47
|
+
.splitlines()
|
|
48
|
+
)
|
|
49
|
+
IMAGENET_SYNSETS = frozenset(IMAGENET_SYNSET_ORDER)
|
|
50
|
+
COCO_ANNOTATION_COUNTS = {
|
|
51
|
+
"instances_val2017.json": 36781,
|
|
52
|
+
"person_keypoints_val2017.json": 11004,
|
|
53
|
+
}
|
|
54
|
+
COCO_CATEGORY_COUNTS = {
|
|
55
|
+
"instances_val2017.json": 80,
|
|
56
|
+
"person_keypoints_val2017.json": 1,
|
|
57
|
+
}
|
|
58
|
+
COCO_CATEGORY_IDS = frozenset(get_dataset_category_ids("coco"))
|
|
59
|
+
COCO_PERSON_KEYPOINT_CATEGORY_IDS = frozenset({1})
|
|
60
|
+
COCO_VALIDATION_IMAGE_IDENTITIES_SHA256 = (
|
|
61
|
+
"f57f71ba25171a0fd99be8c425a91d4a6fdd43d25aadc7e5be51dd37a73281a7"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _path_has_symlink_component(path: Path) -> bool:
|
|
66
|
+
"""Return whether a path traversal or its normalized ancestors contain a symlink."""
|
|
67
|
+
|
|
68
|
+
expanded_path = path.expanduser()
|
|
69
|
+
traversal_path = (
|
|
70
|
+
expanded_path if expanded_path.is_absolute() else Path.cwd() / expanded_path
|
|
71
|
+
)
|
|
72
|
+
normalized_path = Path(os.path.abspath(expanded_path))
|
|
73
|
+
candidates = (
|
|
74
|
+
traversal_path,
|
|
75
|
+
*traversal_path.parents,
|
|
76
|
+
normalized_path,
|
|
77
|
+
*normalized_path.parents,
|
|
78
|
+
)
|
|
79
|
+
return any(component.is_symlink() for component in candidates)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _files_by_stem(
|
|
83
|
+
directory: Path,
|
|
84
|
+
suffixes: set[str],
|
|
85
|
+
*,
|
|
86
|
+
reject_symlinks: bool = False,
|
|
87
|
+
) -> dict[str, Path] | None:
|
|
88
|
+
"""Collect direct child files with supported suffixes by stem.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
directory: Directory containing candidate files.
|
|
92
|
+
suffixes: Accepted lowercase file suffixes.
|
|
93
|
+
reject_symlinks: Whether any symlinked directory or entry invalidates
|
|
94
|
+
the file collection.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Files keyed by stem, an empty mapping for a missing directory, or
|
|
98
|
+
``None`` for duplicate stems or rejected symlinks.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
if (reject_symlinks and directory.is_symlink()) or not directory.is_dir():
|
|
102
|
+
return {}
|
|
103
|
+
entries = list(directory.iterdir())
|
|
104
|
+
if reject_symlinks and any(path.is_symlink() for path in entries):
|
|
105
|
+
return None
|
|
106
|
+
paths = [
|
|
107
|
+
path for path in entries if path.is_file() and path.suffix.lower() in suffixes
|
|
108
|
+
]
|
|
109
|
+
files = {path.stem: path for path in paths}
|
|
110
|
+
return files if len(files) == len(paths) else None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _has_positive_polygon_area(polygon: list[int | float]) -> bool:
|
|
114
|
+
"""Return whether a finite flat polygon encloses non-zero signed area."""
|
|
115
|
+
|
|
116
|
+
points = np.asarray(polygon, dtype=np.float64).reshape(-1, 2)
|
|
117
|
+
signed_double_area = np.dot(points[:, 0], np.roll(points[:, 1], -1)) - np.dot(
|
|
118
|
+
points[:, 1], np.roll(points[:, 0], -1)
|
|
119
|
+
)
|
|
120
|
+
return bool(abs(signed_double_area) > 0)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _canonicalize_quadrilateral(
|
|
124
|
+
coordinates: list[int | float] | tuple[int | float, ...],
|
|
125
|
+
) -> tuple[float, ...]:
|
|
126
|
+
"""Return a quadrilateral key independent of its start vertex and winding."""
|
|
127
|
+
|
|
128
|
+
if len(coordinates) != 8:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"A quadrilateral must contain exactly four two-dimensional vertices."
|
|
131
|
+
)
|
|
132
|
+
points = tuple(
|
|
133
|
+
(float(coordinates[index]), float(coordinates[index + 1]))
|
|
134
|
+
for index in range(0, len(coordinates), 2)
|
|
135
|
+
)
|
|
136
|
+
candidates = []
|
|
137
|
+
for winding in (points, tuple(reversed(points))):
|
|
138
|
+
candidates.extend(winding[index:] + winding[:index] for index in range(4))
|
|
139
|
+
return tuple(coordinate for point in min(candidates) for coordinate in point)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _polygon_has_positive_image_overlap(
|
|
143
|
+
polygon: list[int | float], image_shape: tuple[int, int] | None
|
|
144
|
+
) -> bool:
|
|
145
|
+
"""Return whether a polygon covers non-zero area within an image rectangle."""
|
|
146
|
+
|
|
147
|
+
if image_shape is None:
|
|
148
|
+
return True
|
|
149
|
+
height, width = image_shape
|
|
150
|
+
if height <= 0 or width <= 0:
|
|
151
|
+
return False
|
|
152
|
+
points = [
|
|
153
|
+
tuple(point) for point in np.asarray(polygon, dtype=np.float64).reshape(-1, 2)
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
def _clip(
|
|
157
|
+
vertices: list[tuple[float, float]],
|
|
158
|
+
inside: Any,
|
|
159
|
+
intersect: Any,
|
|
160
|
+
) -> list[tuple[float, float]]:
|
|
161
|
+
clipped: list[tuple[float, float]] = []
|
|
162
|
+
if not vertices:
|
|
163
|
+
return clipped
|
|
164
|
+
previous = vertices[-1]
|
|
165
|
+
previous_inside = bool(inside(previous))
|
|
166
|
+
for current in vertices:
|
|
167
|
+
current_inside = bool(inside(current))
|
|
168
|
+
if current_inside != previous_inside:
|
|
169
|
+
clipped.append(intersect(previous, current))
|
|
170
|
+
if current_inside:
|
|
171
|
+
clipped.append(current)
|
|
172
|
+
previous = current
|
|
173
|
+
previous_inside = current_inside
|
|
174
|
+
return clipped
|
|
175
|
+
|
|
176
|
+
def _vertical_intersection(
|
|
177
|
+
x: float, start: tuple[float, float], end: tuple[float, float]
|
|
178
|
+
) -> tuple[float, float]:
|
|
179
|
+
delta_x = end[0] - start[0]
|
|
180
|
+
if delta_x == 0:
|
|
181
|
+
return x, start[1]
|
|
182
|
+
ratio = (x - start[0]) / delta_x
|
|
183
|
+
return x, start[1] + ratio * (end[1] - start[1])
|
|
184
|
+
|
|
185
|
+
def _horizontal_intersection(
|
|
186
|
+
y: float, start: tuple[float, float], end: tuple[float, float]
|
|
187
|
+
) -> tuple[float, float]:
|
|
188
|
+
delta_y = end[1] - start[1]
|
|
189
|
+
if delta_y == 0:
|
|
190
|
+
return start[0], y
|
|
191
|
+
ratio = (y - start[1]) / delta_y
|
|
192
|
+
return start[0] + ratio * (end[0] - start[0]), y
|
|
193
|
+
|
|
194
|
+
points = _clip(
|
|
195
|
+
points,
|
|
196
|
+
lambda point: point[0] >= 0,
|
|
197
|
+
lambda a, b: _vertical_intersection(0, a, b),
|
|
198
|
+
)
|
|
199
|
+
points = _clip(
|
|
200
|
+
points,
|
|
201
|
+
lambda point: point[0] <= width,
|
|
202
|
+
lambda a, b: _vertical_intersection(width, a, b),
|
|
203
|
+
)
|
|
204
|
+
points = _clip(
|
|
205
|
+
points,
|
|
206
|
+
lambda point: point[1] >= 0,
|
|
207
|
+
lambda a, b: _horizontal_intersection(0, a, b),
|
|
208
|
+
)
|
|
209
|
+
points = _clip(
|
|
210
|
+
points,
|
|
211
|
+
lambda point: point[1] <= height,
|
|
212
|
+
lambda a, b: _horizontal_intersection(height, a, b),
|
|
213
|
+
)
|
|
214
|
+
if len(points) < 3:
|
|
215
|
+
return False
|
|
216
|
+
clipped = [coordinate for point in points for coordinate in point]
|
|
217
|
+
return _has_positive_polygon_area(clipped)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _imagenet_ready(root: Path) -> bool:
|
|
221
|
+
"""Check the organizer's complete ImageNet-1k validation class tree."""
|
|
222
|
+
|
|
223
|
+
if not root.is_dir():
|
|
224
|
+
return False
|
|
225
|
+
class_dirs = [path for path in root.iterdir() if path.is_dir()]
|
|
226
|
+
if {path.name for path in class_dirs} != IMAGENET_SYNSETS:
|
|
227
|
+
return False
|
|
228
|
+
image_names: set[str] = set()
|
|
229
|
+
for class_dir in class_dirs:
|
|
230
|
+
if IMAGENET_CLASS_PATTERN.fullmatch(class_dir.name) is None:
|
|
231
|
+
return False
|
|
232
|
+
images = [
|
|
233
|
+
path
|
|
234
|
+
for path in class_dir.iterdir()
|
|
235
|
+
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
|
|
236
|
+
]
|
|
237
|
+
if len(images) != IMAGENET_IMAGES_PER_CLASS or any(
|
|
238
|
+
path.suffix != ".JPEG"
|
|
239
|
+
or IMAGENET_IMAGE_PATTERN.fullmatch(path.stem) is None
|
|
240
|
+
for path in images
|
|
241
|
+
):
|
|
242
|
+
return False
|
|
243
|
+
image_names.update(path.name for path in images)
|
|
244
|
+
return len(image_names) == IMAGENET_CLASS_COUNT * IMAGENET_IMAGES_PER_CLASS
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _load_coco_image_names(
|
|
248
|
+
annotation_path: Path, task: str = "object_detection"
|
|
249
|
+
) -> set[str] | None:
|
|
250
|
+
"""Load unique validation image filenames from a COCO annotation file."""
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
annotation: Any = json.loads(annotation_path.read_text(encoding="utf-8"))
|
|
254
|
+
except (json.JSONDecodeError, OSError, UnicodeError):
|
|
255
|
+
return None
|
|
256
|
+
if not isinstance(annotation, dict):
|
|
257
|
+
return None
|
|
258
|
+
image_records = annotation.get("images")
|
|
259
|
+
if (
|
|
260
|
+
not isinstance(image_records, list)
|
|
261
|
+
or len(image_records) != COCO_VALIDATION_SAMPLE_COUNT
|
|
262
|
+
):
|
|
263
|
+
return None
|
|
264
|
+
names: list[str] = []
|
|
265
|
+
image_ids: list[int] = []
|
|
266
|
+
image_shapes: dict[int, tuple[int, int] | None] = {}
|
|
267
|
+
for record in image_records:
|
|
268
|
+
if not isinstance(record, dict):
|
|
269
|
+
continue
|
|
270
|
+
file_name = record.get("file_name")
|
|
271
|
+
image_id = record.get("id")
|
|
272
|
+
if (
|
|
273
|
+
isinstance(file_name, str)
|
|
274
|
+
and isinstance(image_id, int)
|
|
275
|
+
and not isinstance(image_id, bool)
|
|
276
|
+
):
|
|
277
|
+
names.append(file_name)
|
|
278
|
+
image_ids.append(image_id)
|
|
279
|
+
height, width = record.get("height"), record.get("width")
|
|
280
|
+
if (
|
|
281
|
+
isinstance(height, int)
|
|
282
|
+
and not isinstance(height, bool)
|
|
283
|
+
and height > 0
|
|
284
|
+
and isinstance(width, int)
|
|
285
|
+
and not isinstance(width, bool)
|
|
286
|
+
and width > 0
|
|
287
|
+
):
|
|
288
|
+
image_shapes[image_id] = (height, width)
|
|
289
|
+
else:
|
|
290
|
+
return None
|
|
291
|
+
if (
|
|
292
|
+
len(names) != len(image_records)
|
|
293
|
+
or len(names) != len(set(names))
|
|
294
|
+
or len(image_ids) != len(set(image_ids))
|
|
295
|
+
):
|
|
296
|
+
return None
|
|
297
|
+
if len(image_records) == 5000:
|
|
298
|
+
identity_payload = "".join(
|
|
299
|
+
f"{image_id}:{file_name}\n"
|
|
300
|
+
for image_id, file_name in sorted(zip(image_ids, names, strict=True))
|
|
301
|
+
).encode()
|
|
302
|
+
if (
|
|
303
|
+
hashlib.sha256(identity_payload).hexdigest()
|
|
304
|
+
!= COCO_VALIDATION_IMAGE_IDENTITIES_SHA256
|
|
305
|
+
):
|
|
306
|
+
return None
|
|
307
|
+
annotation_records = annotation.get("annotations")
|
|
308
|
+
categories = annotation.get("categories")
|
|
309
|
+
expected_annotations = COCO_ANNOTATION_COUNTS.get(annotation_path.name)
|
|
310
|
+
expected_categories = COCO_CATEGORY_COUNTS.get(annotation_path.name)
|
|
311
|
+
if (
|
|
312
|
+
not isinstance(annotation_records, list)
|
|
313
|
+
or not isinstance(categories, list)
|
|
314
|
+
or len(annotation_records) != expected_annotations
|
|
315
|
+
or len(categories) != expected_categories
|
|
316
|
+
):
|
|
317
|
+
return None
|
|
318
|
+
category_ids: list[int] = []
|
|
319
|
+
for category in categories:
|
|
320
|
+
if not isinstance(category, dict):
|
|
321
|
+
return None
|
|
322
|
+
category_id = category.get("id")
|
|
323
|
+
if not isinstance(category_id, int) or isinstance(category_id, bool):
|
|
324
|
+
return None
|
|
325
|
+
category_ids.append(category_id)
|
|
326
|
+
if len(category_ids) != len(set(category_ids)):
|
|
327
|
+
return None
|
|
328
|
+
expected_category_ids = (
|
|
329
|
+
COCO_PERSON_KEYPOINT_CATEGORY_IDS
|
|
330
|
+
if annotation_path.name == "person_keypoints_val2017.json"
|
|
331
|
+
else COCO_CATEGORY_IDS
|
|
332
|
+
)
|
|
333
|
+
if set(category_ids) != expected_category_ids:
|
|
334
|
+
return None
|
|
335
|
+
if not _coco_task_annotations_valid(
|
|
336
|
+
annotation_records,
|
|
337
|
+
image_ids=set(image_ids),
|
|
338
|
+
category_ids=set(category_ids),
|
|
339
|
+
image_shapes=image_shapes,
|
|
340
|
+
task=task,
|
|
341
|
+
):
|
|
342
|
+
return None
|
|
343
|
+
return set(names)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _coco_task_annotations_valid(
|
|
347
|
+
annotation_records: list[Any],
|
|
348
|
+
*,
|
|
349
|
+
image_ids: set[int],
|
|
350
|
+
category_ids: set[int],
|
|
351
|
+
image_shapes: dict[int, tuple[int, int] | None],
|
|
352
|
+
task: str,
|
|
353
|
+
) -> bool:
|
|
354
|
+
"""Validate task-specific COCO annotation payloads for readiness and APIs."""
|
|
355
|
+
|
|
356
|
+
annotation_ids: list[int] = []
|
|
357
|
+
for record in annotation_records:
|
|
358
|
+
if not isinstance(record, dict):
|
|
359
|
+
return False
|
|
360
|
+
annotation_id = record.get("id")
|
|
361
|
+
image_id = record.get("image_id")
|
|
362
|
+
category_id = record.get("category_id")
|
|
363
|
+
if (
|
|
364
|
+
not isinstance(annotation_id, int)
|
|
365
|
+
or isinstance(annotation_id, bool)
|
|
366
|
+
or not isinstance(image_id, int)
|
|
367
|
+
or isinstance(image_id, bool)
|
|
368
|
+
or image_id not in image_ids
|
|
369
|
+
or not isinstance(category_id, int)
|
|
370
|
+
or isinstance(category_id, bool)
|
|
371
|
+
or category_id not in category_ids
|
|
372
|
+
):
|
|
373
|
+
return False
|
|
374
|
+
image_shape = image_shapes.get(image_id)
|
|
375
|
+
if image_shape is None:
|
|
376
|
+
return False
|
|
377
|
+
image_height, image_width = image_shape
|
|
378
|
+
bbox = record.get("bbox")
|
|
379
|
+
if (
|
|
380
|
+
not isinstance(bbox, list)
|
|
381
|
+
or len(bbox) != 4
|
|
382
|
+
or any(
|
|
383
|
+
not isinstance(value, (int, float))
|
|
384
|
+
or isinstance(value, bool)
|
|
385
|
+
or not np.isfinite(value)
|
|
386
|
+
for value in bbox
|
|
387
|
+
)
|
|
388
|
+
or bbox[2] <= 0
|
|
389
|
+
or bbox[3] <= 0
|
|
390
|
+
or bbox[0] >= image_width
|
|
391
|
+
or bbox[0] + bbox[2] <= 0
|
|
392
|
+
or bbox[1] >= image_height
|
|
393
|
+
or bbox[1] + bbox[3] <= 0
|
|
394
|
+
):
|
|
395
|
+
return False
|
|
396
|
+
area = record.get("area")
|
|
397
|
+
iscrowd = record.get("iscrowd")
|
|
398
|
+
if (
|
|
399
|
+
not isinstance(area, (int, float))
|
|
400
|
+
or isinstance(area, bool)
|
|
401
|
+
or not np.isfinite(area)
|
|
402
|
+
or area <= 0
|
|
403
|
+
or area > image_height * image_width
|
|
404
|
+
or not isinstance(iscrowd, int)
|
|
405
|
+
or isinstance(iscrowd, bool)
|
|
406
|
+
or iscrowd not in {0, 1}
|
|
407
|
+
):
|
|
408
|
+
return False
|
|
409
|
+
if task == "pose_estimation" and area > bbox[2] * bbox[3]:
|
|
410
|
+
return False
|
|
411
|
+
if task == "instance_segmentation":
|
|
412
|
+
segmentation = record.get("segmentation")
|
|
413
|
+
if isinstance(segmentation, list):
|
|
414
|
+
if not segmentation or any(
|
|
415
|
+
not isinstance(polygon, list)
|
|
416
|
+
or len(polygon) < 6
|
|
417
|
+
or len(polygon) % 2
|
|
418
|
+
or any(
|
|
419
|
+
not isinstance(value, (int, float))
|
|
420
|
+
or isinstance(value, bool)
|
|
421
|
+
or not np.isfinite(value)
|
|
422
|
+
for value in polygon
|
|
423
|
+
)
|
|
424
|
+
or not _has_positive_polygon_area(polygon)
|
|
425
|
+
or not _polygon_has_positive_image_overlap(
|
|
426
|
+
polygon, image_shapes.get(image_id)
|
|
427
|
+
)
|
|
428
|
+
or not _valid_coco_polygon(polygon, image_shapes.get(image_id))
|
|
429
|
+
for polygon in segmentation
|
|
430
|
+
):
|
|
431
|
+
return False
|
|
432
|
+
elif isinstance(segmentation, dict):
|
|
433
|
+
if not _valid_coco_rle(segmentation, image_shapes.get(image_id)):
|
|
434
|
+
return False
|
|
435
|
+
else:
|
|
436
|
+
return False
|
|
437
|
+
if task == "pose_estimation":
|
|
438
|
+
keypoints = record.get("keypoints")
|
|
439
|
+
num_keypoints = record.get("num_keypoints")
|
|
440
|
+
if (
|
|
441
|
+
not isinstance(keypoints, list)
|
|
442
|
+
or len(keypoints) != 51
|
|
443
|
+
or any(
|
|
444
|
+
not isinstance(value, (int, float))
|
|
445
|
+
or isinstance(value, bool)
|
|
446
|
+
or not np.isfinite(value)
|
|
447
|
+
for value in keypoints
|
|
448
|
+
)
|
|
449
|
+
or not isinstance(num_keypoints, int)
|
|
450
|
+
or isinstance(num_keypoints, bool)
|
|
451
|
+
or not 0 <= num_keypoints <= 17
|
|
452
|
+
or any(
|
|
453
|
+
keypoints[index] not in {0, 1, 2}
|
|
454
|
+
for index in range(2, len(keypoints), 3)
|
|
455
|
+
)
|
|
456
|
+
or num_keypoints
|
|
457
|
+
!= sum(keypoints[index] > 0 for index in range(2, len(keypoints), 3))
|
|
458
|
+
):
|
|
459
|
+
return False
|
|
460
|
+
height, width = image_shape
|
|
461
|
+
if any(
|
|
462
|
+
keypoints[index + 2] > 0
|
|
463
|
+
and not (
|
|
464
|
+
0 <= keypoints[index] < width and 0 <= keypoints[index + 1] < height
|
|
465
|
+
)
|
|
466
|
+
for index in range(0, len(keypoints), 3)
|
|
467
|
+
):
|
|
468
|
+
return False
|
|
469
|
+
annotation_ids.append(annotation_id)
|
|
470
|
+
return len(annotation_ids) == len(set(annotation_ids))
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def _decode_coco_rle_counts(counts: str) -> list[int] | None:
|
|
474
|
+
"""Decode COCO's compact RLE run-length string without trusting its payload."""
|
|
475
|
+
|
|
476
|
+
run_counts: list[int] = []
|
|
477
|
+
position = 0
|
|
478
|
+
while position < len(counts):
|
|
479
|
+
value = 0
|
|
480
|
+
shift = 0
|
|
481
|
+
more = True
|
|
482
|
+
while more:
|
|
483
|
+
if position >= len(counts):
|
|
484
|
+
return None
|
|
485
|
+
code = ord(counts[position]) - 48
|
|
486
|
+
position += 1
|
|
487
|
+
if not 0 <= code <= 0x3F:
|
|
488
|
+
return None
|
|
489
|
+
value |= (code & 0x1F) << shift
|
|
490
|
+
more = bool(code & 0x20)
|
|
491
|
+
shift += 5
|
|
492
|
+
if shift > 60:
|
|
493
|
+
return None
|
|
494
|
+
if not more and code & 0x10:
|
|
495
|
+
value |= -1 << shift
|
|
496
|
+
if len(run_counts) > 2:
|
|
497
|
+
value += run_counts[-2]
|
|
498
|
+
if value < 0:
|
|
499
|
+
return None
|
|
500
|
+
run_counts.append(value)
|
|
501
|
+
return run_counts
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def _valid_coco_rle(
|
|
505
|
+
segmentation: dict[str, Any], image_shape: tuple[int, int] | None
|
|
506
|
+
) -> bool:
|
|
507
|
+
"""Validate and decode an RLE mask against its referenced COCO image shape."""
|
|
508
|
+
|
|
509
|
+
counts = segmentation.get("counts")
|
|
510
|
+
size = segmentation.get("size")
|
|
511
|
+
if (
|
|
512
|
+
image_shape is None
|
|
513
|
+
or not isinstance(size, list)
|
|
514
|
+
or len(size) != 2
|
|
515
|
+
or any(
|
|
516
|
+
not isinstance(value, int) or isinstance(value, bool) or value <= 0
|
|
517
|
+
for value in size
|
|
518
|
+
)
|
|
519
|
+
or tuple(size) != image_shape
|
|
520
|
+
or not isinstance(counts, (str, list))
|
|
521
|
+
):
|
|
522
|
+
return False
|
|
523
|
+
if isinstance(counts, list):
|
|
524
|
+
if any(
|
|
525
|
+
not isinstance(value, int) or isinstance(value, bool) or value < 0
|
|
526
|
+
for value in counts
|
|
527
|
+
):
|
|
528
|
+
return False
|
|
529
|
+
run_counts = counts
|
|
530
|
+
else:
|
|
531
|
+
run_counts = _decode_coco_rle_counts(counts)
|
|
532
|
+
if run_counts is None:
|
|
533
|
+
return False
|
|
534
|
+
if sum(run_counts) != math.prod(size):
|
|
535
|
+
return False
|
|
536
|
+
try:
|
|
537
|
+
encoded = (
|
|
538
|
+
coco_mask.frPyObjects(segmentation, size[0], size[1])
|
|
539
|
+
if isinstance(counts, list)
|
|
540
|
+
else segmentation
|
|
541
|
+
)
|
|
542
|
+
decoded = np.asarray(coco_mask.decode(encoded))
|
|
543
|
+
except (RuntimeError, TypeError, ValueError):
|
|
544
|
+
return False
|
|
545
|
+
return decoded.shape == tuple(size) and bool(np.any(decoded))
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def _valid_coco_polygon(
|
|
549
|
+
polygon: list[int | float], image_shape: tuple[int, int] | None
|
|
550
|
+
) -> bool:
|
|
551
|
+
"""Require a COCO polygon to rasterize to foreground in its image."""
|
|
552
|
+
|
|
553
|
+
if image_shape is None:
|
|
554
|
+
return True
|
|
555
|
+
height, width = image_shape
|
|
556
|
+
try:
|
|
557
|
+
encoded = coco_mask.frPyObjects([polygon], height, width)
|
|
558
|
+
decoded = np.asarray(coco_mask.decode(encoded))
|
|
559
|
+
except (RuntimeError, TypeError, ValueError):
|
|
560
|
+
return False
|
|
561
|
+
return bool(np.any(decoded))
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _coco_ready(root: Path, task: str) -> bool:
|
|
565
|
+
"""Check the complete COCO 2017 image split and task annotation metadata."""
|
|
566
|
+
|
|
567
|
+
annotation_name = (
|
|
568
|
+
"person_keypoints_val2017.json"
|
|
569
|
+
if task == "pose_estimation"
|
|
570
|
+
else "instances_val2017.json"
|
|
571
|
+
)
|
|
572
|
+
annotation_names = _load_coco_image_names(root / annotation_name, task)
|
|
573
|
+
if annotation_names is None:
|
|
574
|
+
return False
|
|
575
|
+
image_dir = root / "val2017"
|
|
576
|
+
if not image_dir.is_dir():
|
|
577
|
+
return False
|
|
578
|
+
image_paths = [
|
|
579
|
+
path
|
|
580
|
+
for path in image_dir.iterdir()
|
|
581
|
+
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
|
|
582
|
+
]
|
|
583
|
+
if len(image_paths) != COCO_VALIDATION_SAMPLE_COUNT or any(
|
|
584
|
+
path.suffix.lower() != ".jpg" or COCO_IMAGE_PATTERN.fullmatch(path.stem) is None
|
|
585
|
+
for path in image_paths
|
|
586
|
+
):
|
|
587
|
+
return False
|
|
588
|
+
if {path.name for path in image_paths} != annotation_names:
|
|
589
|
+
return False
|
|
590
|
+
try:
|
|
591
|
+
payload = json.loads((root / annotation_name).read_text(encoding="utf-8"))
|
|
592
|
+
image_records = payload["images"]
|
|
593
|
+
image_shapes = {
|
|
594
|
+
record["file_name"]: (record["height"], record["width"])
|
|
595
|
+
for record in image_records
|
|
596
|
+
}
|
|
597
|
+
except (KeyError, TypeError, ValueError, json.JSONDecodeError, OSError):
|
|
598
|
+
return False
|
|
599
|
+
for image_path in image_paths:
|
|
600
|
+
expected_shape = image_shapes.get(image_path.name)
|
|
601
|
+
if (
|
|
602
|
+
not isinstance(expected_shape, tuple)
|
|
603
|
+
or len(expected_shape) != 2
|
|
604
|
+
or any(
|
|
605
|
+
not isinstance(value, int) or isinstance(value, bool) or value <= 0
|
|
606
|
+
for value in expected_shape
|
|
607
|
+
)
|
|
608
|
+
):
|
|
609
|
+
return False
|
|
610
|
+
try:
|
|
611
|
+
with Image.open(image_path) as image:
|
|
612
|
+
image.load()
|
|
613
|
+
width, height = image.size
|
|
614
|
+
except OSError:
|
|
615
|
+
return False
|
|
616
|
+
if (height, width) != expected_shape:
|
|
617
|
+
return False
|
|
618
|
+
return True
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def _dotav1_ready(root: Path) -> bool:
|
|
622
|
+
"""Check complete paired DOTAv1 validation images and labels."""
|
|
623
|
+
|
|
624
|
+
flat_image_dir = root / "images"
|
|
625
|
+
flat_images = _files_by_stem(flat_image_dir, IMAGE_SUFFIXES)
|
|
626
|
+
if flat_images is None:
|
|
627
|
+
return False
|
|
628
|
+
image_dir = flat_image_dir if flat_images else flat_image_dir / "val"
|
|
629
|
+
images = flat_images if flat_images else _files_by_stem(image_dir, IMAGE_SUFFIXES)
|
|
630
|
+
if images is None or len(images) != DOTAV1_VALIDATION_SAMPLE_COUNT:
|
|
631
|
+
return False
|
|
632
|
+
|
|
633
|
+
normalized_labels = _files_by_stem(root / "labels" / "val", {".txt"})
|
|
634
|
+
original_labels = _files_by_stem(root / "labels" / "val_original", {".txt"})
|
|
635
|
+
if normalized_labels is None or original_labels is None:
|
|
636
|
+
return False
|
|
637
|
+
label_stems = normalized_labels.keys() | original_labels.keys()
|
|
638
|
+
return images.keys() == label_stems
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _widerface_ready(root: Path) -> bool:
|
|
642
|
+
"""Check the complete WiderFace validation image tree and metadata files."""
|
|
643
|
+
|
|
644
|
+
required_files = (
|
|
645
|
+
"wider_face_val.mat",
|
|
646
|
+
"wider_easy_val.mat",
|
|
647
|
+
"wider_medium_val.mat",
|
|
648
|
+
"wider_hard_val.mat",
|
|
649
|
+
)
|
|
650
|
+
if not all((root / file_name).is_file() for file_name in required_files):
|
|
651
|
+
return False
|
|
652
|
+
expected_images = _load_widerface_image_names(root / "wider_face_val.mat")
|
|
653
|
+
if expected_images is None:
|
|
654
|
+
return False
|
|
655
|
+
image_root = root / "images"
|
|
656
|
+
if not image_root.is_dir():
|
|
657
|
+
return False
|
|
658
|
+
event_dirs = [path for path in image_root.iterdir() if path.is_dir()]
|
|
659
|
+
if len(event_dirs) != WIDERFACE_EVENT_COUNT or any(
|
|
660
|
+
WIDERFACE_EVENT_PATTERN.fullmatch(path.name) is None for path in event_dirs
|
|
661
|
+
):
|
|
662
|
+
return False
|
|
663
|
+
if {path.name for path in event_dirs} != expected_images.keys():
|
|
664
|
+
return False
|
|
665
|
+
actual_images = {
|
|
666
|
+
event_dir.name: {
|
|
667
|
+
path.name
|
|
668
|
+
for path in event_dir.iterdir()
|
|
669
|
+
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
|
|
670
|
+
}
|
|
671
|
+
for event_dir in event_dirs
|
|
672
|
+
}
|
|
673
|
+
image_shapes = _widerface_image_shapes(root, expected_images)
|
|
674
|
+
return (
|
|
675
|
+
actual_images == expected_images
|
|
676
|
+
and sum(len(image_names) for image_names in actual_images.values())
|
|
677
|
+
== WIDERFACE_VALIDATION_SAMPLE_COUNT
|
|
678
|
+
and image_shapes is not None
|
|
679
|
+
and _widerface_difficulty_metadata_ready(
|
|
680
|
+
root, expected_images, image_shapes=image_shapes
|
|
681
|
+
)
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def _widerface_difficulty_metadata_ready(
|
|
686
|
+
root: Path,
|
|
687
|
+
expected_images: dict[str, set[str]],
|
|
688
|
+
*,
|
|
689
|
+
image_shapes: list[list[tuple[int, int]]] | None = None,
|
|
690
|
+
) -> bool:
|
|
691
|
+
"""Validate WiderFace difficulty metadata and its decoded-image geometry."""
|
|
692
|
+
|
|
693
|
+
try:
|
|
694
|
+
main = loadmat(root / "wider_face_val.mat")
|
|
695
|
+
face_boxes = main["face_bbx_list"]
|
|
696
|
+
difficulties = [
|
|
697
|
+
loadmat(root / file_name)["gt_list"]
|
|
698
|
+
for file_name in (
|
|
699
|
+
"wider_easy_val.mat",
|
|
700
|
+
"wider_medium_val.mat",
|
|
701
|
+
"wider_hard_val.mat",
|
|
702
|
+
)
|
|
703
|
+
]
|
|
704
|
+
except (IndexError, KeyError, MatReadError, OSError, TypeError, ValueError):
|
|
705
|
+
return False
|
|
706
|
+
if len(face_boxes) != len(expected_images) or any(
|
|
707
|
+
len(table) != len(expected_images) for table in difficulties
|
|
708
|
+
):
|
|
709
|
+
return False
|
|
710
|
+
if image_shapes is not None and len(image_shapes) != len(expected_images):
|
|
711
|
+
return False
|
|
712
|
+
difficulty_has_eligible_face = [False] * len(difficulties)
|
|
713
|
+
for event_index, image_names in enumerate(expected_images.values()):
|
|
714
|
+
try:
|
|
715
|
+
event_faces = face_boxes[event_index][0]
|
|
716
|
+
except (IndexError, TypeError):
|
|
717
|
+
return False
|
|
718
|
+
if len(event_faces) != len(image_names):
|
|
719
|
+
return False
|
|
720
|
+
if image_shapes is not None and len(image_shapes[event_index]) != len(
|
|
721
|
+
image_names
|
|
722
|
+
):
|
|
723
|
+
return False
|
|
724
|
+
face_counts: list[int] = []
|
|
725
|
+
for image_index, face_entry in enumerate(event_faces):
|
|
726
|
+
try:
|
|
727
|
+
face_array = np.asarray(face_entry[0])
|
|
728
|
+
except (IndexError, TypeError):
|
|
729
|
+
return False
|
|
730
|
+
if (
|
|
731
|
+
face_array.ndim != 2
|
|
732
|
+
or face_array.shape[1] != 4
|
|
733
|
+
or len(face_array) == 0
|
|
734
|
+
or not np.issubdtype(face_array.dtype, np.number)
|
|
735
|
+
or np.issubdtype(face_array.dtype, np.complexfloating)
|
|
736
|
+
or not np.isfinite(face_array).all()
|
|
737
|
+
or (face_array[:, 2:] <= 0).any()
|
|
738
|
+
or len(np.unique(face_array, axis=0)) != len(face_array)
|
|
739
|
+
):
|
|
740
|
+
return False
|
|
741
|
+
if image_shapes is not None:
|
|
742
|
+
height, width = image_shapes[event_index][image_index]
|
|
743
|
+
if (
|
|
744
|
+
height <= 0
|
|
745
|
+
or width <= 0
|
|
746
|
+
or not (
|
|
747
|
+
(face_array[:, 0] < width)
|
|
748
|
+
& (face_array[:, 0] + face_array[:, 2] > 0)
|
|
749
|
+
& (face_array[:, 1] < height)
|
|
750
|
+
& (face_array[:, 1] + face_array[:, 3] > 0)
|
|
751
|
+
).all()
|
|
752
|
+
):
|
|
753
|
+
return False
|
|
754
|
+
face_counts.append(len(face_array))
|
|
755
|
+
for difficulty_index, table in enumerate(difficulties):
|
|
756
|
+
try:
|
|
757
|
+
event_indices = table[event_index][0]
|
|
758
|
+
except (IndexError, TypeError):
|
|
759
|
+
return False
|
|
760
|
+
if len(event_indices) != len(image_names):
|
|
761
|
+
return False
|
|
762
|
+
for image_index in range(len(event_faces)):
|
|
763
|
+
try:
|
|
764
|
+
keep_indices = np.asarray(event_indices[image_index][0])
|
|
765
|
+
except (IndexError, TypeError):
|
|
766
|
+
return False
|
|
767
|
+
if keep_indices.ndim == 0:
|
|
768
|
+
keep_indices = keep_indices.reshape(1)
|
|
769
|
+
elif keep_indices.ndim == 2:
|
|
770
|
+
if keep_indices.size == 0:
|
|
771
|
+
if keep_indices.shape[0] != 0:
|
|
772
|
+
return False
|
|
773
|
+
keep_indices = keep_indices.reshape(0)
|
|
774
|
+
elif keep_indices.shape[1] == 1:
|
|
775
|
+
keep_indices = keep_indices[:, 0]
|
|
776
|
+
else:
|
|
777
|
+
return False
|
|
778
|
+
elif keep_indices.ndim != 1:
|
|
779
|
+
return False
|
|
780
|
+
try:
|
|
781
|
+
valid_indices = (
|
|
782
|
+
np.isfinite(keep_indices).all()
|
|
783
|
+
and np.equal(keep_indices, np.trunc(keep_indices)).all()
|
|
784
|
+
)
|
|
785
|
+
except (TypeError, ValueError):
|
|
786
|
+
return False
|
|
787
|
+
if not valid_indices:
|
|
788
|
+
return False
|
|
789
|
+
if keep_indices.size and (
|
|
790
|
+
int(keep_indices.min()) < 1
|
|
791
|
+
or int(keep_indices.max()) > face_counts[image_index]
|
|
792
|
+
):
|
|
793
|
+
return False
|
|
794
|
+
if len(np.unique(keep_indices)) != keep_indices.size:
|
|
795
|
+
return False
|
|
796
|
+
difficulty_has_eligible_face[difficulty_index] |= bool(
|
|
797
|
+
keep_indices.size
|
|
798
|
+
)
|
|
799
|
+
if not all(difficulty_has_eligible_face):
|
|
800
|
+
return False
|
|
801
|
+
return True
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
def _flatten_matlab_strings(value: Any) -> list[str]:
|
|
805
|
+
"""Flatten strings stored inside nested MATLAB cell arrays."""
|
|
806
|
+
|
|
807
|
+
if isinstance(value, (str, np.str_)):
|
|
808
|
+
return [str(value)]
|
|
809
|
+
if isinstance(value, np.ndarray):
|
|
810
|
+
strings: list[str] = []
|
|
811
|
+
for item in value.flat:
|
|
812
|
+
strings.extend(_flatten_matlab_strings(item))
|
|
813
|
+
return strings
|
|
814
|
+
return []
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
def _load_widerface_image_names(annotation_path: Path) -> dict[str, set[str]] | None:
|
|
818
|
+
"""Load exact event and image identities from WiderFace validation metadata."""
|
|
819
|
+
|
|
820
|
+
try:
|
|
821
|
+
annotation = loadmat(annotation_path)
|
|
822
|
+
event_list = annotation["event_list"]
|
|
823
|
+
file_list = annotation["file_list"]
|
|
824
|
+
except (IndexError, KeyError, MatReadError, OSError, TypeError, ValueError):
|
|
825
|
+
return None
|
|
826
|
+
if len(event_list) != len(file_list):
|
|
827
|
+
return None
|
|
828
|
+
|
|
829
|
+
expected: dict[str, set[str]] = {}
|
|
830
|
+
for event_cell, file_cell in zip(event_list, file_list, strict=True):
|
|
831
|
+
event_names = _flatten_matlab_strings(event_cell)
|
|
832
|
+
image_stems = _flatten_matlab_strings(file_cell)
|
|
833
|
+
if (
|
|
834
|
+
len(event_names) != 1
|
|
835
|
+
or not image_stems
|
|
836
|
+
or len(image_stems) != len(set(image_stems))
|
|
837
|
+
or WIDERFACE_EVENT_PATTERN.fullmatch(event_names[0]) is None
|
|
838
|
+
or any(not stem or Path(stem).name != stem for stem in image_stems)
|
|
839
|
+
or event_names[0] in expected
|
|
840
|
+
):
|
|
841
|
+
return None
|
|
842
|
+
expected[event_names[0]] = {f"{stem}.jpg" for stem in image_stems}
|
|
843
|
+
if len(expected) != WIDERFACE_EVENT_COUNT:
|
|
844
|
+
return None
|
|
845
|
+
return expected
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def _widerface_image_shapes(
|
|
849
|
+
root: Path, expected_images: dict[str, set[str]]
|
|
850
|
+
) -> list[list[tuple[int, int]]] | None:
|
|
851
|
+
"""Load WiderFace image shapes in the annotation's event and file order."""
|
|
852
|
+
|
|
853
|
+
try:
|
|
854
|
+
annotation = loadmat(root / "wider_face_val.mat")
|
|
855
|
+
event_list = annotation["event_list"]
|
|
856
|
+
file_list = annotation["file_list"]
|
|
857
|
+
except (IndexError, KeyError, MatReadError, OSError, TypeError, ValueError):
|
|
858
|
+
return None
|
|
859
|
+
if len(event_list) != len(file_list) or len(event_list) != len(expected_images):
|
|
860
|
+
return None
|
|
861
|
+
|
|
862
|
+
all_shapes: list[list[tuple[int, int]]] = []
|
|
863
|
+
for event_cell, file_cell in zip(event_list, file_list, strict=True):
|
|
864
|
+
event_names = _flatten_matlab_strings(event_cell)
|
|
865
|
+
image_stems = _flatten_matlab_strings(file_cell)
|
|
866
|
+
if (
|
|
867
|
+
len(event_names) != 1
|
|
868
|
+
or not image_stems
|
|
869
|
+
or expected_images.get(event_names[0])
|
|
870
|
+
!= {f"{stem}.jpg" for stem in image_stems}
|
|
871
|
+
):
|
|
872
|
+
return None
|
|
873
|
+
event_shapes: list[tuple[int, int]] = []
|
|
874
|
+
for stem in image_stems:
|
|
875
|
+
image_path = root / "images" / event_names[0] / f"{stem}.jpg"
|
|
876
|
+
if image_path.is_symlink():
|
|
877
|
+
return None
|
|
878
|
+
try:
|
|
879
|
+
with Image.open(image_path) as image:
|
|
880
|
+
image.load()
|
|
881
|
+
width, height = image.size
|
|
882
|
+
except OSError:
|
|
883
|
+
return None
|
|
884
|
+
event_shapes.append((height, width))
|
|
885
|
+
all_shapes.append(event_shapes)
|
|
886
|
+
return all_shapes
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def dense_dataset_ready(data_path: str | Path, dataset: str) -> bool:
|
|
890
|
+
"""Return whether a dense dataset matches its taxonomy and full validation split.
|
|
891
|
+
|
|
892
|
+
Args:
|
|
893
|
+
data_path: Organized dataset root.
|
|
894
|
+
dataset: Dense validation taxonomy.
|
|
895
|
+
|
|
896
|
+
Returns:
|
|
897
|
+
Whether the dataset has the expected filename identity, matched targets,
|
|
898
|
+
and complete validation sample count.
|
|
899
|
+
"""
|
|
900
|
+
|
|
901
|
+
root = Path(data_path).expanduser()
|
|
902
|
+
if _path_has_symlink_component(root) or not root.is_dir():
|
|
903
|
+
return False
|
|
904
|
+
normalized = dataset.lower()
|
|
905
|
+
if normalized == "nyu-depth":
|
|
906
|
+
images = _files_by_stem(
|
|
907
|
+
root / "images", {".jpg", ".jpeg", ".png"}, reject_symlinks=True
|
|
908
|
+
)
|
|
909
|
+
depths = _files_by_stem(root / "depth", {".npy"}, reject_symlinks=True)
|
|
910
|
+
if images is None or depths is None:
|
|
911
|
+
return False
|
|
912
|
+
if not (
|
|
913
|
+
len(images) == NYU_DEPTH_VALIDATION_SAMPLE_COUNT
|
|
914
|
+
and len(depths) == NYU_DEPTH_VALIDATION_SAMPLE_COUNT
|
|
915
|
+
and images.keys() == depths.keys()
|
|
916
|
+
):
|
|
917
|
+
return False
|
|
918
|
+
for stem, image_path in images.items():
|
|
919
|
+
try:
|
|
920
|
+
with Image.open(image_path) as image:
|
|
921
|
+
image.load()
|
|
922
|
+
image_shape = (image.height, image.width)
|
|
923
|
+
raw_depth = np.load(depths[stem], allow_pickle=False)
|
|
924
|
+
except (OSError, ValueError):
|
|
925
|
+
return False
|
|
926
|
+
if not np.issubdtype(raw_depth.dtype, np.number) or np.issubdtype(
|
|
927
|
+
raw_depth.dtype, np.complexfloating
|
|
928
|
+
):
|
|
929
|
+
return False
|
|
930
|
+
with np.errstate(over="ignore", invalid="ignore"):
|
|
931
|
+
depth = np.asarray(raw_depth, dtype=np.float32)
|
|
932
|
+
if (
|
|
933
|
+
depth.ndim != 2
|
|
934
|
+
or depth.shape != image_shape
|
|
935
|
+
or not np.isfinite(depth).all()
|
|
936
|
+
or bool((depth < 0).any())
|
|
937
|
+
or not bool(((depth > 0.001) & (depth < 100.0)).any())
|
|
938
|
+
):
|
|
939
|
+
return False
|
|
940
|
+
return True
|
|
941
|
+
|
|
942
|
+
images = _files_by_stem(
|
|
943
|
+
root / "images", {".jpg", ".jpeg", ".png"}, reject_symlinks=True
|
|
944
|
+
)
|
|
945
|
+
annotations = _files_by_stem(root / "annotations", {".png"}, reject_symlinks=True)
|
|
946
|
+
if images is None or annotations is None or images.keys() != annotations.keys():
|
|
947
|
+
return False
|
|
948
|
+
|
|
949
|
+
for stem, image_path in images.items():
|
|
950
|
+
try:
|
|
951
|
+
with Image.open(image_path) as image:
|
|
952
|
+
image.load()
|
|
953
|
+
image_shape = (image.height, image.width)
|
|
954
|
+
with Image.open(annotations[stem]) as annotation_image:
|
|
955
|
+
annotation = np.asarray(annotation_image)
|
|
956
|
+
except OSError:
|
|
957
|
+
return False
|
|
958
|
+
if normalized == "cityscapes" and annotation.ndim == 3:
|
|
959
|
+
if (
|
|
960
|
+
annotation.shape[2] not in {3, 4}
|
|
961
|
+
or not np.array_equal(annotation[..., 0], annotation[..., 1])
|
|
962
|
+
or not np.array_equal(annotation[..., 0], annotation[..., 2])
|
|
963
|
+
):
|
|
964
|
+
return False
|
|
965
|
+
annotation = annotation[..., 0]
|
|
966
|
+
if annotation.ndim != 2 or annotation.shape != image_shape:
|
|
967
|
+
return False
|
|
968
|
+
if normalized == "ade20k":
|
|
969
|
+
if annotation.dtype != np.uint8 or (
|
|
970
|
+
annotation.size and int(annotation.max()) > 150
|
|
971
|
+
):
|
|
972
|
+
return False
|
|
973
|
+
if not bool((annotation > 0).any()):
|
|
974
|
+
return False
|
|
975
|
+
elif normalized == "cityscapes":
|
|
976
|
+
if annotation.size and (
|
|
977
|
+
int(annotation.min()) < 0 or int(annotation.max()) > 255
|
|
978
|
+
):
|
|
979
|
+
return False
|
|
980
|
+
if not bool(np.all((annotation <= 33) | (annotation == 255))):
|
|
981
|
+
return False
|
|
982
|
+
train_ids = CITYSCAPES_SOURCE_TO_TRAIN_ID[annotation.astype(np.uint8)]
|
|
983
|
+
if not bool((train_ids != 255).any()):
|
|
984
|
+
return False
|
|
985
|
+
|
|
986
|
+
if normalized == "ade20k":
|
|
987
|
+
return (
|
|
988
|
+
len(images) == ADE20K_VALIDATION_SAMPLE_COUNT
|
|
989
|
+
and all(stem.startswith("ADE_val_") for stem in images)
|
|
990
|
+
and all(
|
|
991
|
+
path.suffix.lower() in {".jpg", ".jpeg"} for path in images.values()
|
|
992
|
+
)
|
|
993
|
+
and all(
|
|
994
|
+
not (root / file_name).is_symlink() and (root / file_name).is_file()
|
|
995
|
+
for file_name in ADE20K_METADATA_FILES
|
|
996
|
+
)
|
|
997
|
+
)
|
|
998
|
+
if normalized == "cityscapes":
|
|
999
|
+
city_counts: dict[str, int] = {}
|
|
1000
|
+
for stem in images:
|
|
1001
|
+
match = CITYSCAPES_SAMPLE_ID_PATTERN.fullmatch(stem)
|
|
1002
|
+
if match is not None:
|
|
1003
|
+
city = match.group("city")
|
|
1004
|
+
city_counts[city] = city_counts.get(city, 0) + 1
|
|
1005
|
+
return (
|
|
1006
|
+
len(images) == CITYSCAPES_VALIDATION_SAMPLE_COUNT
|
|
1007
|
+
and all(
|
|
1008
|
+
CITYSCAPES_SAMPLE_ID_PATTERN.fullmatch(stem) is not None
|
|
1009
|
+
for stem in images
|
|
1010
|
+
)
|
|
1011
|
+
and all(path.suffix.lower() == ".png" for path in images.values())
|
|
1012
|
+
and (
|
|
1013
|
+
CITYSCAPES_VALIDATION_SAMPLE_COUNT != 500
|
|
1014
|
+
or city_counts == CITYSCAPES_VALIDATION_CITY_COUNTS
|
|
1015
|
+
)
|
|
1016
|
+
)
|
|
1017
|
+
return False
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def dataset_ready(data_path: str | Path, task: str, dataset: str | None = None) -> bool:
|
|
1021
|
+
"""Return whether an organized dataset matches its task, taxonomy, and full validation split.
|
|
1022
|
+
|
|
1023
|
+
Args:
|
|
1024
|
+
data_path: Organized dataset root.
|
|
1025
|
+
task: Canonical vision task.
|
|
1026
|
+
dataset: Optional validation taxonomy.
|
|
1027
|
+
|
|
1028
|
+
Returns:
|
|
1029
|
+
Whether the dataset has the expected identity, metadata, and sample count.
|
|
1030
|
+
"""
|
|
1031
|
+
|
|
1032
|
+
root = Path(data_path).expanduser()
|
|
1033
|
+
normalized_task = normalize_vision_task(task)
|
|
1034
|
+
expected_dataset = {
|
|
1035
|
+
"image_classification": "imagenet",
|
|
1036
|
+
"object_detection": "coco",
|
|
1037
|
+
"instance_segmentation": "coco",
|
|
1038
|
+
"pose_estimation": "coco",
|
|
1039
|
+
"face_detection": "widerface",
|
|
1040
|
+
"obb": "dotav1",
|
|
1041
|
+
"depth_estimation": "nyu-depth",
|
|
1042
|
+
}.get(normalized_task)
|
|
1043
|
+
normalized_dataset = (dataset or expected_dataset or "").lower()
|
|
1044
|
+
|
|
1045
|
+
if normalized_task == "semantic_segmentation":
|
|
1046
|
+
return dense_dataset_ready(root, normalized_dataset or "ade20k")
|
|
1047
|
+
if expected_dataset is None or normalized_dataset != expected_dataset:
|
|
1048
|
+
return False
|
|
1049
|
+
if normalized_task == "image_classification":
|
|
1050
|
+
return _imagenet_ready(root)
|
|
1051
|
+
if normalized_task in {
|
|
1052
|
+
"object_detection",
|
|
1053
|
+
"instance_segmentation",
|
|
1054
|
+
"pose_estimation",
|
|
1055
|
+
}:
|
|
1056
|
+
return _coco_ready(root, normalized_task)
|
|
1057
|
+
if normalized_task == "face_detection":
|
|
1058
|
+
return _widerface_ready(root)
|
|
1059
|
+
if normalized_task == "obb":
|
|
1060
|
+
return _dotav1_ready(root)
|
|
1061
|
+
if normalized_task == "depth_estimation":
|
|
1062
|
+
return dense_dataset_ready(root, normalized_dataset)
|
|
1063
|
+
return False
|