├── CONTRIBUTING.md ├── DATA.md ├── LICENSE ├── README.md ├── detection ├── config.py ├── data.py ├── dataset │ ├── __init__.py │ ├── cls_mapping_coco_voc.py │ ├── coco.py │ └── voc.py ├── modeling │ ├── __init__.py │ └── generalized_stac_rcnn.py ├── predict.py ├── scripts │ ├── coco │ │ ├── eval_stg1.sh │ │ ├── train_stac.sh │ │ ├── train_stg1.sh │ │ └── train_stg2.sh │ └── voc │ │ ├── eval_stg1.sh │ │ ├── train_stac.sh │ │ ├── train_stg1.sh │ │ └── train_stg2.sh ├── train_stg1.py ├── train_stg2.py └── utils │ ├── __init__.py │ ├── augmentation.py │ └── stac_helper.py ├── docs ├── TroubleShooting.md └── diagram.png ├── prepare_datasets ├── data │ └── STAC_JSON.tar ├── prepare_coco_data.py └── voc_instruction.md ├── requirements.txt └── third_party ├── FasterRCNN ├── FasterRCNN │ ├── BALLOON.md │ ├── NOTES.md │ ├── README.md │ ├── __init__.py │ ├── common.py │ ├── config.py │ ├── convert_d2 │ │ ├── README.md │ │ └── convert_d2.py │ ├── data.py │ ├── dataset │ │ ├── __init__.py │ │ ├── balloon.py │ │ ├── coco.py │ │ └── dataset.py │ ├── eval.py │ ├── modeling │ │ ├── __init__.py │ │ ├── backbone.py │ │ ├── generalized_rcnn.py │ │ ├── model_box.py │ │ ├── model_cascade.py │ │ ├── model_fpn.py │ │ ├── model_frcnn.py │ │ ├── model_mrcnn.py │ │ └── model_rpn.py │ ├── predict.py │ ├── train.py │ ├── utils │ │ ├── README.md │ │ ├── __init__.py │ │ ├── box_ops.py │ │ ├── custom.py │ │ └── np_box_ops.py │ └── viz.py └── LICENSE ├── auto_augment ├── LICENSE └── auto_augment │ ├── __init__.py │ ├── augmentations.py │ ├── custom_ops.py │ ├── policies.py │ ├── shake_drop.py │ ├── shake_shake.py │ └── wrn.py └── tensorpack ├── .lgtm.yml ├── CHANGES.md ├── LICENSE ├── README.md ├── docs ├── Makefile ├── README.md ├── _static │ ├── build_toc.js │ ├── build_toc_group.js │ ├── favicon.ico │ ├── jquery-3.2.1.min.js │ └── sanitize_desc_name.js ├── _templates │ └── layout.html ├── conf.py ├── index.rst ├── modules │ ├── callbacks.rst │ ├── contrib.rst │ ├── dataflow.dataset.rst │ ├── dataflow.imgaug.rst │ ├── dataflow.rst │ ├── graph_builder.rst │ ├── index.rst │ ├── input_source.rst │ ├── models.rst │ ├── predict.rst │ ├── tfutils.rst │ ├── train.rst │ └── utils.rst ├── requirements.txt ├── tensorpack.xml └── tutorial │ ├── callback.md │ ├── dataflow.md │ ├── efficient-dataflow.md │ ├── extend │ ├── augmentor.md │ ├── callback.md │ ├── dataflow.md │ ├── input-source.md │ ├── input-source.png │ ├── model.md │ └── trainer.md │ ├── faq.md │ ├── index.rst │ ├── inference.md │ ├── intro.rst │ ├── parallel-dataflow.md │ ├── performance-tuning.md │ ├── philosophy │ └── dataflow.md │ ├── save-load.md │ ├── summary.md │ ├── symbolic.md │ ├── trainer.md │ └── training-interface.md ├── readthedocs.yml ├── scripts ├── README.md ├── checkpoint-manipulate.py ├── checkpoint-prof.py ├── dump-model-params.py └── ls-checkpoint.py ├── setup.py ├── sotabench ├── sotabench.py ├── sotabench.yml └── sotabench_setup.sh ├── tensorpack ├── __init__.py ├── callbacks │ ├── __init__.py │ ├── base.py │ ├── concurrency.py │ ├── graph.py │ ├── group.py │ ├── hooks.py │ ├── inference.py │ ├── inference_runner.py │ ├── misc.py │ ├── monitor.py │ ├── param.py │ ├── param_test.py │ ├── prof.py │ ├── saver.py │ ├── stats.py │ ├── steps.py │ ├── summary.py │ └── trigger.py ├── compat │ ├── __init__.py │ └── tensor_spec.py ├── contrib │ ├── __init__.py │ └── keras.py ├── dataflow │ ├── __init__.py │ ├── base.py │ ├── common.py │ ├── dataset │ │ ├── __init__.py │ │ ├── bsds500.py │ │ ├── caltech101.py │ │ ├── cifar.py │ │ ├── ilsvrc.py │ │ ├── mnist.py │ │ └── svhn.py │ ├── format.py │ ├── image.py │ ├── imgaug │ │ ├── __init__.py │ │ ├── base.py │ │ ├── convert.py │ │ ├── crop.py │ │ ├── deform.py │ │ ├── external.py │ │ ├── geometry.py │ │ ├── imgaug_test.py │ │ ├── imgproc.py │ │ ├── meta.py │ │ ├── misc.py │ │ ├── noise.py │ │ ├── paste.py │ │ └── transform.py │ ├── parallel.py │ ├── parallel_map.py │ ├── raw.py │ ├── remote.py │ ├── serialize.py │ └── serialize_test.py ├── graph_builder │ ├── __init__.py │ ├── distributed.py │ ├── model_desc.py │ ├── training.py │ └── utils.py ├── input_source │ ├── __init__.py │ ├── input_source.py │ └── input_source_base.py ├── libinfo.py ├── models │ ├── __init__.py │ ├── _old_batch_norm.py │ ├── batch_norm.py │ ├── common.py │ ├── conv2d.py │ ├── fc.py │ ├── layer_norm.py │ ├── linearwrap.py │ ├── models_test.py │ ├── nonlin.py │ ├── pool.py │ ├── registry.py │ ├── regularize.py │ ├── shape_utils.py │ ├── shapes.py │ ├── tflayer.py │ └── utils.py ├── predict │ ├── __init__.py │ ├── base.py │ ├── concurrency.py │ ├── config.py │ ├── dataset.py │ ├── feedfree.py │ └── multigpu.py ├── tfutils │ ├── __init__.py │ ├── argscope.py │ ├── collection.py │ ├── common.py │ ├── dependency.py │ ├── distributed.py │ ├── export.py │ ├── gradproc.py │ ├── model_utils.py │ ├── optimizer.py │ ├── scope_utils.py │ ├── sesscreate.py │ ├── sessinit.py │ ├── summary.py │ ├── symbolic_functions.py │ ├── tower.py │ ├── unit_tests.py │ ├── varmanip.py │ └── varreplace.py ├── train │ ├── __init__.py │ ├── base.py │ ├── config.py │ ├── interface.py │ ├── model_desc.py │ ├── tower.py │ ├── trainers.py │ └── utility.py └── utils │ ├── __init__.py │ ├── argtools.py │ ├── compatible_serialize.py │ ├── concurrency.py │ ├── debug.py │ ├── develop.py │ ├── fs.py │ ├── gpu.py │ ├── loadcaffe.py │ ├── logger.py │ ├── naming.py │ ├── nvml.py │ ├── palette.py │ ├── serialize.py │ ├── stats.py │ ├── timer.py │ ├── utils.py │ └── viz.py ├── tests ├── benchmark-serializer.py ├── case_script.py ├── dev │ └── git-hooks │ │ └── pre-commit ├── install-tensorflow.sh ├── run-tests.sh ├── test_char_rnn.py ├── test_infogan.py ├── test_mnist.py ├── test_mnist_similarity.py └── test_resnet.py └── tox.ini /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement (CLA). You (or your employer) retain the copyright to your 10 | contribution; this simply gives us permission to use and redistribute your 11 | contributions as part of the project. Head over to 12 | to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult 23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 24 | information on using pull requests. 25 | 26 | ## Community Guidelines 27 | 28 | This project follows 29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). -------------------------------------------------------------------------------- /DATA.md: -------------------------------------------------------------------------------- 1 | ## Environment setting 2 | 3 | ```bash 4 | export PRJROOT=/path/to/your/project/directory/STAC 5 | export DATAROOT=/path/to/your/dataroot 6 | export RESULTDIR=/path/to/save/model 7 | export COCODIR=$DATAROOT/coco 8 | export VOCDIR=$DATAROOT/voc 9 | export PYTHONPATH=$PYTHONPATH:${PRJROOT}/third_party/FasterRCNN:${PRJROOT}/third_party/auto_augment:${PRJROOT}/third_party/tensorpack 10 | ``` 11 | 12 | ## Prepare data 13 | 14 | ### Download COCO data 15 | 16 | ```bash 17 | mkdir -p ${COCODIR} 18 | cd ${COCODIR} 19 | 20 | wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip 21 | wget http://images.cocodataset.org/zips/train2017.zip 22 | wget http://images.cocodataset.org/zips/val2017.zip 23 | wget http://images.cocodataset.org/zips/unlabeled2017.zip 24 | 25 | unzip annotations_trainval2017.zip -d . 26 | unzip -q train2017.zip -d . 27 | unzip -q val2017.zip -d . 28 | unzip -q unlabeled2017.zip -d . 29 | 30 | # resulting format 31 | # ${COCODIR} 32 | # - train2017 33 | # - xxx.jpg 34 | # - val2017 35 | # - xxx.jpg 36 | # - unlabled2017 37 | # - xxx.jpg 38 | # - annotations 39 | # - xxx.json 40 | # - ... 41 | ``` 42 | 43 | ### Download VOC data 44 | 45 | ```bash 46 | mkdir -p ${VOCDIR} 47 | cd ${VOCDIR} 48 | 49 | wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar 50 | wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar 51 | wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar 52 | tar -xf VOCtrainval_06-Nov-2007.tar 53 | tar -xf VOCtest_06-Nov-2007.tar 54 | tar -xf VOCtrainval_11-May-2012.tar 55 | 56 | # resulting format 57 | # ${VOCDIR} 58 | # - VOCdevkit 59 | # - VOC2007 60 | # - Annotations 61 | # - JPEGImages 62 | # - ... 63 | # - VOC2012 64 | # - Annotations 65 | # - JPEGImages 66 | # - ... 67 | ``` 68 | 69 | ### Generate labeled and unlabeled splits with different proportions of labeled data 70 | 71 | ```bash 72 | cd ${PRJROOT}/prepare_datasets 73 | 74 | # Format: 75 | # labeled split - .@ 76 | # unlabeled split - .@-unlabeled 77 | for seed in 1 2 3 4 5; do 78 | for percent in 1 2 5 10 20; do 79 | python3 prepare_coco_data.py --percent $percent --seed $seed & 80 | done 81 | done 82 | 83 | ``` 84 | 85 | ### Download JSON files for unlabeled images of COCO data and PASCAL VOC data 86 | 87 | ```bash 88 | cd ${DATAROOT} 89 | 90 | wget https://storage.cloud.google.com/gresearch/ssl_detection/STAC_JSON.tar 91 | tar -xf STAC_JSON.tar.gz 92 | 93 | # coco/annotations/instances_unlabeled2017.json 94 | # coco/annotations/semi_supervised/instances_unlabeledtrainval20class.json 95 | # voc/VOCdevkit/VOC2007/instances_diff_test.json 96 | # voc/VOCdevkit/VOC2007/instances_diff_trainval.json 97 | # voc/VOCdevkit/VOC2007/instances_test.json 98 | # voc/VOCdevkit/VOC2007/instances_trainval.json 99 | # voc/VOCdevkit/VOC2012/instances_diff_trainval.json 100 | # voc/VOCdevkit/VOC2012/instances_trainval.json 101 | 102 | ``` 103 | 104 | 105 | -------------------------------------------------------------------------------- /detection/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # -*- coding: utf-8 -*- 15 | # File: config.py 16 | 17 | import numpy as np 18 | import os 19 | import pprint 20 | import six 21 | import re 22 | 23 | from tensorpack.utils import logger 24 | from tensorpack.utils.gpu import get_num_gpu 25 | from FasterRCNN.config import * 26 | from FasterRCNN.config import config 27 | 28 | _C = config 29 | # The proposed method related hyperparams. 30 | # The majority of config argumetns are defined at third_party/FasterRCNN/FasterRCNN/config.py 31 | 32 | _C.TRAIN.STAGE = 1 # Stage of the training 33 | _C.TRAIN.CONFIDENCE = 0.9 # Confidence threshold 34 | _C.TRAIN.WU = 2. # loss weight of unlabled data 35 | _C.TRAIN.NO_PRN_LOSS = False # disable RPN loss 36 | _C.EVAL.PSEUDO_INFERENCE = False # Doing pseduo labeling inference process, this makes inferense on orignal image size. 37 | _C.TRAIN.AUGTYPE = 'strong' # augmentation type for unlabeled data 38 | _C.TRAIN.AUGTYPE_LAB = 'default' # augmentation type for labeled data 39 | _C.DATA.UNLABEL = ('',) # extra unlabeled json files (not always used) 40 | 41 | _C.freeze() # avoid typo / wrong config keys 42 | -------------------------------------------------------------------------------- /detection/dataset/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .coco import * 16 | from .voc import * 17 | -------------------------------------------------------------------------------- /detection/dataset/cls_mapping_coco_voc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Generate coco class mapping to voc 20 class. 16 | 17 | The resulting mapping dict will be hard-coded in coco.py 18 | 19 | python dataset/cls_mapping_coco_voc.py 20 | """ 21 | 22 | voc_class_names = [ 23 | "motorbike", "dog", "person", "horse", "sofa", "bicycle", "cow", "boat", 24 | "train", "car", "bird", "cat", "chair", "pottedplant", "sheep", "aeroplane", 25 | "bottle", "bus", "diningtable", "tvmonitor" 26 | ] 27 | 28 | coco_class_names = [ 29 | "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", 30 | "truck", "boat", "traffic light", "fire hydrant", "stop sign", 31 | "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", 32 | "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", 33 | "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", 34 | "baseball bat", "baseball glove", "skateboard", "surfboard", 35 | "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", 36 | "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", 37 | "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", 38 | "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", 39 | "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", 40 | "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", 41 | "hair drier", "toothbrush" 42 | ] 43 | 44 | 45 | identity_name_mapping = { 46 | # voc to coco name mapping of same class 47 | "aeroplane": "airplane", 48 | "motorbike": "motorcycle", 49 | "sofa": "couch", 50 | "pottedplant": "potted plant", 51 | "tvmonitor": "tv", 52 | "diningtable": "dining table", 53 | 54 | } 55 | 56 | 57 | COCO_id_to_category_id = { 58 | 13: 12, 59 | 14: 13, 60 | 15: 14, 61 | 16: 15, 62 | 17: 16, 63 | 18: 17, 64 | 19: 18, 65 | 20: 19, 66 | 21: 20, 67 | 22: 21, 68 | 23: 22, 69 | 24: 23, 70 | 25: 24, 71 | 27: 25, 72 | 28: 26, 73 | 31: 27, 74 | 32: 28, 75 | 33: 29, 76 | 34: 30, 77 | 35: 31, 78 | 36: 32, 79 | 37: 33, 80 | 38: 34, 81 | 39: 35, 82 | 40: 36, 83 | 41: 37, 84 | 42: 38, 85 | 43: 39, 86 | 44: 40, 87 | 46: 41, 88 | 47: 42, 89 | 48: 43, 90 | 49: 44, 91 | 50: 45, 92 | 51: 46, 93 | 52: 47, 94 | 53: 48, 95 | 54: 49, 96 | 55: 50, 97 | 56: 51, 98 | 57: 52, 99 | 58: 53, 100 | 59: 54, 101 | 60: 55, 102 | 61: 56, 103 | 62: 57, 104 | 63: 58, 105 | 64: 59, 106 | 65: 60, 107 | 67: 61, 108 | 70: 62, 109 | 72: 63, 110 | 73: 64, 111 | 74: 65, 112 | 75: 66, 113 | 76: 67, 114 | 77: 68, 115 | 78: 69, 116 | 79: 70, 117 | 80: 71, 118 | 81: 72, 119 | 82: 73, 120 | 84: 74, 121 | 85: 75, 122 | 86: 76, 123 | 87: 77, 124 | 88: 78, 125 | 89: 79, 126 | 90: 80 127 | } # noqa 128 | 129 | category_id_to_COCO_id = {v: k for k, v in COCO_id_to_category_id.items()} 130 | 131 | mapping = {} 132 | for i, name in enumerate(voc_class_names): 133 | index = coco_class_names.index(identity_name_mapping.get(name, name)) + 1 134 | coco_index = category_id_to_COCO_id.get(index, index) 135 | mapping[coco_index] = i + 1 136 | 137 | print( 138 | mapping 139 | ) # {64: 12, 1: 1, 67: 17, 3: 9, 4: 7, 5: 3, 6: 4, 7: 15, 72: 13, 9: 8, 44: 16, 2: 19, 16: 6, 17: 14, 18: 18, 19: 10, 20: 20, 21: 5, 62: 2, 63: 11} 140 | -------------------------------------------------------------------------------- /detection/modeling/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/detection/modeling/__init__.py -------------------------------------------------------------------------------- /detection/scripts/coco/eval_stg1.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # DATASET='coco_train2017.1@10' 16 | # UNLABELED_DATASET='${DATASET}-unlabeled' 17 | # CKPT_PATH=result/${DATASET} 18 | # PSEUDO_PATH=${CKPT_PATH}/PSEUDO_DATA 19 | # export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 20 | 21 | # Check pseudo path 22 | if [ ! -d ${PSEUDO_PATH} ]; then 23 | mkdir -p ${PSEUDO_PATH} 24 | fi 25 | 26 | # Evaluate the model for sanity check 27 | python3 predict.py \ 28 | --evaluate ${PSEUDO_PATH}/eval.json \ 29 | --load "${CKPT_PATH}"/model-180000 \ 30 | --config \ 31 | DATA.BASEDIR=${COCODIR} \ 32 | DATA.TRAIN="('${UNLABELED_DATASET}',)" 33 | 34 | # Extract pseudo label 35 | python3 predict.py \ 36 | --predict_unlabeled ${PSEUDO_PATH} \ 37 | --load "${CKPT_PATH}"/model-180000 \ 38 | --config \ 39 | DATA.BASEDIR=${COCODIR} \ 40 | DATA.TRAIN="('${UNLABELED_DATASET}',)" \ 41 | EVAL.PSEUDO_INFERENCE=True 42 | 43 | echo "Pseudo Label generation is done" 44 | echo ${PSEUDO_PATH} 45 | echo "Now start training STAC" 46 | -------------------------------------------------------------------------------- /detection/scripts/coco/train_stac.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # train_stac.sh 16 | 17 | DATASET=coco_train2017.1@10 18 | UNLABELED_DATASET=${DATASET}-unlabeled 19 | CKPT_PATH=result/${DATASET} 20 | PSEUDO_PATH=${CKPT_PATH}/PSEUDO_DATA 21 | export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 22 | 23 | . scripts/coco/train_stg1.sh 24 | . scripts/coco/eval_stg1.sh 25 | . scripts/coco/train_stg2.sh 26 | -------------------------------------------------------------------------------- /detection/scripts/coco/train_stg1.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # --simple_path makes train_log/${DATASET}/${EXPNAME} as exact location to save 16 | # DATASET='coco_train2017.1@10' 17 | # CKPT_PATH=result/${DATASET} 18 | # export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 19 | 20 | python3 train_stg1.py \ 21 | --logdir ${CKPT_PATH} --simple_path --config \ 22 | BACKBONE.WEIGHTS=${COCODIR}/ImageNet-R50-AlignPadding.npz \ 23 | DATA.BASEDIR=${COCODIR} \ 24 | DATA.TRAIN="('${DATASET}',)" \ 25 | MODE_MASK=False \ 26 | FRCNN.BATCH_PER_IM=64 \ 27 | PREPROC.TRAIN_SHORT_EDGE_SIZE="[500,800]" \ 28 | TRAIN.EVAL_PERIOD=20 \ 29 | TRAIN.AUGTYPE_LAB='default' \ 30 | $@ 31 | -------------------------------------------------------------------------------- /detection/scripts/coco/train_stg2.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # --simple_path makes train_log/${DATASET}/${EXPNAME} as exact location to save 16 | # DATASET='coco_train2017.1@10' 17 | # UNLABELED_DATASET='${DATASET}-unlabeled' 18 | # CKPT_PATH=result/${DATASET} 19 | # PSEUDO_PATH=${CKPT_PATH}/PSEUDO_DATA 20 | # export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 21 | 22 | python3 train_stg2.py \ 23 | --logdir=${CKPT_PATH}/STAC --simple_path \ 24 | --pseudo_path=${PSEUDO_PATH} \ 25 | --config \ 26 | BACKBONE.WEIGHTS=${COCODIR}/ImageNet-R50-AlignPadding.npz \ 27 | DATA.BASEDIR=${COCODIR} \ 28 | DATA.TRAIN="('${DATASET}',)" \ 29 | DATA.UNLABEL="('${UNLABELED_DATASET}',)" \ 30 | MODE_MASK=False \ 31 | FRCNN.BATCH_PER_IM=64 \ 32 | PREPROC.TRAIN_SHORT_EDGE_SIZE="[500,800]" \ 33 | TRAIN.EVAL_PERIOD=20 \ 34 | TRAIN.AUGTYPE_LAB='default' \ 35 | TRAIN.AUGTYPE='strong' \ 36 | TRAIN.CONFIDENCE=0.9 \ 37 | TRAIN.WU=2 \ 38 | $@ 39 | -------------------------------------------------------------------------------- /detection/scripts/voc/eval_stg1.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # DATASET='VOC2007/instances_trainval' 16 | # TESTSET='VOC2007/instances_test' 17 | # UNLABELED_DATASET="'VOC2012/instances_trainval','coco_unlabeledtrainval20class'" 18 | # CKPT_PATH=result/${DATASET} 19 | # PSEUDO_PATH=${CKPT_PATH}/PSEUDO_DATA 20 | # export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 21 | 22 | # Check pseudo path 23 | if [ ! -d ${PSEUDO_PATH} ]; then 24 | mkdir -p ${PSEUDO_PATH} 25 | fi 26 | 27 | # Evaluate the model for sanity check 28 | python3 predict.py \ 29 | --evaluate ${PSEUDO_PATH}/eval.json \ 30 | --load "${CKPT_PATH}"/model-80000 \ 31 | --config \ 32 | DATA.BASEDIR=${VOCDIR}/VOCdevkit \ 33 | DATA.TRAIN="(${UNLABELED_DATASET},)" \ 34 | DATA.VAL="('${TESTSET}',)" \ 35 | RPN.ANCHOR_SIZES="(8,16,32)" \ 36 | PREPROC.TEST_SHORT_EDGE_SIZE=600 \ 37 | TEST.FRCNN_NMS_THRESH=0.3 \ 38 | TEST.RESULT_SCORE_THRESH=0.0001 \ 39 | 40 | # Extract pseudo label 41 | python3 predict.py \ 42 | --predict_unlabeled ${PSEUDO_PATH} \ 43 | --load "${CKPT_PATH}"/model-80000 \ 44 | --config \ 45 | DATA.BASEDIR=${VOCDIR}/VOCdevkit \ 46 | DATA.TRAIN="(${UNLABELED_DATASET},)" \ 47 | RPN.ANCHOR_SIZES="(8,16,32)" \ 48 | PREPROC.TEST_SHORT_EDGE_SIZE=600 \ 49 | TEST.FRCNN_NMS_THRESH=0.3 \ 50 | TEST.RESULT_SCORE_THRESH=0.0001 \ 51 | EVAL.PSEUDO_INFERENCE=True 52 | 53 | echo "Pseudo Label generation is done" 54 | echo ${PSEUDO_PATH} 55 | echo "Now start training STAC" 56 | -------------------------------------------------------------------------------- /detection/scripts/voc/train_stac.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # train_stac.sh 16 | 17 | DATASET='VOC2007/instances_trainval' 18 | TESTSET='VOC2007/instances_test' 19 | UNLABELED_DATASET="'VOC2012/instances_trainval','coco_unlabeledtrainval20class'" 20 | CKPT_PATH=result/${DATASET} 21 | PSEUDO_PATH=${CKPT_PATH}/PSEUDO_DATA 22 | export CUDA_VISIBLE_DEVICES=0 23 | 24 | . scripts/voc/train_stg1.sh 25 | . scripts/voc/eval_stg1.sh 26 | . scripts/voc/train_stg2.sh 27 | -------------------------------------------------------------------------------- /detection/scripts/voc/train_stg1.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # --simple_path makes train_log/${DATASET}/${EXPNAME} as exact location to save 16 | # DATASET='VOC2007/instances_trainval' 17 | # TESTSET='VOC2007/instances_test' 18 | # CKPT_PATH=result/${DATASET} 19 | # export CUDA_VISIBLE_DEVICES=0 20 | 21 | python3 train_stg1.py \ 22 | --logdir=${CKPT_PATH} --simple_path --config \ 23 | BACKBONE.WEIGHTS=${COCODIR}/ImageNet-R50-AlignPadding.npz \ 24 | DATA.BASEDIR=${VOCDIR}/VOCdevkit \ 25 | DATA.TRAIN="('${DATASET}',)" \ 26 | DATA.VAL="('${TESTSET}',)" \ 27 | MODE_MASK=False \ 28 | PREPROC.MAX_SIZE=1000 \ 29 | FRCNN.BATCH_PER_IM=256 \ 30 | TRAIN.EVAL_PERIOD=20 \ 31 | TRAIN.LR_SCHEDULE=[7500,10000] \ 32 | TRAIN.NUM_GPUS=1 \ 33 | TRAIN.AUGTYPE_LAB='default' \ 34 | $@ 35 | -------------------------------------------------------------------------------- /detection/scripts/voc/train_stg2.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # --simple_path makes train_log/${DATASET}/${EXPNAME} as exact location to save 16 | # DATASET='VOC2007/instances_trainval' 17 | # TESTSET='VOC2007/instances_test' 18 | # UNLABELED_DATASET="'VOC2012/instances_trainval','coco_unlabeledtrainval20class'" 19 | # CKPT_PATH=result/${DATASET} 20 | # PSEUDO_PATH=${CKPT_PATH}/PSEUDO_DATA 21 | # export CUDA_VISIBLE_DEVICES=0 22 | 23 | python3 train_stg2.py \ 24 | --logdir=${CKPT_PATH}/STAC --simple_path \ 25 | --pseudo_path ${PSEUDO_PATH} \ 26 | --config \ 27 | BACKBONE.WEIGHTS=${COCODIR}/ImageNet-R50-AlignPadding.npz \ 28 | DATA.BASEDIR=${VOCDIR}/VOCdevkit \ 29 | DATA.TRAIN="('${DATASET}',)" \ 30 | DATA.VAL="('${TESTSET}',)" \ 31 | DATA.UNLABEL="(${UNLABELED_DATASET},)" \ 32 | MODE_MASK=False \ 33 | PREPROC.MAX_SIZE=1000 \ 34 | FRCNN.BATCH_PER_IM=256 \ 35 | TRAIN.EVAL_PERIOD=20 \ 36 | TRAIN.LR_SCHEDULE=[7500,15000,20000] \ 37 | TRAIN.GAMMA=0.3 \ 38 | TRAIN.NUM_GPUS=1 \ 39 | TRAIN.AUGTYPE_LAB='default' \ 40 | TRAIN.AUGTYPE='strong' \ 41 | TRAIN.CONFIDENCE=0.9 \ 42 | TRAIN.WU=2 \ 43 | $@ 44 | -------------------------------------------------------------------------------- /detection/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/detection/utils/__init__.py -------------------------------------------------------------------------------- /detection/utils/stac_helper.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Helper functions.""" 15 | 16 | from tensorpack.callbacks.base import Callback 17 | from tensorpack.compat import tfv1 as tf 18 | from tensorpack.tfutils.summary import add_moving_summary 19 | from tensorpack.utils import logger 20 | 21 | 22 | def add_moving_summary_no_nan(x, name=None): 23 | if not name: 24 | name = x.name 25 | add_moving_summary( 26 | tf.cond( 27 | tf.math.is_nan(x), 28 | lambda: tf.constant(0, tf.float32), 29 | lambda: x, 30 | name=name)) 31 | 32 | 33 | class WeightSyncCallBack(Callback): 34 | """Sync weight from source scope to target scope.""" 35 | 36 | def __init__(self, schedule, src_scope='stg2'): 37 | self.schedule = schedule 38 | self.src_scope = src_scope 39 | self.assign_ops = [] 40 | 41 | def _setup_graph(self): 42 | # get weight copy ops 43 | trainable_collection = tf.get_collection_ref( 44 | tf.GraphKeys.TRAINABLE_VARIABLES) 45 | mirrored_collection_name_map = dict() 46 | for var in trainable_collection: 47 | mirrored_collection_name_map[var.name.replace(self.src_scope + '/', 48 | '')] = var 49 | mirrored_collection_name_set = set(mirrored_collection_name_map.keys()) 50 | model_collection = tf.get_collection_ref(tf.GraphKeys.MODEL_VARIABLES) 51 | assign_ops = [] 52 | for var in model_collection: 53 | if var.name in mirrored_collection_name_set: 54 | op = var.assign(mirrored_collection_name_map[var.name]) 55 | assign_ops.append(op) 56 | self.assign_ops.extend(assign_ops) 57 | assert len(assign_ops) == len(trainable_collection) 58 | logger.info( 59 | '[WeightSyncCallBack] Create {} assign ops for WeightSyncCallBack, schedule = {}' 60 | .format(len(assign_ops), self.schedule)) 61 | 62 | def _sync_weight(self): 63 | sess = tf.get_default_session() 64 | sess.run(self.assign_ops) 65 | logger.info('[WeightSyncCallBack] Sync weight at epoch {}'.format( 66 | self.epoch_num)) 67 | 68 | def _before_epoch(self): 69 | if self.epoch_num in self.schedule: 70 | self._sync_weight() 71 | 72 | 73 | class PathLog(Callback): 74 | """Path logging callback.""" 75 | 76 | def __init__(self, path): 77 | self.path = path 78 | 79 | def before_train(self): 80 | self._after_epoch() 81 | 82 | def _after_epoch(self): 83 | logger.info('-' * 100) 84 | logger.info('Model save path: {}'.format(self.path)) 85 | logger.info('-' * 100) 86 | -------------------------------------------------------------------------------- /docs/TroubleShooting.md: -------------------------------------------------------------------------------- 1 | # Trouble Shootings 2 | 3 | ## Tensorflow version 4 | Though Tensorpack offical code requires Tensorflow version >= 1.5, we found 1.5 will cause OOM issue when training big models while 1.4 works well. But we did not verify other versions >1.5. 5 | 6 | ## libtensorflow_framework 7 | ``` 8 | tensorflow.python.framework.errors_impl.NotFoundError: libtensorflow_framework.so: cannot open shared object file: No such file or directory 9 | ``` 10 | Please deactivate and uninstall/delete horovod package 11 | ``` 12 | pip3 uninstall horovod 13 | sudo rm -rf /usr/local/lib/python3.5/dist-packages/horovod 14 | ``` 15 | 16 | ## \__Unicode Error\__ for pycocotools 17 | Sometimes, the installed pycocotools does not fully support Python 3. The reason is still unknown so far. Please check 18 | ``` 19 | python3.5/dist-packages/pycocotools/coco.py 20 | ``` 21 | If you line 308 looks like 22 | ``` 23 | if type(resFile) == str or type(resFile) == unicode: 24 | ``` 25 | replace it with, 26 | ``` 27 | if type(resFile) == str or type(resFile) == bytes: 28 | ``` 29 | 30 | ## Speed 31 | Increase `DATA.NUM_WORKERS` can speedup significantly if you have enought CPU cores. 32 | -------------------------------------------------------------------------------- /docs/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/docs/diagram.png -------------------------------------------------------------------------------- /prepare_datasets/prepare_coco_data.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #!/bin/bash 16 | """Generate labeled and unlabeled data for coco train. 17 | 18 | Example: 19 | python3 object_detection/prepare_coco_data.py 20 | """ 21 | 22 | import argparse 23 | import numpy as np 24 | import json 25 | import os 26 | 27 | assert 'COCODIR' in os.environ, 'Set COCODIR in bash' 28 | DATA_DIR = os.environ['COCODIR'] 29 | 30 | 31 | def prepare_coco_data(seed=1, percent=10.0, version=2017): 32 | """Prepare COCO data for Semi-supervised learning 33 | 34 | Args: 35 | seed: random seed for data split 36 | percent: percentage of labeled data 37 | version: COCO data version 38 | """ 39 | def _save_anno(name, images, annotations): 40 | """Save annotation 41 | """ 42 | print('>> Processing data {}.json saved ({} images {} annotations)'.format( 43 | name, len(images), len(annotations))) 44 | new_anno = {} 45 | new_anno['images'] = images 46 | new_anno['annotations'] = annotations 47 | new_anno['licenses'] = anno['licenses'] 48 | new_anno['categories'] = anno['categories'] 49 | new_anno['info'] = anno['info'] 50 | path = '{}/{}'.format(COCOANNODIR, 'semi_supervised') 51 | if not os.path.exists(path): 52 | os.mkdir(path) 53 | 54 | with open( 55 | '{root}/{folder}/{save_name}.json'.format( 56 | save_name=name, root=COCOANNODIR, folder='semi_supervised'), 57 | 'w') as f: 58 | json.dump(new_anno, f) 59 | print('>> Data {}.json saved ({} images {} annotations)'.format( 60 | name, len(images), len(annotations))) 61 | 62 | np.random.seed(seed) 63 | COCOANNODIR = os.path.join(DATA_DIR, 'annotations') 64 | 65 | anno = json.load(open(os.path.join(COCOANNODIR, 66 | 'instances_train{}.json'.format(version)))) 67 | 68 | image_list = anno['images'] 69 | labeled_tot = int(percent / 100. * len(image_list)) 70 | labeled_ind = np.random.choice(range(len(image_list)), size=labeled_tot) 71 | labeled_id = [] 72 | labeled_images = [] 73 | unlabeled_images = [] 74 | labeled_ind = set(labeled_ind) 75 | for i in range(len(image_list)): 76 | if i in labeled_ind: 77 | labeled_images.append(image_list[i]) 78 | labeled_id.append(image_list[i]['id']) 79 | else: 80 | unlabeled_images.append(image_list[i]) 81 | 82 | # get all annotations of labeled images 83 | labeled_id = set(labeled_id) 84 | labeled_annotations = [] 85 | unlabeled_annotations = [] 86 | for an in anno['annotations']: 87 | if an['image_id'] in labeled_id: 88 | labeled_annotations.append(an) 89 | else: 90 | unlabeled_annotations.append(an) 91 | 92 | # save labeled and unlabeled 93 | save_name = 'instances_train{version}.{seed}@{tot}'.format( 94 | version=version, seed=seed, tot=int(percent)) 95 | _save_anno(save_name, labeled_images, labeled_annotations) 96 | save_name = 'instances_train{version}.{seed}@{tot}-unlabeled'.format( 97 | version=version, seed=seed, tot=int(percent)) 98 | _save_anno(save_name, unlabeled_images, unlabeled_annotations) 99 | 100 | 101 | if __name__ == '__main__': 102 | 103 | parser = argparse.ArgumentParser() 104 | parser.add_argument('--percent', type=float, default=10) 105 | parser.add_argument('--version', type=int, default=2017) 106 | parser.add_argument('--seed', type=int, help='seed', default=1) 107 | 108 | args = parser.parse_args() 109 | prepare_coco_data(args.seed, args.percent, args.version) 110 | -------------------------------------------------------------------------------- /prepare_datasets/voc_instruction.md: -------------------------------------------------------------------------------- 1 | ## Environment setting 2 | 3 | - Make sure you are in STAC env3 environment. 4 | 5 | ```bash 6 | export PRJROOT=/path/to/your/project/directory/STAC 7 | export DATAROOT=/path/to/your/dataroot 8 | export COCODIR=$DATAROOT/coco 9 | export VOCDIR=$DATAROOT/voc 10 | export PYTHONPATH=$PYTHONPATH:${PRJROOT}/object_detection/FasterRCNN:${PRJROOT} 11 | ``` 12 | 13 | ## Download data 14 | 15 | ```bash 16 | mkdir -p $VOCDIR 17 | cd $VOCDIR 18 | 19 | wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar 20 | wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar 21 | wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar 22 | tar -xf VOCtrainval_06-Nov-2007.tar 23 | tar -xf VOCtest_06-Nov-2007.tar 24 | tar -xf VOCtrainval_11-May-2012.tar 25 | 26 | # resulting format 27 | # $VOCDIR 28 | # - VOCdevkit 29 | # - VOC2007 30 | # - Annotations 31 | # - JPEGImages 32 | # - ... 33 | # - VOC2012 34 | # - Annotations 35 | # - JPEGImages 36 | # - ... 37 | ``` 38 | 39 | ## Generate labeled and unlabeled splits 40 | 41 | - [Download format converter from pascal voc to coco.](https://github.com/CivilNet/Gemfield/blob/master/src/python/pascal_voc_xml2json/pascal_voc_xml2json.py) 42 | 43 | ```bash 44 | cd ${PRJROOT}/prepare_datasets 45 | 46 | wget https://raw.githubusercontent.com/CivilNet/Gemfield/master/src/python/pascal_voc_xml2json/pascal_voc_xml2json.py 47 | ``` 48 | 49 | - Generate json files of pascal voc labeled and unlabeled data. 50 | 51 | ```bash 52 | cd ${PRJROOT}/prepare_datasets 53 | 54 | # Format (TODO): 55 | # labeled split - .@ 56 | # unlabeled split - .@-unlabeled 57 | python3 prepare_voc_data.py --data_dir $VOCDIR 58 | ``` 59 | - Generate json file of pascal voc unlabeled data from coco labeled data with overlapping objects. ([TODO]: Chun-liang?) 60 | - See [here](prepare_datasets/coco_instruction.md) to download coco data. 61 | 62 | ```bash 63 | cd ${PRJROOT}/prepare_datasets 64 | 65 | # Format (TODO): 66 | 67 | python prepare_voc_data_from_coco.py 68 | ``` 69 | 70 | 71 | REMOVE below 72 | 73 | ## Train FRCNN on voc 74 | ```bash 75 | DATASET='VOC2007/instances_trainval' 76 | TESTSET='VOC2007/instances_test' 77 | CUDA_VISIBLE_DEVICES=0 python train.py --logdir train_log/${DATASET}/f50-fpn --config \ 78 | BACKBONE.WEIGHTS=./ImageNet-R50-AlignPadding.npz \ 79 | DATA.BASEDIR=${VOCDIR}/VOCdevkit \ 80 | DATA.TRAIN="('${DATASET}',)" \ 81 | DATA.VAL="('${TESTSET}',)" 82 | MODE_MASK=False 83 | ``` 84 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py 2 | easydict 3 | cython 4 | numpy==1.16.4 5 | tensorflow-gpu==1.14.0 6 | tqdm 7 | msgpack 8 | msgpack_numpy 9 | tabulate 10 | pillow 11 | imgaug==0.4.0 12 | deepdish 13 | gast==0.2.2 14 | scipy -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/BALLOON.md: -------------------------------------------------------------------------------- 1 | ## Balloon Demo 2 | 3 | This is a demo on how to train tensorpack's Mask R-CNN on a custom dataset. 4 | We use the [balloon dataset](https://github.com/matterport/Mask_RCNN/tree/master/samples/balloon) 5 | as an example. 6 | 7 | 1. Download and unzip the dataset: 8 | ``` 9 | wget https://github.com/matterport/Mask_RCNN/releases/download/v2.1/balloon_dataset.zip 10 | unzip balloon_dataset.zip 11 | ``` 12 | 13 | 2. (included already) Since this dataset is not in COCO format, we add a new file 14 | [dataset/balloon.py](dataset/balloon.py) to load the dataset. 15 | Refer to [dataset/dataset.py](dataset/dataset.py) on the required interface of a new dataset. 16 | 17 | 3. (included already) Register the names of the new dataset in `train.py` and `predict.py`, by calling `register_balloon("/path/to/balloon_dataset")` 18 | 19 | 4. Download a model pretrained on COCO from tensorpack model zoo: 20 | ``` 21 | wget http://models.tensorpack.com/FasterRCNN/COCO-MaskRCNN-R50FPN2x.npz 22 | ``` 23 | 24 | 5. Start fine-tuning on the new dataset: 25 | ``` 26 | ./train.py --config DATA.BASEDIR=~/data/balloon MODE_FPN=True \ 27 | "DATA.VAL=('balloon_val',)" "DATA.TRAIN=('balloon_train',)" \ 28 | TRAIN.BASE_LR=1e-3 TRAIN.EVAL_PERIOD=0 "TRAIN.LR_SCHEDULE=[1000]" \ 29 | "PREPROC.TRAIN_SHORT_EDGE_SIZE=[600,1200]" TRAIN.CHECKPOINT_PERIOD=1 DATA.NUM_WORKERS=1 \ 30 | --load COCO-MaskRCNN-R50FPN2x.npz --logdir train_log/balloon 31 | ``` 32 | 33 | 6. You can train as long as you want, but it only takes __a few minutes__ to produce nice results. 34 | You can visualize the results of the latest model by: 35 | ``` 36 | ./predict.py --config DATA.BASEDIR=~/data/balloon MODE_FPN=True \ 37 | "DATA.VAL=('balloon_val',)" "DATA.TRAIN=('balloon_train',)" \ 38 | --load train_log/balloon/checkpoint --predict ~/data/balloon/val/*.jpg 39 | ``` 40 | 41 | This command will produce images like this in your window: 42 | 43 | ![demo](https://user-images.githubusercontent.com/1381301/62665002-915ff880-b932-11e9-9f7e-f24f83d5d69c.jpg) 44 | 45 | 46 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/third_party/FasterRCNN/FasterRCNN/__init__.py -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/convert_d2/README.md: -------------------------------------------------------------------------------- 1 | ## Detectron2 Conversion 2 | 3 | We provide a script to migrate a model trained in 4 | [detectron2](https://github.com/facebookresearch/detectron2) to tensorpack. 5 | 6 | The script reads a detectron2 config file and a detectron2 pkl file in the model 7 | zoo. It produces a corresponding tensorpack configs, as well as a 8 | tensorpack-compatible checkpoint. 9 | 10 | It currently supports ResNet{50,101}-{C4,FPN}-{Faster,Mask,Cascade} R-CNN models 11 | in 12 | [detectron2 model zoo](https://github.com/facebookresearch/detectron2/blob/master/MODEL_ZOO.md). 13 | 14 | ### Usage: 15 | 16 | ``` 17 | # 1. Download the corresponding model from detectron2 model zoo 18 | # 2. Convert: 19 | 20 | $ python convert_d2.py --d2-config detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml --d2-pkl model_final_f10217.pkl --output R50FPN-d2-converted.npz 21 | # the script will print tensorpack configs 22 | 'MODE_MASK=True' 'MODE_FPN=True' 'BACKBONE.STRIDE_1X1=True' 'PREPROC.PIXEL_MEAN=[123.675,116.28,103.53]' 'PREPROC.PIXEL_STD=[1.0,1.0,1.0]' 23 | 24 | # 3. Use the above configs to verify the conversion is correct: 25 | $ ./predict.py --evaluate out.json --load R50FPN-d2-converted.npz --config DATA.BASEDIR=~/data/coco 'MODE_MASK=True' 'MODE_FPN=True' 'BACKBONE.STRIDE_1X1=True' 'PREPROC.PIXEL_MEAN=[123.675,116.28,103.53]' 'PREPROC.PIXEL_STD=[1.0,1.0,1.0]' 26 | 27 | # 4. Naively convert the model to a frozen pb file: 28 | $ ./predict.py --output-pb out.pb --load R50FPN-d2-converted.npz --config DATA.BASEDIR=~/data/coco 'MODE_MASK=True' 'MODE_FPN=True' 'BACKBONE.STRIDE_1X1=True' 'PREPROC.PIXEL_MEAN=[123.675,116.28,103.53]' 'PREPROC.PIXEL_STD=[1.0,1.0,1.0]' 29 | ``` 30 | 31 | Note: this script does not support arbitrary detectron2 config. When run against 32 | an unsupported config, it may fail silently and produce erroneous models. 33 | 34 | For models in detectron2 model zoo, there is a small incompatibility: 35 | 36 | * `POOLER_SAMPLING_RATIO=0` in RoIAlign: there is no equivalence in 37 | TensorFlow. Our RoIAlign only implements `POOLER_SAMPLING_RATIO=2`. The 38 | results are quite similar, and the final AP may be different by <0.5. 39 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/dataset/__init__.py: -------------------------------------------------------------------------------- 1 | from .dataset import * 2 | # from .coco import * 3 | from .balloon import * 4 | # from .voc import * 5 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/dataset/balloon.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import json 4 | from .dataset import DatasetSplit, DatasetRegistry 5 | 6 | __all__ = ["register_balloon"] 7 | 8 | 9 | class BalloonDemo(DatasetSplit): 10 | 11 | def __init__(self, base_dir, split): 12 | assert split in ["train", "val"] 13 | base_dir = os.path.expanduser(base_dir) 14 | self.imgdir = os.path.join(base_dir, split) 15 | assert os.path.isdir(self.imgdir), self.imgdir 16 | 17 | def training_roidbs(self): 18 | json_file = os.path.join(self.imgdir, "via_region_data.json") 19 | with open(json_file) as f: 20 | obj = json.load(f) 21 | 22 | ret = [] 23 | for _, v in obj.items(): 24 | fname = v["filename"] 25 | fname = os.path.join(self.imgdir, fname) 26 | 27 | roidb = {"file_name": fname} 28 | 29 | annos = v["regions"] 30 | 31 | boxes = [] 32 | segs = [] 33 | for _, anno in annos.items(): 34 | assert not anno["region_attributes"] 35 | anno = anno["shape_attributes"] 36 | px = anno["all_points_x"] 37 | py = anno["all_points_y"] 38 | poly = np.stack((px, py), axis=1) + 0.5 39 | maxxy = poly.max(axis=0) 40 | minxy = poly.min(axis=0) 41 | 42 | boxes.append([minxy[0], minxy[1], maxxy[0], maxxy[1]]) 43 | segs.append([poly]) 44 | N = len(annos) 45 | roidb["boxes"] = np.asarray(boxes, dtype=np.float32) 46 | roidb["segmentation"] = segs 47 | roidb["class"] = np.ones((N,), dtype=np.int32) 48 | roidb["is_crowd"] = np.zeros((N,), dtype=np.int8) 49 | ret.append(roidb) 50 | return ret 51 | 52 | 53 | def register_balloon(basedir): 54 | for split in ["train", "val"]: 55 | name = "balloon_" + split 56 | DatasetRegistry.register(name, lambda x=split: BalloonDemo(basedir, x)) 57 | DatasetRegistry.register_metadata(name, "class_names", ["BG", "balloon"]) 58 | 59 | 60 | if __name__ == "__main__": 61 | basedir = "~/data/balloon" 62 | roidbs = BalloonDemo(basedir, "train").training_roidbs() 63 | print("#images:", len(roidbs)) 64 | 65 | from viz import draw_annotation 66 | from tensorpack.utils.viz import interactive_imshow as imshow 67 | import cv2 68 | for r in roidbs: 69 | im = cv2.imread(r["file_name"]) 70 | vis = draw_annotation(im, r["boxes"], r["class"], r["segmentation"]) 71 | imshow(vis) 72 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/modeling/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/third_party/FasterRCNN/FasterRCNN/modeling/__init__.py -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/utils/README.md: -------------------------------------------------------------------------------- 1 | # Some third-party helper functions 2 | 3 | + box_ops.py: modified from 4 | [TF object detection API](https://github.com/tensorflow/models/blob/master/research/object_detection/core/box_list_ops.py). 5 | + np_box_ops.py: copied from 6 | [TF object detection API](https://github.com/tensorflow/models/blob/master/research/object_detection/utils/np_box_ops.py). 7 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/third_party/FasterRCNN/FasterRCNN/utils/__init__.py -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/utils/box_ops.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: box_ops.py 3 | 4 | import tensorflow as tf 5 | 6 | from tensorpack.tfutils.scope_utils import under_name_scope 7 | """This file is modified from https://github.com/tensorflow/models/blob/master/object_detection/core/box_list_ops.py""" 8 | 9 | 10 | @under_name_scope() 11 | def area(boxes): 12 | """ 13 | Args: 14 | boxes: nx4 floatbox 15 | 16 | Returns: 17 | n 18 | """ 19 | x_min, y_min, x_max, y_max = tf.split(boxes, 4, axis=1) 20 | return tf.squeeze((y_max - y_min) * (x_max - x_min), [1]) 21 | 22 | 23 | @under_name_scope() 24 | def pairwise_intersection(boxlist1, boxlist2): 25 | """Compute pairwise intersection areas between boxes. 26 | 27 | Args: 28 | boxlist1: Nx4 floatbox 29 | boxlist2: Mx4 30 | 31 | Returns: 32 | a tensor with shape [N, M] representing pairwise intersections 33 | """ 34 | x_min1, y_min1, x_max1, y_max1 = tf.split(boxlist1, 4, axis=1) 35 | x_min2, y_min2, x_max2, y_max2 = tf.split(boxlist2, 4, axis=1) 36 | all_pairs_min_ymax = tf.minimum(y_max1, tf.transpose(y_max2)) 37 | all_pairs_max_ymin = tf.maximum(y_min1, tf.transpose(y_min2)) 38 | intersect_heights = tf.maximum(0.0, all_pairs_min_ymax - all_pairs_max_ymin) 39 | all_pairs_min_xmax = tf.minimum(x_max1, tf.transpose(x_max2)) 40 | all_pairs_max_xmin = tf.maximum(x_min1, tf.transpose(x_min2)) 41 | intersect_widths = tf.maximum(0.0, all_pairs_min_xmax - all_pairs_max_xmin) 42 | return intersect_heights * intersect_widths 43 | 44 | 45 | @under_name_scope() 46 | def pairwise_iou(boxlist1, boxlist2): 47 | """Computes pairwise intersection-over-union between box collections. 48 | 49 | Args: 50 | boxlist1: Nx4 floatbox 51 | boxlist2: Mx4 52 | 53 | Returns: 54 | a tensor with shape [N, M] representing pairwise iou scores. 55 | """ 56 | intersections = pairwise_intersection(boxlist1, boxlist2) 57 | areas1 = area(boxlist1) 58 | areas2 = area(boxlist2) 59 | unions = ( 60 | tf.expand_dims(areas1, 1) + tf.expand_dims(areas2, 0) - intersections) 61 | return tf.where( 62 | tf.equal(intersections, 0.0), tf.zeros_like(intersections), 63 | tf.truediv(intersections, unions)) 64 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/utils/custom.py: -------------------------------------------------------------------------------- 1 | # Lint as: python3 2 | """TODO(zizhaoz): DO NOT SUBMIT without one-line documentation for custom. 3 | 4 | TODO(zizhaoz): DO NOT SUBMIT without a detailed description of custom. 5 | """ 6 | 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | from absl import app 12 | from absl import flags 13 | 14 | import numpy as np 15 | 16 | FLAGS = flags.FLAGS 17 | 18 | 19 | def find_bg_and_fg_proposals(scores, ratios=(0.1, 0.1)): 20 | """Find top kb% and bottom kf% boxes as background and forground indices. 21 | 22 | Args: 23 | ratios: a tuple of (float, float) or (int, int). Float represents ratios and 24 | int represents actual numbers. 25 | scores: Nx81: 0 is background 26 | """ 27 | if type(ratios[0]) is int: 28 | n1, n2 = ratios 29 | assert type(ratios[1]) is int 30 | else: 31 | assert type(ratios[1]) is float 32 | n1 = int(scores.shape[0] * ratios[0]) 33 | n2 = int(scores.shape[0] * ratios[1]) 34 | 35 | bg_scores = scores[:, 0] 36 | sorted_ind = np.argsort(bg_scores)[::-1] # top 37 | bg_ind = sorted_ind[:n1] # take the top k background regions 38 | fg_ind = sorted_ind[-n2:] # take the top k background regions 39 | 40 | return bg_ind, fg_ind 41 | -------------------------------------------------------------------------------- /third_party/FasterRCNN/FasterRCNN/utils/np_box_ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Operations for [N, 4] numpy arrays representing bounding boxes. 16 | 17 | Example box operations that are supported: 18 | * Areas: compute bounding box areas 19 | * IOU: pairwise intersection-over-union scores 20 | """ 21 | import numpy as np 22 | 23 | 24 | def area(boxes): 25 | """Computes area of boxes. 26 | 27 | Args: 28 | boxes: Numpy array with shape [N, 4] holding N boxes 29 | 30 | Returns: 31 | a numpy array with shape [N*1] representing box areas 32 | """ 33 | return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]) 34 | 35 | 36 | def intersection(boxes1, boxes2): 37 | """Compute pairwise intersection areas between boxes. 38 | 39 | Args: 40 | boxes1: a numpy array with shape [N, 4] holding N boxes 41 | boxes2: a numpy array with shape [M, 4] holding M boxes 42 | 43 | Returns: 44 | a numpy array with shape [N*M] representing pairwise intersection area 45 | """ 46 | [y_min1, x_min1, y_max1, x_max1] = np.split(boxes1, 4, axis=1) 47 | [y_min2, x_min2, y_max2, x_max2] = np.split(boxes2, 4, axis=1) 48 | 49 | all_pairs_min_ymax = np.minimum(y_max1, np.transpose(y_max2)) 50 | all_pairs_max_ymin = np.maximum(y_min1, np.transpose(y_min2)) 51 | intersect_heights = np.maximum( 52 | np.zeros(all_pairs_max_ymin.shape, dtype='f4'), 53 | all_pairs_min_ymax - all_pairs_max_ymin) 54 | all_pairs_min_xmax = np.minimum(x_max1, np.transpose(x_max2)) 55 | all_pairs_max_xmin = np.maximum(x_min1, np.transpose(x_min2)) 56 | intersect_widths = np.maximum( 57 | np.zeros(all_pairs_max_xmin.shape, dtype='f4'), 58 | all_pairs_min_xmax - all_pairs_max_xmin) 59 | return intersect_heights * intersect_widths 60 | 61 | 62 | def iou(boxes1, boxes2): 63 | """Computes pairwise intersection-over-union between box collections. 64 | 65 | Args: 66 | boxes1: a numpy array with shape [N, 4] holding N boxes. 67 | boxes2: a numpy array with shape [M, 4] holding M boxes. 68 | 69 | Returns: 70 | a numpy array with shape [N, M] representing pairwise iou scores. 71 | """ 72 | intersect = intersection(boxes1, boxes2) 73 | area1 = area(boxes1) 74 | area2 = area(boxes2) 75 | union = np.expand_dims( 76 | area1, axis=1) + np.expand_dims( 77 | area2, axis=0) - intersect 78 | return intersect / union 79 | 80 | 81 | def ioa(boxes1, boxes2): 82 | """Computes pairwise intersection-over-area between box collections. 83 | 84 | Intersection-over-area (ioa) between two boxes box1 and box2 is defined as 85 | their intersection area over box2's area. Note that ioa is not symmetric, 86 | that is, IOA(box1, box2) != IOA(box2, box1). 87 | 88 | Args: 89 | boxes1: a numpy array with shape [N, 4] holding N boxes. 90 | boxes2: a numpy array with shape [M, 4] holding N boxes. 91 | 92 | Returns: 93 | a numpy array with shape [N, M] representing pairwise ioa scores. 94 | """ 95 | intersect = intersection(boxes1, boxes2) 96 | inv_areas = np.expand_dims(1.0 / area(boxes2), axis=0) 97 | return intersect * inv_areas 98 | -------------------------------------------------------------------------------- /third_party/auto_augment/LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/third_party/auto_augment/LICENSE -------------------------------------------------------------------------------- /third_party/auto_augment/auto_augment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/ssl_detection/00d52272f61b56eade8d5ace18213cba6c74f6d8/third_party/auto_augment/auto_augment/__init__.py -------------------------------------------------------------------------------- /third_party/tensorpack/.lgtm.yml: -------------------------------------------------------------------------------- 1 | queries: 2 | - exclude: py/unguarded-next-in-generator 3 | - exclude: py/explicit-call-to-delete 4 | - exclude: py/polluting-import 5 | - exclude: py/import-and-import-from 6 | - exclude: py/similar-function 7 | - exclude: py/unused-local-variable 8 | # buggy: https://discuss.lgtm.com/t/python-false-positive-about-super/1330/3 9 | - exclude: py/super-not-enclosing-class 10 | - exclude: py/unreachable-statement 11 | extraction: 12 | python: 13 | prepare: 14 | packages: 15 | - libcap-dev 16 | python_setup: 17 | version: 3 18 | -------------------------------------------------------------------------------- /third_party/tensorpack/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = tensorpack 8 | SOURCEDIR = . 9 | BUILDDIR = build 10 | 11 | .PHONY: help Makefile docset clean 12 | 13 | all: html 14 | 15 | # Put it first so that "make" without argument is like "make help". 16 | help: 17 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 18 | 19 | docset: html 20 | convert _static/favicon.ico ./favicon.png 21 | doc2dash -d ./ -n $(SPHINXPROJ) --enable-js --force $(BUILDDIR)/html/ -I tutorial/index.html -i favicon.png 22 | tar czvf tensorpack.docset.tgz tensorpack.docset 23 | 24 | # Catch-all target: route all unknown targets to Sphinx using the new 25 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 26 | html: Makefile 27 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 28 | 29 | clean: 30 | rm -rf build 31 | -------------------------------------------------------------------------------- /third_party/tensorpack/docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Build the docs: 3 | 4 | ### Dependencies: 5 | 1. Python 3 6 | 2. Remove "tensorflow" from `requirements.txt` since you probably prefer to install TensorFlow by yourself. 7 | 3. `pip install -r requirements.txt`. Note that these requirements are different from tensorpack dependencies. 8 | 9 | ### Build HTML docs: 10 | `make html` 11 | will build the docs in `build/html`. 12 | 13 | ### Build Dash/Zeal docset 14 | 15 | 1. `pip install doc2dash` 16 | 2. `make docset` produces `tensorpack.docset`. 17 | 18 | ### Subscribe to docset updates in Dash/Zeal: 19 | 20 | Add this feed in Dash/Zeal: `https://github.com/tensorpack/tensorpack/raw/master/docs/tensorpack.xml`. 21 | -------------------------------------------------------------------------------- /third_party/tensorpack/docs/_static/build_toc.js: -------------------------------------------------------------------------------- 1 | // modified from 2 | // https://stackoverflow.com/questions/12150491/toc-list-with-all-classes-generated-by-automodule-in-sphinx-docs 3 | 4 | $(function (){ 5 | var createList = function(selected) { 6 | var ul = $('