[
  {
    "path": ".github/workflows/build_osx.yml",
    "content": "name: Build OS X\n\non: [push]\n\njobs:\n  build:\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: recursive\n\n      - name: Install Qt 5 + OpenCV + protobuf\n        run: brew install qt@5 opencv protobuf\n\n      - name: Force link Qt\n        run: brew link qt@5 --force\n      \n      - name: Compile protobuf messages\n        run: protoc --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto\n\n      - name: Run QMake\n        run: qmake -makefile -o Makefile DeepLabel.pro\n\n      - name: Run Make\n        run: make -j4\n\n      - name: Deploy\n        run: bash deploy_mac.sh ./release\n\n      - name: Archive production artifacts\n        uses: actions/upload-artifact@v2\n        with:\n          name: osx-release\n          path: ./release/deeplabel.dmg\n"
  },
  {
    "path": ".github/workflows/build_ubuntu.yml",
    "content": "name: Build Ubuntu\n\non: [push]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: recursive\n\n      - name: Install Qt 5 + protobuf\n        run: sudo apt-get update && sudo apt-get install -qq -y libprotobuf-dev qt5-default git build-essential protobuf-compiler cmake pkg-config libjpeg-dev libtiff5-dev libpng-dev\n\n      - name: Clone OpenCV\n        run: >\n          git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv;\n          git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv_contrib;\n\n      - name: Compile OpenCV\n        run: >\n            cd opencv\n            && mkdir build\n            && cd build\n            && cmake ..\n            -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules\n            -DBUILD_TESTS=OFF\n            -DBUILD_PERF_TESTS=OFF ..\n            -DCMAKE_BUILD_TYPE=RELEASE\n            -DBUILD_EXAMPLES=OFF\n            -DBUILD_DOCS=OFF\n            -DBUILD_JAVA=OFF\n            -DBUILD_opencv_apps=OFF\n            -DBUILD_opencv_aruco=OFF\n            -DBUILD_opencv_barcode=OFF\n            -DBUILD_opencv_bgsegm=OFF\n            -DBUILD_opencv_bioinspired=OFF\n            -DBUILD_opencv_calib3d=OFF\n            -DBUILD_opencv_datasets=OFF\n            -DBUILD_opencv_dpm=OFF\n            -DBUILD_opencv_dnn_superres=OFF\n            -DBUILD_opencv_face=OFF\n            -DBUILD_opencv_features2d=OFF\n            -DBUILD_opencv_flann=OFF\n            -DBUILD_opencv_fuzzy=OFF\n            -DBUILD_opencv_gapi=OFF\n            -DBUILD_opencv_highgui=ON\n            -DBUILD_opencv_img_hash=OFF\n            -DBUILD_opencv_intensity_transform=OFF\n            -DBUILD_opencv_line_descriptor=OFF\n            -DBUILD_opencv_mcc=OFF\n            -DBUILD_opencv_objc_bindings_generator=OFF\n            -DBUILD_opencv_objdetect=OFF\n            -DBUILD_opencv_optflow=OFF\n            -DBUILD_opencv_phase_unwrapping=OFF\n            -DBUILD_opencv_photo=OFF\n            -DBUILD_opencv_rgbd=OFF\n            -DBUILD_opencv_saliency=OFF\n            -DBUILD_opencv_shape=OFF\n            -DBUILD_opencv_js_bindings_generator=OFF\n            -DBUILD_opencv_freetype=OFF\n            -DBUILD_opencv_quality=OFF\n            -DBUILD_opencv_reg=OFF\n            -DBUILD_opencv_stereo=OFF\n            -DBUILD_opencv_stitching=OFF\n            -DBUILD_opencv_structured_light=OFF\n            -DBUILD_opencv_superres=OFF\n            -DBUILD_opencv_surface_matching=OFF\n            -DBUILD_opencv_text=OFF\n            -DBUILD_opencv_ts=OFF\n            -DBUILD_opencv_videostab=OFF\n            -DBUILD_opencv_viz=OFF\n            -DBUILD_opencv_wechat_qrcode=OFF\n            -DBUILD_opencv_ximgproc=OFF\n            -DBUILD_opencv_xobjdetect=OFF\n            -DBUILD_opencv_xphoto=OFF\n            -DBUILD_opencv_xfeatures2d=OFF\n            && make -j4\n            && sudo make install\n      \n      - name: Compile protobuf messages\n        run: protoc --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto\n\n      - name: Run QMake\n        run: qmake -makefile -o Makefile DeepLabel.pro\n\n      - name: Run Make\n        run: make -j4\n\n      - name: Archive production artifacts\n        uses: actions/upload-artifact@v2\n        with:\n          name: linux-release\n          path: ./release/deeplabel\n"
  },
  {
    "path": ".github/workflows/build_windows.yml",
    "content": "name: Build Windows\n\non: [push]\n\njobs:\n  build:\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: recursive\n\n      - name: Install Qt 5 and mingw\n        run: choco install qt5-default mingw --confirm -r --no-progress\n\n      - name: Clone protobuf\n        run: git clone https://github.com/protocolbuffers/protobuf\n\n      - name: Install protobuf\n        run: >\n          cd protobuf/cmake;\n          mkdir build;\n          cd build;\n          $install_path=Resolve-Path ../../;\n          cmake ..\n          -Dprotobuf_BUILD_SHARED_LIBS=ON\n          -Dprotobuf_BUILD_EXAMPLES=OFF\n          -Dprotobuf_BUILD_TESTS=OFF\n          -Dprotobuf_WITH_ZLIB=ON\n          -Dprotobuf_BUILD_PROTOC_BINARIES=ON\n          -DCMAKE_INSTALL_PREFIX=\"$install_path\";\n          cmake --build . --parallel 4 --target install --config Release\n\n      - name: Compile protobuf messages\n        run: ./protobuf/bin/protoc.exe --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto\n\n      - name: Clone OpenCV\n        run: >\n          git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv;\n          git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv_contrib\n\n      - name: Compile OpenCV\n        run: >\n            cd opencv; \n            mkdir build; \n            cd build;\n            $extra_path=Resolve-Path ../../opencv_contrib/modules;\n            $install_path=Resolve-Path .;\n            cmake .. \n            -DOPENCV_EXTRA_MODULES_PATH=\"$extra_path\"\n            -DBUILD_TESTS=OFF\n            -DBUILD_PERF_TESTS=OFF ..\n            -DCMAKE_BUILD_TYPE=RELEASE\n            -DBUILD_EXAMPLES=OFF\n            -DBUILD_DOCS=OFF\n            -DBUILD_JAVA=OFF\n            -DBUILD_opencv_apps=OFF\n            -DBUILD_opencv_aruco=OFF\n            -DBUILD_opencv_barcode=OFF\n            -DBUILD_opencv_bgsegm=OFF\n            -DBUILD_opencv_bioinspired=OFF\n            -DBUILD_opencv_calib3d=OFF\n            -DBUILD_opencv_datasets=OFF\n            -DBUILD_opencv_dpm=OFF\n            -DBUILD_opencv_dnn_superres=OFF\n            -DBUILD_opencv_face=OFF\n            -DBUILD_opencv_features2d=OFF\n            -DBUILD_opencv_flann=OFF\n            -DBUILD_opencv_fuzzy=OFF\n            -DBUILD_opencv_gapi=OFF\n            -DBUILD_opencv_highgui=ON\n            -DBUILD_opencv_img_hash=OFF\n            -DBUILD_opencv_intensity_transform=OFF\n            -DBUILD_opencv_line_descriptor=OFF\n            -DBUILD_opencv_mcc=OFF\n            -DBUILD_opencv_objc_bindings_generator=OFF\n            -DBUILD_opencv_objdetect=OFF\n            -DBUILD_opencv_optflow=OFF\n            -DBUILD_opencv_phase_unwrapping=OFF\n            -DBUILD_opencv_photo=OFF\n            -DBUILD_opencv_quality=OFF\n            -DBUILD_opencv_reg=OFF\n            -DBUILD_opencv_rgbd=OFF\n            -DBUILD_opencv_saliency=OFF\n            -DBUILD_opencv_shape=OFF\n            -DBUILD_opencv_js_bindings_generator=OFF\n            -DBUILD_opencv_freetype=OFF\n            -DBUILD_opencv_stereo=OFF\n            -DBUILD_opencv_stitching=OFF\n            -DBUILD_opencv_structured_light=OFF\n            -DBUILD_opencv_superres=OFF\n            -DBUILD_opencv_surface_matching=OFF\n            -DBUILD_opencv_text=OFF\n            -DBUILD_opencv_ts=OFF\n            -DBUILD_opencv_videostab=OFF\n            -DBUILD_opencv_viz=OFF\n            -DBUILD_opencv_wechat_qrcode=OFF\n            -DBUILD_opencv_ximgproc=OFF\n            -DBUILD_opencv_xobjdetect=OFF\n            -DBUILD_opencv_xphoto=OFF\n            -DBUILD_opencv_xfeatures2d=OFF\n            -DBUILD_opencv_world=ON\n            -DCMAKE_INSTALL_PREFIX=\"$install_path\";\n            cmake --build . --parallel 4 --config Release --target install\n\n      - name: Run QMake\n        run: C:/Qt/5.15.2/mingw81_64/bin/qmake.exe -makefile -o Makefile DeepLabel.pro\n\n      - name: Run Make\n        run: mingw32-make -f Makefile.Release\n\n      - name: Archive production artifacts\n        uses: actions/upload-artifact@v2\n        with:\n          name: windows-release\n          path: ./release/deeplabel\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\n*.pro.user*\nMakefile\nui_*\n.moc\n.obj\n*.stash\nopencv\nprotobuf"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"QtAwesome\"]\n\tpath = QtAwesome\n\turl = https://github.com/gamecreature/QtAwesome.git\n"
  },
  {
    "path": "DeepLabel.pro",
    "content": "#-------------------------------------------------\n#\n# Project created by QtCreator 2017-10-04T17:31:00\n#\n#-------------------------------------------------\n\nQT       += core gui sql testlib concurrent xml\n\ngreaterThan(QT_MAJOR_VERSION, 4): QT += widgets\n\nTARGET = deeplabel\nTEMPLATE = app\n\n# The following define makes your compiler emit warnings if you use\n# any feature of Qt which has been marked as deprecated (the exact warnings\n# depend on your compiler). Please consult the documentation of the\n# deprecated API in order to know how to port your code away from it.\nDEFINES += QT_DEPRECATED_WARNINGS\n\n# You can also make your code fail to compile if you use deprecated APIs.\n# In order to do so, uncomment the following line.\n# You can also select to disable deprecated APIs only up to a certain version of Qt.\n#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0\n\nmacx{\nmessage(\"Mac\")\n#PKG_CONFIG = /usr/local/bin/pkg-config\nCONFIG += c++17\n\n# Build without FFMPEG unless you want a lot of pain later\nCONFIG += link_pkgconfig\nPKGCONFIG += protobuf\n\n# If you use pkg-config, *everything* gets linked. Wasteful.\nINCLUDEPATH += /usr/local/include/opencv4\nLIBS += -L/usr/local/opt/opencv/lib/\nLIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lopencv_tracking -lopencv_video -lopencv_videoio -lopencv_dnn\n}\n\nunix:!macx{\nmessage(\"Linux\")\n#CONFIG += link_pkgconfig\n#PKGCONFIG += opencv4\nCONFIG += c++17\nINCLUDEPATH += /usr/local/include/opencv4\nLIBS += -L/usr/local/lib -lprotobuf -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lopencv_tracking -lopencv_video -lopencv_videoio -lopencv_dnn\n}\n\nwin32{\nmessage(\"Windows\")\nDEFINES += PROTOBUF_USE_DLLS\n\ndefined(WITH_CUDA){\n    message(\"Building with CUDA\")\n    CONFIG += WITH_CUDA\n    INCLUDEPATH += \"$$_PRO_FILE_PWD_/opencv/build_cuda/include\"\n    LIBS += -L\"$$_PRO_FILE_PWD_/opencv/build_cuda/x64/vc16/lib\"\n}else{\n    INCLUDEPATH += \"$$_PRO_FILE_PWD_/opencv/build/include\"\n    LIBS += -L\"$$_PRO_FILE_PWD_/opencv/build/x64/vc16/lib\"\n}\n\nINCLUDEPATH += \"$$_PRO_FILE_PWD_/protobuf/include\"\nLIBS += -L\"$$_PRO_FILE_PWD_/protobuf/lib\"\n\n#QMAKE_CXXFLAGS += \"/std:c++17 /permissive-\"\nCONFIG(debug, debug|release) {\nLIBS += -lopencv_world453d -llibprotobufd\n}else{\nLIBS += -lopencv_world453 -llibprotobuf\n}\n}\n\n# For building in a single folder\nCONFIG(debug, debug|release) {\n    CONFIG(WITH_CUDA){\n        DESTDIR = debug_cuda\n    }else{\n        DESTDIR = debug\n    }\n    OBJECTS_DIR = .obj_debug\n    MOC_DIR     = .moc_debug\n}else {\n    DEFINES += QT_NO_DEBUG_OUTPUT\n    CONFIG(WITH_CUDA){\n        message(\"Building with CUDA\")\n        DESTDIR = release_cuda\n    }else{\n        DESTDIR = release\n    }\n    OBJECTS_DIR = .obj\n    MOC_DIR     = .moc\n}\n\nINCLUDEPATH += \"$$_PRO_FILE_PWD_/src\"\nVPATH = \"$$_PRO_FILE_PWD_/src\"\n\nSOURCES += \\\n    src/crc32.cpp \\\n        src/main.cpp \\\n        src/mainwindow.cpp \\\n        src/labelproject.cpp \\\n        src/imagelabel.cpp \\\n        src/kittiexporter.cpp \\\n        src/darknetexporter.cpp \\\n        src/exportdialog.cpp \\\n        src/multitracker.cpp \\\n    src/baseexporter.cpp \\\n    src/baseimporter.cpp \\\n    src/birdsaiimporter.cpp \\\n    src/cliparser.cpp \\\n    src/cocoexporter.cpp \\\n    src/darknetimporter.cpp \\\n    src/pascalvocimporter.cpp \\\n    src/proto/example.pb.cc \\\n    src/proto/feature.pb.cc \\\n    src/gcpexporter.cpp \\\n    src/imagedisplay.cpp \\\n    src/importdialog.cpp \\\n    src/motimporter.cpp \\\n    src/pascalvocexporter.cpp \\\n    src/detection/detectoropencv.cpp \\\n    src/detection/detectorsetupdialog.cpp \\\n    src/refinerangedialog.cpp \\\n    src/cocoimporter.cpp \\\n    src/tfrecordexporter.cpp \\\n    src/tfrecordimporter.cpp \\\n    src/videoexporter.cpp\n\nHEADERS += \\\n    src/cliprogressbar.h \\\n    src/crc32.h \\\n    src/importer.h \\\n        src/mainwindow.h \\\n        src/labelproject.h \\\n        src/imagelabel.h \\\n        src/boundingbox.h \\\n        src/kittiexporter.h \\\n        src/darknetexporter.h \\\n        src/exportdialog.h \\\n        src/multitracker.h \\\n    src/baseexporter.h \\\n    src/baseimporter.h \\\n    src/birdsaiimporter.h \\\n    src/cliparser.h \\\n    src/cocoexporter.h \\\n    src/darknetimporter.h \\\n    src/exporter.h \\\n    src/gcpexporter.h \\\n    src/imagedisplay.h \\\n    src/importdialog.h \\\n    src/motimporter.h \\\n    src/pascalvocexporter.h \\\n    src/detection/detectoropencv.h \\\n    src/detection/detectorsetupdialog.h \\\n    src/pascalvocimporter.h \\\n    src/proto/example.pb.h \\\n    src/proto/feature.pb.h \\\n    src/refinerangedialog.h \\\n    src/cocoimporter.h \\\n    src/tfrecordexporter.h \\\n    src/tfrecordimporter.h \\\n    src/videoexporter.h\n\nFORMS += \\\n    src/importdialog.ui \\\n    src/mainwindow.ui \\\n    src/exportdialog.ui \\\n    src/imagedisplay.ui \\\n    detection/detectorsetupdialog.ui \\\n    src/refinerangedialog.ui\n\ninclude(QtAwesome/QtAwesome/QtAwesome.pri)\n\n# Deploy apps in OS X and Windows\nisEmpty(TARGET_EXT) {\n    win32 {\n        TARGET_CUSTOM_EXT = .exe\n    }\n    macx {\n        TARGET_CUSTOM_EXT = .app\n    }\n} else {\n    TARGET_CUSTOM_EXT = $${TARGET_EXT}\n}\n\nCONFIG( debug, debug|release ) {\n    # debug\n    DEPLOY_TARGET = $$shell_quote($$shell_path($${OUT_PWD}/debug/$${TARGET}$${TARGET_CUSTOM_EXT}))\n} else {\n    # release\n    DEPLOY_TARGET = $$shell_quote($$shell_path($${OUT_PWD}/release/$${TARGET}$${TARGET_CUSTOM_EXT}))\n}\n\nwin32 {\n    DEPLOY_COMMAND = windeployqt.exe\n}\nmacx {\n    DEPLOY_COMMAND = macdeployqt\n}\n\n#  # Uncomment the following line to help debug the deploy command when running qmake\n#  warning($${DEPLOY_COMMAND} $${DEPLOY_TARGET})\n\n# Use += instead of = if you use multiple QMAKE_POST_LINKs\nwin32 {\n    QMAKE_POST_LINK = $${DEPLOY_COMMAND} $${DEPLOY_TARGET}\n}\nmacx {\n    QMAKE_POST_LINK = $${DEPLOY_COMMAND} $${DEPLOY_TARGET}\n}\n"
  },
  {
    "path": "README.md",
    "content": "# DeepLabel\n\n[![Build OS X](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_osx.yml/badge.svg)](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_osx.yml) [![Build Ubuntu](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_ubuntu.yml/badge.svg)](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_ubuntu.yml)[![Build Windows](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_windows.yml/badge.svg)](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_windows.yml)\n\nDeepLabel is a cross-platform tool for annotating images with labelled bounding boxes. A typical use-case for the program is labelling ground truth data for object-detection machine learning applications. DeepLabel runs as a standalone app and compiles on Windows, Linux and Mac.\n\nDeepLabel is written in C++ and is resource-efficient, typically using less than 100MB RAM to run. There is a GUI or you can run on the command line for batch/automated processing. \n\nDeeplabel also supports running inference using state-of-the-art object detection models like Faster-RCNN and YOLOv4. With support out-of-the-box for CUDA, you can quickly label an entire dataset using an existing model.\n\n**If you use DeepLabel for research or commercial purposes, please cite here!** [![DOI](https://zenodo.org/badge/105791274.svg)](https://zenodo.org/badge/latestdoi/105791274)\n\nDownload the [latest release](https://github.com/jveitchmichaelis/deeplabel/releases/latest)! If you are an OS X user, check the `Actions` tab to download an automated and self-contained DMG build. The Github CI runs on every push and attempts to build DeepLabel for Windows, Mac and Linux. You can check the action workflows for hints on how to compile everything if you're having trouble.\n\n**Note: Deeplabel for Windows now packages CUDA and CUDNN for inference. This results an enormous distributable size as the cudnn inference libraries are 600MB+ alone. The CUDA runtime adds another 300MB. I'm looking into uploading simultaneous non-CUDA releases to save space. The alternative is to ask you to install CUDA and CUDNN yourself, but you'd need the correct version which is a pain.**\n\nReady made binaries for Windows and OS X are on the release page. It is recommended that you build for Linux yourself, but it's not difficult.\n\n![Deeplabel Interface](gui_example.png)\n\n## Workflow\n\nDeepLabel was built with convenience in mind. Image locations, classes and labels are stored in a local sqlite database (called a _project_, in the application). When a label is added or removed, this is immediately reflected in the database.\n\nA typical workflow for DeepLabel is:\n\n1. Create a new project database\n2. Add images, or import an existing project in a variety of common ML formats\n3. Load in a class list, or manually add classes\n4. Label/inspect the images\n5. Export data in the desired format\n\n## Command Line Interface\n\nDeeplabel now has a convenient command line interface to facilitate import and export of data:\n\n```\n(base) PS C:\\Users\\Josh> deeplabel.exe -h\nUsage: deeplabel.exe [options] mode\n\nOptions:\n  -?, -h, --help                                     Displays help on\n                                                     commandline options.\n  --help-all                                         Displays help including Qt\n                                                     specific options.\n  -v, --version                                      Displays version\n                                                     information.\n  -f, --format <[kitti, darknet, gcp, voc, coco, mot export format\n  , birdsai, tfrecord]>\n  -o, --output <folder path>                         output folder\n  -i, --input <file path>                            label database\n  -s, --split <percentage>                           validation split\n                                                     percentage\n  --no-subfolders                                    export directly to\n                                                     specified folder\n  --prefix <prefix>                                  filename prefix\n  -n, --names <file path>                            names file\n  --bucket                                           GCP bucket\n  --local                                            use local paths for GCP\n                                                     export\n  --export-map                                       export label.pbtxt file\n  --shuffle                                          shuffle images when\n                                                     splitting\n  --append-labels                                    append to label files\n  --export-unlabelled                                export images without\n                                                     labels\n  --images <images>                                  import image path/folder\n  --annotations <annotations>                        import annotation\n                                                     path/folder\n  --import-unlabelled                                import images without\n                                                     labels\n  --overwrite                                        overwrite existing\n                                                     databases\n  --records <images>                                 mask for TF Records (*\n                                                     wildcard)\n\nArguments:\n  mode                                               [export, import]\n```\n\nFor example, if you want to export a dataset to TFRecord:\n\n```\ndeeplabel.exe export -i labels.lbdlb -f TFRecord -n project.names -o ./output/\n```\n\n## Data import\n\nCurrently you can import data in the following formats:\n\n* Darknet (provide image list and names)\n* COCO (provide an annotation .json file and image folder)\n* MOT\n* TFRecord (parsing works, but full import is not possible yet)\n* Pascal VOC\n\n## Data export\n\nCurrently you can export in:\n\n* KITTI (e.g. for Nvidia DIGITS)\n* Darknet for YOLO\n* Pascal VOC\n* COCO (experimental)\n* Google Cloud Platform (e.g. for AutoML)\n* TFRecord (for the Tensorflow Object Detection library)\n  * Note this uses protobuf directly and there is _no_ dependency on Tensorflow. I believe this is one of the few implementations of TFRecord writing in c++.\n* Video (experimental, command line only)\n\nDeeplabel treats your data as \"golden\" and does not make any attempt to modify it directly. This is a safe approach to avoid accidental corruption of a dataset that you spent months collating. As such, when you export labels, a copy of your data will be created with associated label files. For example, KITTI requires frames to be numerically labelled. In the future, augmentation may also be added, which is another reason to **not** modify your existing images.\n\nWhen exporting to darknet, you should specify an existing \"names\" file so that your output labels have consistent class IDs. Similarly, when you export to Pascal VOC, you have the option of exporting a label map file which maps class IDs to labels. This file is quite easy to generate yourself (and you may already have it). The format is:\n\n```\n{\nitem {\n  name: some_class\n  id: 1\n  displayname: some_class\n}\n```\n\nDeepLabel can automatically split your data into train and validation sets, you can choose what fraction to use (you can set 0% or 100% if you just want a test or validation set).\n\nSince the labelling metadata is in the sqlite database, it should be fairly easy to write a Python script (or whatever) to convert the output to your preferred system. Many frameworks will accept Pascal VOC formatted data, so that's a good start.\n\n## Model inference (automatic tagging)\n\nDeepLabel now supports automatic image tagging from a pre-trained model. A good approach for rapidly tagging thousands of images is to train a \"rough\" model on a small subset of your data, use that model to tag more data, correct the labels as required, and repeat. Over time, the model will get better and better and you will need to make fewer corrections.\n\n### Supported models\n\nYou can load in a Darknet-type model, for example Yolov3, Yolov3-spp and Yolov3-Tiny or  a Tensorflow model.\n\nMost of the standard Tensorflow models are supported e.g. Faster-RCNN, Mobilenet-SSD. Currently the assumption is that the model ends with a `DetectionOutput` layer. This is an Nx7 sized tensor where `N` is the number of output labels, after non-maximum suppression has been applied (typically 100 for stock models). The columns are `batch id, class id, confidence, box coords`. \n\nYou need to provide:\n\n* The model configuration file (`.cfg, .pbtxt`)\n* The model weights (`.weights, .pb`)\n* A names file (`.names`)\n\nThe extension doesn't really matter, but these are the standard ones.\n\n**Note: ensure that your names file corresponds to the expect label IDs that your model outputs. For example, Darknet uses an 80-class file, Tensorflow models often use a 91-class file, MobileNet-SSD outputs labels starting from 1, Faster-RCNN outputs labels starting from 0, etc.**\n\nYou can then run inference on a single image (the magic wand icon), or an entire project (`Detection->Run on Project`). On a modern CPU, Yolov3 takes around 0.5 seconds to tag a full HD image. DeepLabel will use OpenCV's OpenCL backend by default (you can select CPU if necessary), which should provide some acceleration on GPUs.\n\nIf you're not seeing any detections, you can adjust the confidence threshold (`Detection->Set Threshold`). If your detection boxes look right, but the classe labels are wrong, check your names file is correct.\n\nDownload Tensorflow models for testing from OpenCV [here](https://github.com/opencv/opencv/wiki/TensorFlow-Object-Detection-API).\n\n### Target\n\nYou can run models on CPU or OpenCL. All models will work on CPU. You may see a speed up with OpenCL, but the CPU backend is far more heavily optimised (and typically is better than other framework's CPU performance).\n\nOpenCL support is somewhat experimental and you may get an out-of-memory error (and Deeplabel will unceremoniously crash). Try with CPU if this happens. Typical performance is anywhere from 50 ms for Tiny Yolov3 to a couple of seconds for Faster-RCNN\n\n## 16-bit File Handling\n\nDeeplabel supports 16-bit images and will automatically scale the colours to 8-bit grayscale. \n\nYou can optionally apply a colourmap to a single channel image, which may improve contrast.\n\nIn the future, support should be added for OpenCV's Tensorflow backend.\n\n## Bounding box tracking and refinement\n\nDeepLabel supports multi-threaded object tracking for quickly labelling video sequences. Simply label the first frame, initialise the tracker and select \"propagate on next image\". The software will initialise a tracker for each bounding box and attempt to track it in the next frame. If tracking is lost, you can re-label the offending image, restart the tracker and keep going. In this way, you can quickly label thousands of images in minutes.\n\nDeepLabel also supports bounding box refinement using some simple thresholding and contour segmentation techniques. This was designed for labelling thermal infrared images, but it should work on colour images too. It works best if the object has a high contrast. It will also probably fail if you have two overlapping objects that look very similar.\n\nAn experimental foreground/background segmenter is being tested, but it seems to be a bit overenthusiastic right now.\n\n## Video support\n\nDeepLabel currently supports videos by a brute force route. You can load in a video which will be automatically split into JPEG frames and then loaded into your label database. Currently the entire video will be loaded, though in the future functionality will likely be similar to VOTT with frame skip options and no need to split the video beforehand. Note that when dealing with videos you will typically need to export them as separate frames anyway.\n\nAny video format that OpenCV (and its backends) can open should work.\n\nUsage\n--\n\nUsing the software should be fairly straightforward. Once you've created a project database and added images to it, you can get on with the fun part of adding bounding boxes.\n\nFirst, add the classes that you want.\n\nDeepLabel operates in two modes: draw and select. In **draw** mode, you can click to define the corners of a bounding box rectangle. If you're happy with the box, hit space to confirm. The rectangle will be added to the image with a class label.\n\nIf you need to delete a label, switch to **select** mode. Click on a rectangle, it will highlight green, the hit delete or backspace to remove it.\n\nAll changes are immediately reflected in the database.\n\n**Navigate** through images using the left/right or a/d keys. You can use ctrl+left/right to quickly advance through your dataset.\n\nYou should find a/d to be quite a natural way of navigating without moving your left hand. There is a progress bar to indicate how far through the dataset you've labelled.\n\nOnce you're done labelling, open the export menu to copy and rename your images and generate label files. When you export, you need to tell Deeplabel what classes you wish to export (typically via a `.names` file). This means that you can consistently export multiple label files (e.g. one per video sequence) and have the class/class IDs match up.\n\nBuilding from source\n--\n\nIt's recommended that you use Qt5, but Qt4 will probably work. You need to have Qt's SQL extensions installed.\n\n**OpenCV Requirements**\n\nThis is mostly a pure Qt project, but there are some limitations to what Qt can do with images. In particular, scaling sucks (even with `Qt::SmoothTransform`). Qt's image reader is also not particularly robust, so OpenCV is used there. OpenCV is also used for image augmentation. On OS X or Linux it's expected that you have `pkg-config` installed to handle dependencies. \n\nYou need to compile OpenCV with contrib (`-DOPENCV_EXTRA_MODULES_PATH`) for object tracking. You should also compile with (`-DOPENCV_GENERATE_PKGCONFIG`). Only OpenCV 4+ is supported due to API changes. Make sure you checkout the same tagged release for the main repository and the contrib repository.\n\n``` bash\ngit clone https://github.com/opencv/opencv\ngit clone https://github.com/opencv/opencv_contrib\ncd opencv && git checkout 4.5.3 && cd ../\ncd opencv_contrib && git checkout 4.5.3 && cd ../\n\ncd opencv\nmkdir build && cd build\ncmake .. -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules\nmake -j8 \nsudo make install\n```\n\nOn Mac, Homebrew automatically include pkg-config support and the contrib packages.\n\n**Linux**\n\n[See Github Action for representative build instructions.](https://github.com/jveitchmichaelis/deeplabel/blob/master/.github/workflows/build_ubuntu.yml)\n\nBuild opencv using your preferred method (e.g. above). You need Qt5 installed - not just Qt Creator.\n\n```\nsudo apt install git build-essential qt5-default libprotobuf-dev protobuf-compiler\n```\n\nProto files are already compiled within the `src/proto` folder, but if you need to, you can compile with:\n\n```\nprotoc --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto \n```\n\nClone the repository, then:\n\n```bash\ngit submodule update --init --recursive\nqmake -makefile -o Makefile DeepLabel.pro\nmake -j4\n```\n\n**Mac**\nInstall dependencies using Homebrew:\n\n``` bash\nbrew install qt opencv protobuf\n```\n\nNote that qt is not linked by default, so either force link it (`brew link -f qt`) or follow the post-install instructions to see where qmake is installed.\n\nClone the repo, open the pro file in Qt Creator and build. Deployment is automatic on Windows and OS X. Alternatively:\n\n```bash\ngit submodule update --init --recursive\nqmake -makefile -o Makefile DeepLabel.pro\nmake -j4\n```\n\n`madeployqt` is automatically run after compilation, and on OS X will build a `.dmg` file. This does have the irritating side effect of linking and copying every `dylib` OpenCV has to offer so feel free to dig into the package and delete some of the dylibs that you don't need. This is a tradeoff between output file size and convenience.\n\nAn extra script is provided to fix paths to certain libraries on OS X.\n\n**Windows**\n\nUnfortunately you need to install OpenCV from source, because the official binary releases don't include the contrib modules (which include tracking algorithms and DNN support). Or just download a DeepLabel release from [here](https://github.com/jveitchmichaelis/deeplabel/releases).\n\nYou will also need protobuf installed - make sure you compile with DLLs enabled, not static libraries.\n\nOnce you've installed OpenCV...(!)\n\nClone the repo, update the submodules to fetch QtAwesome, open the pro file in Qt Creator and modify the paths to your opencv install. Build as normal. Make sure you copy all the OpenCV DLLs after install.\n\nNotes\n--\n\n#### Overlapping bounding boxes\n\nDeepLabel doesn't care if your bounding boxes overlap, but selecting overlapping bounding boxes is a tricky problem from a UI point of view. Currently the solution is simple: all rectangles containing the cursor will be highlighted and if you hit delete, the most recent one will be deleted.\n\n#### Image paths\n\nImage paths in the database are stored relative to the database location. This means you can easily copy over files to another system, provided you keep the relative structure of the files.\n\n#### Support for other descriptors (e.g. occluded, truncated)\n\nIn the future I'd like to add the ability to mark labels as occluded or truncated. I haven't decided on the best way to implement this yet - most detector networks don't use this information, but it's useful for stats.\n\n#### Database schema\n\nDeepLabel uses a simple relational database with the following schema:\n\n\tCREATE TABLE classes (class_id INTEGER PRIMARY KEY ASC, name varchar(32))\n\tCREATE TABLE images (image_id INTEGER PRIMARY KEY ASC, path varchar(256))\n\tCREATE TABLE labels (label_id INTEGER PRIMARY KEY ASC, image_id int, class_id int, x int, y int, width int, height int)\n\nThese fields are the bare minimum and more may be added later (see note on descriptors above). It may also be useful to include metadata about images for statistics purposes.\n\n#### License\n\nThis code is MIT licensed - feel free to fork and use without restriction, commercially or privately, but please do cite. Copyright Josh Veitch-Michaelis 2017 - present.\n"
  },
  {
    "path": "deploy_mac.sh",
    "content": "#!/usr/bin/env bash\n\nbuild_folder=$1\napp_path=$build_folder/DeepLabel.app\n\nmacdeployqt $app_path\npython fix_paths_mac.py $app_path/Contents/MacOS/DeepLabel\nhdiutil create -volname DeepLabel -srcfolder $app_path -ov -format UDZO $build_folder/deeplabel.dmg\n"
  },
  {
    "path": "fix_paths_mac.py",
    "content": "import subprocess\nimport os\nimport sys\nfrom shutil import copyfile\n\nexecutable = sys.argv[1]\napp_folder = os.path.join(*executable.split('/')[:-3])\ncontent_folder = os.path.join(app_folder, \"Contents\")\nframework_path = os.path.join(content_folder, \"Frameworks\")\n\nprint(executable)\nprint(\"Working in {} \".format(app_folder))\n\ndef file_in_folder(file, folder):\n    return os.path.exists(os.path.join(folder, file))\n\ndef otool(s):\n    o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE)\n\n    for l in o.stdout:\n        l = l.decode()\n\n        if l[0] == '\\t':\n            path = l.split(' ', 1)[0][1:]\n\n            if \"@executable_path\" in path:\n                path = path.replace(\"@executable_path\", \"\")\n                path = os.path.join(content_folder, path[4:])\n\n            if \"@loader_path\" in path:\n                path = path.replace(\"@loader_path\", framework_path)\n\n            if \"@rpath\" in path:\n                path = path.replace(\"@rpath\", framework_path)\n\n            dependency_dylib_name = os.path.split(path)[-1]\n\n            if \"usr/local\" in path:\n                if app_folder in s:\n\n                    print(\"Warning: {} depends on {}\".format(s, path))\n\n                    if file_in_folder(dependency_dylib_name, framework_path):\n                        print(\"Dependent library {} is already in framework folder\".format(dependency_dylib_name))\n                    \n                        print(\"Running install name tool to fix {}.\".format(s))\n                        \n                        if dependency_dylib_name == os.path.split(s)[-1]:\n                            _ = subprocess.Popen(['install_name_tool', '-id', os.path.join(\"@loader_path\", dependency_dylib_name), s], stdout=subprocess.PIPE)\n\n                        _ = subprocess.Popen(['install_name_tool', '-change', path, os.path.join(\"@loader_path\", dependency_dylib_name), s], stdout=subprocess.PIPE)\n                else:\n                    pass\n\n            yield path\n\nneed = set([executable])\ndone = set()\n\nwhile need:\n    needed = set(need)\n    need = set()\n    for f in needed:\n        need.update(otool(f))\n    done.update(needed)\n    need.difference_update(done)\n"
  },
  {
    "path": "src/baseexporter.cpp",
    "content": "#include \"baseexporter.h\"\n\nBaseExporter::BaseExporter(LabelProject *project, QObject *parent) : QObject(parent)\n{\n    this->project = project;\n}\n\nvoid BaseExporter::disableProgress(bool disable){\n    disable_progress = disable;\n}\n\nvoid BaseExporter::setValidationSplit(bool split){\n    validation_split = split;\n}\n\nvoid BaseExporter::splitData(float split, bool shuffle, int seed){\n\n    if(split < 0 || split > 1){\n        qCritical() << \"Invalid split fraction, should be [0,1]\";\n    }\n\n    if(shuffle){\n        std::random_device rd;\n        std::mt19937 generator(rd());\n        generator.seed(static_cast<unsigned int>(seed));\n\n        std::shuffle(images.begin(), images.end(), generator);\n    }\n\n    int pivot = static_cast<int>(images.size() * split);\n    validation_set = images.mid(0, pivot);\n    train_set = images.mid(pivot);\n\n    qDebug() << \"Split: \" << split;\n    if(split > 0){\n        qInfo() << train_set.size() << \" images selected for train set.\";\n        qInfo() << validation_set.size() << \" images selected for validation set.\";\n    }else{\n        qInfo() << train_set.size() << \" images selected for output.\";\n    }\n\n}\n\nvoid BaseExporter::setExportUnlabelled(bool res){\n    export_unlabelled = res;\n\n    bool relative_path = true;\n    if(export_unlabelled){\n        project->getImageList(images, relative_path);\n    }else{\n        project->getLabelledImageList(images, relative_path);\n    }\n\n    qDebug() << \"Selected \" << images.size() << \" images\";\n}\n\nbool BaseExporter::setOutputFolder(const QString folder, bool no_subfolders){\n\n    if(folder == \"\") return false;\n\n    output_folder = folder;\n\n    //Make output folder if it doesn't exist\n    if (!QDir(output_folder).exists()){\n        qInfo() << \"Making output folder\" << output_folder;\n        QDir().mkpath(output_folder);\n    }\n\n    if(!no_subfolders){\n        //Make the training and validation folders\n        train_folder = QDir::cleanPath(output_folder+\"/train\");\n        if (!QDir(train_folder).exists()){\n            qInfo() << \"Making training folder\" << train_folder;\n            QDir().mkpath(train_folder);\n        }\n\n        if(validation_split){\n            val_folder = QDir::cleanPath(output_folder+\"/val\");\n            if (!QDir(val_folder).exists()){\n                qInfo() << \"Making validation folder\" << val_folder;\n                QDir().mkpath(val_folder);\n            }\n\n            val_label_folder = QDir::cleanPath(val_folder);\n            val_image_folder = QDir::cleanPath(val_folder);\n        }\n\n        train_label_folder = QDir::cleanPath(train_folder);\n        train_image_folder = QDir::cleanPath(train_folder);\n    }else{\n        train_folder = QDir::cleanPath(output_folder);\n        val_folder = QDir::cleanPath(output_folder);\n        val_label_folder = QDir::cleanPath(output_folder);\n        val_image_folder = QDir::cleanPath(output_folder);\n        train_label_folder = QDir::cleanPath(output_folder);\n        train_image_folder = QDir::cleanPath(output_folder);\n        qDebug() << \"Output all to\" << train_image_folder;\n    }\n\n\n    return true;\n\n}\n\nvoid BaseExporter::setFilenamePrefix(QString prefix){\n    if(prefix != \"\"){\n        filename_prefix = prefix;\n    }\n}\n\nbool BaseExporter::saveImage(cv::Mat &image, const QString output, const double scale_x, const double scale_y){\n\n    if(image.rows == 0 || image.cols == 0){\n        qCritical() << \"Empty image \";\n        return false;\n    }\n\n    if(scale_x > 0 && scale_y > 0)\n        cv::resize(image, image, cv::Size(), scale_x, scale_y);\n\n    std::vector<int> compression_params;\n\n    // Png compression - maximum is super slow\n    // TODO: add support to adjust this\n    if(output.split(\".\").last().toLower() == \"png\"){\n        compression_params.push_back(cv::IMWRITE_PNG_COMPRESSION);\n        compression_params.push_back(6);\n    }\n\n    return cv::imwrite(output.toStdString(), image, compression_params);\n}\n"
  },
  {
    "path": "src/baseexporter.h",
    "content": "#ifndef BASEEXPORTER_H\n#define BASEEXPORTER_H\n\n#include <QObject>\n#include <QDateTime>\n#include <QImageReader>\n#include <QFileDialog>\n\n#include <opencv2/opencv.hpp>\n#include <random>\n\n#include <cliprogressbar.h>\n#include <labelproject.h>\n#include <boundingbox.h>\n\nenum export_image_type{\n    EXPORT_UNASSIGNED,\n    EXPORT_TRAIN,\n    EXPORT_VAL,\n    EXPORT_TEST\n};\n\nclass BaseExporter : public QObject\n{\n    Q_OBJECT\npublic:\n    explicit BaseExporter(LabelProject *project, QObject *parent = nullptr);\n    void disableProgress(bool disable);\nsignals:\n    void export_progress(int);\n\npublic slots:\n    void splitData(float split=1, bool shuffle=false, int seed=42);\n    bool setOutputFolder(QString folder, bool no_subfolders=false);\n    void setExportUnlabelled(bool res);\n    void setAppendLabels(bool res){append_labels = res;}\n    void setValidationSplit(bool split);\n    void setFilenamePrefix(QString prefix);\n    virtual void process() = 0;\n\nprotected:\n    LabelProject *project;\n    QList<QString> train_set;\n    QList<QString> validation_set;\n    QList<QString> images;\n\n    QString train_folder;\n    QString train_label_folder;\n    QString train_image_folder;\n\n    QString val_folder;\n    QString val_label_folder;\n    QString val_image_folder;\n\n    QString output_folder;\n    QString filename_prefix = \"\";\n\n    std::map<QString, int> id_map;\n\n    int image_id;\n    int label_id;\n\n    bool disable_progress = false;\n    bool append_labels = false;\n    bool validation_split = false;\n    bool export_unlabelled = false;\n    bool saveImage(cv::Mat &image, const QString output, const double scale_x = -1.0, const double scale_y = -1.0);\n};\n\n#endif // BASEEXPORTER_H\n"
  },
  {
    "path": "src/baseimporter.cpp",
    "content": "#include \"baseimporter.h\"\n\nBaseImporter::BaseImporter(QObject *parent) : QObject(parent)\n{\n\n}\n\nvoid BaseImporter::setImportUnlabelled(bool import){\n    import_unlabelled = import;\n}\n\nvoid BaseImporter::setRootFolder(QString folder){\n    label_root = folder;\n}\n\nvoid BaseImporter::addAsset(QString image_path, QList<BoundingBox> boxes){\n    if(boxes.size() == 0 && !import_unlabelled)\n        return;\n\n    if(project->addAsset(image_path)){\n\n        for(auto &box : boxes){\n            project->addLabel(image_path, box);\n        }\n    }\n}\n\nQList<QString> BaseImporter::readLines(QString path){\n    QFile file(path);\n    QList<QString> lines = {};\n\n    if (file.open(QFile::ReadOnly)) {\n        while (!file.atEnd()){\n            auto line = QString(file.readLine()).simplified();\n            lines.append(line);\n        }\n    }\n\n    return lines;\n}\n"
  },
  {
    "path": "src/baseimporter.h",
    "content": "#ifndef BASEIMPORTER_H\n#define BASEIMPORTER_H\n\n#include <QObject>\n#include <QFile>\n#include <QRegularExpression>\n#include <labelproject.h>\n#include <boundingbox.h>\n#include <cliprogressbar.h>\n\nclass BaseImporter : public QObject\n{\n    Q_OBJECT\n\npublic:\n    explicit BaseImporter(QObject *parent = nullptr);\n    void import(){};\n\n    void setRootFolder(QString folder);\n    void setImportUnlabelled(bool import);\nsignals:\n\nprotected:\n    bool import_unlabelled = false;\n    QString label_root;\n    LabelProject *project;\n\n    void addAsset(QString image_path, QList<BoundingBox> boxes);\n    QList<QString> readLines(QString path);\n\npublic slots:\n\n};\n\n#endif // BASEIMPORTER_H\n"
  },
  {
    "path": "src/birdsaiimporter.cpp",
    "content": "#include \"birdsaiimporter.h\"\n\nvoid BirdsAIImporter::import(QString sequence_folder, QString annotation_folder){\n    QDir seq_dir(sequence_folder);\n\n    // Find all sequence folders\n    QDirIterator it(sequence_folder, QDir::Dirs);\n    QList<QString> subfolders;\n\n    while (it.hasNext()) {\n        auto subfolder = QDir(it.next()).canonicalPath();\n        subfolders.append(subfolder);\n    }\n\n    if(subfolders.size() == 0){\n        qWarning() << \"Couldn't find any sequences in \" << sequence_folder;\n        return;\n    }\n\n    subfolders.removeDuplicates();\n    subfolders.sort();\n\n    for(auto &subfolder : subfolders){\n\n        qInfo() << \"Checking: \" << subfolder;\n\n        auto sequence_name = QFileInfo(subfolder).baseName();\n        auto annotation_dir = QDir(annotation_folder);\n\n        QString annotation_file = annotation_dir\n                                    .absoluteFilePath(QString(\"%1.csv\")\n                                    .arg(sequence_name));\n\n        qDebug() << \"Looking for: \" << annotation_file;\n\n        if(annotation_dir.exists(annotation_file)){\n            // Find images:\n            auto labels = getLabels(annotation_file);\n\n            QList<QList<BoundingBox>> label_list;\n            QList<QString> image_list;\n\n            qInfo() << \"Adding annotations from: \" << annotation_file;\n\n            for(auto &image : QDir(subfolder).entryList(QDir::Files)){\n                qDebug() << \"Adding labels for\" << image;\n\n                // Extract image ID, ignoring leading zeros\n                auto split_file = QFileInfo(image).baseName().split(\"_\");\n                int image_id = split_file.back().toInt();\n\n                // Get boxes for this ID and add to DB\n                auto boxes = findBoxes(labels, image_id);\n\n                QString abs_image_path = QDir(subfolder).absoluteFilePath(image);\n                if(boxes.empty() && !import_unlabelled){\n                    continue;\n                }\n\n                label_list.append(boxes);\n                image_list.append(abs_image_path);\n\n            }\n\n            project->addLabelledAssets(image_list, label_list);\n\n        }else{\n            qWarning() << \"Failed to find annotation file: \" << annotation_file;\n        }\n    }\n\n}\n\nQList<BoundingBox> BirdsAIImporter::findBoxes(QVector<QStringList> labels, int id){\n\n    QList<BoundingBox> boxes = {};\n\n    // <frame>, <id>, <bb_left>, <bb_top>, <bb_width>, <bb_height>, <class>, <species>, etc\n    for(auto &label : labels){\n\n        if(label.at(0).toInt() != id){\n            continue;\n        }\n\n        BoundingBox bbox;\n\n        /* https://sites.google.com/view/elizabethbondi/dataset\n         * -1: unknown,\n         * 0: human,\n         * 1: elephant,\n         * 2: lion,\n         * 3: giraffe,\n         * 4: dog,\n         * 5: crocodile,\n         * 6: hippo,\n         * 7: zebra,\n         * 8: rhino\n         */\n        int label_species = label.at(7).toInt();\n        bbox.classid = label_species + 2; // Since in the database they're 1-indexed\n        bbox.classname = project->getClassName(bbox.classid);\n\n        if(bbox.classname == \"\"){\n            qWarning() << \"Class ID \" << bbox.classid << \" not found in names file.\";\n        }\n\n        auto top_left = QPoint(label.at(2).toDouble(), label.at(3).toDouble());\n        auto bottom_right = top_left + QPoint(label.at(4).toDouble(), label.at(5).toDouble());\n\n        bbox.rect = QRect(top_left, bottom_right);\n        boxes.append(bbox);\n    }\n\n    return boxes;\n}\n\n\n"
  },
  {
    "path": "src/birdsaiimporter.h",
    "content": "#ifndef BIRDSAIIMPORTER_H\n#define BIRDSAIIMPORTER_H\n\n#include \"motimporter.h\"\n\nclass BirdsAIImporter : public MOTImporter\n{\npublic:\n    explicit BirdsAIImporter(LabelProject *project, QObject *parent = nullptr) : MOTImporter(project, parent){\n        this->project = project;\n    }\n    void import(QString sequence_folder, QString annotation_folder);\nprivate:\n\n    QList<BoundingBox> findBoxes(QVector<QStringList> labels, int id);\n};\n\n#endif // BIRDSAIIMPORTER_H\n"
  },
  {
    "path": "src/boundingbox.h",
    "content": "#ifndef BOUNDINGBOX_H\n#define BOUNDINGBOX_H\n\n#include <QRect>\n#include <QString>\n\nenum SelectedEdge {\n    EDGE_LEFT,\n    EDGE_TOP,\n    EDGE_RIGHT,\n    EDGE_BOTTOM,\n    EDGE_NONE,\n};\n\nenum SelectedCorner {\n    CORNER_TOPLEFT,\n    CORNER_TOPRIGHT,\n    CORNER_BOTTOMLEFT,\n    CORNER_BOTTOMRIGHT,\n    CORNER_NONE,\n};\n\nstruct BoundingBox{\n    QRect rect = QRect(0,0,0,0);\n    QString classname = \"\";\n    int occluded = 0;\n    bool truncated = false;\n    int classid = 0;\n    double confidence = 0;\n\n    // Drawing helpers\n    bool is_selected = false;\n    SelectedEdge selected_edge = EDGE_NONE;\n    SelectedCorner selected_corner = CORNER_NONE;\n\n    // ID\n    int label_id = -1;\n};\n\ninline QString printBoundingBox(BoundingBox box){\n    return QString(\"%1, xy(%2, %3) w: %4 h: %5\")\n                .arg(box.classname)\n                .arg(box.rect.left())\n                .arg(box.rect.top())\n                .arg(box.rect.width())\n                .arg(box.rect.height());\n}\n\n#endif // BOUNDINGBOX_H\n"
  },
  {
    "path": "src/cliparser.cpp",
    "content": "#include \"cliparser.h\"\n\nCliParser::CliParser(QObject *parent) : QObject(parent)\n{\n    SetupOptions();\n}\n\nvoid CliParser::SetupOptions(){\n\n    exportFormatOption = new QCommandLineOption({\"f\", \"format\"}, \"export format\", \"kitti, darknet, gcp, voc, coco, mot, birdsai, tfrecord\");\n    exportOutputFolder = new QCommandLineOption({\"o\", \"output\"}, \"output folder\", \"folder path\");\n    exportInputFile = new QCommandLineOption({\"i\", \"input\"}, \"label database\", \"file path\");\n    exportValidationSplit = new QCommandLineOption({\"s\", \"split\"}, \"validation split percentage\", \"percentage\", \"20\");\n    exportNoSubfolders = new QCommandLineOption(\"no-subfolders\", \"export directly to specified folder\");\n    exportFilePrefix = new QCommandLineOption(\"prefix\", \"filename prefix\", \"prefix\", \"\");\n    exportNamesFile = new QCommandLineOption({\"n\", \"names\"}, \"names file\", \"file path\");\n    exportGCPBucket = new QCommandLineOption(\"bucket\", \"GCP bucket\");\n    exportGCPLocal = new QCommandLineOption(\"local\", \"use local paths for GCP export\");\n    exportPascalVOCLabelMap = new QCommandLineOption(\"export-map\", \"export label.pbtxt file\");\n    exportShuffleImages = new QCommandLineOption(\"shuffle\", \"shuffle images when splitting\");\n    exportAppendLabels = new QCommandLineOption(\"append-labels\", \"append to label files\");\n    exportUnlabelledImages = new QCommandLineOption(\"export-unlabelled\", \"export images without labels\");\n    exportVideoFilename = new QCommandLineOption(\"video-filename\", \"video output filename\", \"filename\", \"out.mp4\");\n    exportVideoFourcc = new QCommandLineOption(\"fourcc\", \"video codec fourcc\", \"codec\", \"h264\");\n    exportVideoFps = new QCommandLineOption(\"fps\", \"video framerate\", \"fps\", \"10\");\n    exportVideoColourmap = new QCommandLineOption(\"colourmap\", \"video colourmap\", \"colourmap\", \"Inferno\");\n    exportVideoSize = new QCommandLineOption(\"videosize\", \"video size: width, height\", \"w,h\", \"1280,720\");\n    exportVideoDisplayBoxes = new QCommandLineOption(\"display-boxes\", \"display boxes in output\", \"on, off\", \"on\");\n    exportVideoDisplayNames = new QCommandLineOption(\"display-names\", \"display class names\", \"on, off\", \"on\");\n\n    importImages = new QCommandLineOption(\"images\", \"import image path/folder\", \"images\");\n    importRelativePath = new QCommandLineOption(\"relative-path\",\n                                                \"relative file path (for darknet)\",\n                                                \"relative\");\n    importTFRecordMask = new QCommandLineOption(\"records\", \"mask for TF Records (* wildcard)\", \"images\");\n    importAnnotations = new QCommandLineOption(\"annotations\", \"import annotation path/folder\", \"annotations\");\n    importUnlabelledImages = new QCommandLineOption(\"import-unlabelled\", \"import images without labels\");\n    importOverwrite = new QCommandLineOption(\"overwrite\", \"overwrite existing databases\");\n\n    detectChannels = new QCommandLineOption(\"detect-channels\",\n                                            \"number of channels\",\n                                            \"detect-channels\",\n                                            \"3\");\n    detectTarget = new QCommandLineOption(\"detect-target\", \"Detection target\", \"CPU, CUDA\", \"CPU\");\n    detectFramework = new QCommandLineOption(\"detect-framework\", \"Detection framework\", \"detect-framework\", \"Darknet\");\n    detectConvertDepth = new QCommandLineOption(\"convert-depth\", \"Convert 16-bit to 8-bit\");\n    detectGrayToRGB = new QCommandLineOption(\"convert-gray\", \"Convert RGB to grayscale\");\n    detectNMSThresh = new QCommandLineOption(\"nms-thresh\", \"NMS Threshold\", \"threshold\", \"0.7\");\n    detectConfThresh = new QCommandLineOption(\"conf-thresh\", \"Confidence Threshold\", \"threshold\", \"0.5\");\n    detectConfig = new QCommandLineOption(\"config\", \"Path to model config file\", \"path\");\n    detectWeights = new QCommandLineOption(\"weights\", \"Path to model weights file\", \"path\");\n\n    configSilence = new QCommandLineOption({\"q\", \"quiet\"}, \"no log messages\");\n\n    parser.addHelpOption();\n    parser.addVersionOption();\n    parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions);\n\n    parser.addPositionalArgument(\"mode\", \"[export, import]\");\n    parser.addOption(*exportFormatOption);\n    parser.addOption(*exportOutputFolder);\n    parser.addOption(*exportInputFile);\n    parser.addOption(*exportValidationSplit);\n    parser.addOption(*exportNoSubfolders);\n    parser.addOption(*exportFilePrefix);\n    parser.addOption(*exportNamesFile);\n    parser.addOption(*exportGCPBucket);\n    parser.addOption(*exportGCPLocal);\n    parser.addOption(*exportPascalVOCLabelMap);\n    parser.addOption(*exportShuffleImages);\n    parser.addOption(*exportAppendLabels);\n    parser.addOption(*exportVideoFourcc);\n    parser.addOption(*exportVideoFps);\n    parser.addOption(*exportVideoFilename);\n    parser.addOption(*exportVideoColourmap);\n    parser.addOption(*exportVideoSize);\n    parser.addOption(*exportVideoDisplayBoxes);\n    parser.addOption(*exportVideoDisplayNames);\n    parser.addOption(*exportUnlabelledImages);\n    parser.addOption(*importImages);\n    parser.addOption(*importAnnotations);\n    parser.addOption(*importRelativePath);\n    parser.addOption(*importUnlabelledImages);\n    parser.addOption(*importOverwrite);\n    parser.addOption(*importTFRecordMask);\n\n    parser.addOption(*detectChannels);\n    parser.addOption(*detectTarget);\n    parser.addOption(*detectFramework);\n    parser.addOption(*detectConvertDepth);\n    parser.addOption(*detectGrayToRGB);\n    parser.addOption(*detectNMSThresh);\n    parser.addOption(*detectConfThresh);\n    parser.addOption(*detectConfig);\n    parser.addOption(*detectWeights);\n\n    parser.addOption(*configSilence);\n\n}\n\nbool CliParser::Run(){\n\n    parser.process(*QCoreApplication::instance());\n\n    bool res = false;\n    auto mode = parser.positionalArguments().at(0);\n\n    for(auto &option : parser.unknownOptionNames()){\n        qCritical() << \"Unknown option: \" << option;\n    }\n\n    if(parser.isSet(*configSilence)){\n        qSetMessagePattern(\"\");\n    }\n\n    if(mode == \"export\"){\n        res = handleExport();\n    }else if(mode == \"import\"){\n        res = handleImport();\n    }else if(mode == \"merge\"){\n        res = handleMerge();\n    }else if(mode == \"detect\"){\n        res = handleDetect();\n    }\n\n    return res;\n}\n\nbool CliParser::handleDetect(){\n\n    DetectorOpenCV detector;\n\n    qInfo() << \"Using weights: \" << parser.value(\"weights\");\n    qInfo() << \"Using config: \" << parser.value(\"config\");\n\n    detector.setChannels(parser.value(\"detect-channels\").toInt());\n    detector.setTarget(parser.value(\"detect-target\"));\n    detector.setFramework(parser.value(\"detect-framework\"));\n    detector.setConvertGrayscale(parser.isSet(\"convert-gray\"));\n    detector.setConvertDepth(parser.isSet(\"convert-depth\"));\n    detector.setNMSThreshold(parser.value(\"nms-thresh\").toDouble());\n    detector.setConfidenceThreshold(parser.value(\"conf-thresh\").toDouble());\n    detector.loadNetwork(parser.value(\"names\").toStdString(),\n                         parser.value(\"config\").toStdString(),\n                         parser.value(\"weights\").toStdString());\n\n    LabelProject project;\n    project.loadDatabase(parser.value(\"input\"));\n\n    detector.runOnProject(&project);\n\n    return true;\n}\n\nbool CliParser::handleMerge(){\n    qCritical() << \"Not implemented yet\";\n    return false;\n}\n\nbool CliParser::handleImport(){\n\n    QString database = parser.value(\"input\");\n\n    LabelProject project;\n    if(QFileInfo(database).exists()){\n        if(!parser.isSet(\"overwrite\")){\n            qCritical() << \"Database exists, will not ovewrite.\";\n            return false;\n        }\n    }else{\n        project.createDatabase(database);\n    }\n\n    project.loadDatabase(database);\n\n    QThread* import_thread = new QThread;\n\n    bool import_unlabelled = parser.isSet(*importUnlabelledImages);\n\n    if(parser.value(\"format\") == \"darknet\"){\n\n        if(!QFileInfo(parser.value(\"images\")).exists()){\n            qCritical() << \"Image list doesn't exist\";\n            return false;\n        }\n\n        if(!QFileInfo(parser.value(\"names\")).exists()){\n            qCritical() << \"Names file doesn't exist\";\n            return false;\n        }\n\n        DarknetImporter importer(&project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_unlabelled);\n        if (parser.isSet(\"relative-path\")) {\n            importer.import(parser.value(\"images\"),\n                            parser.value(\"names\"),\n                            parser.value(\"relative-path\"));\n        } else {\n            importer.import(parser.value(\"images\"), parser.value(\"names\"));\n        }\n    }else if(parser.value(\"format\") == \"coco\"){\n\n        if(!QFileInfo(parser.value(\"annotations\")).exists()){\n            qCritical() << \"Image sequence folder doesn't exist\";\n            return false;\n        }\n\n        CocoImporter importer(&project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_unlabelled);\n        importer.import(parser.value(\"annotations\"), parser.value(\"images\"));\n    }else if(parser.value(\"format\") == \"mot\"){\n\n        if(!QDir(parser.value(\"images\")).exists()){\n            qCritical() << \"Image sequence folder doesn't exist\";\n            return false;\n        }\n\n        if(!QFileInfo(parser.value(\"names\")).exists()){\n            qCritical() << \"Names file doesn't exist\";\n            return false;\n        }\n\n        MOTImporter importer(&project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_unlabelled);\n        importer.loadClasses(parser.value(\"names\"));\n        importer.import(parser.value(\"images\"));\n    }else if(parser.value(\"format\") == \"birdsai\"){\n\n        if(!QDir(parser.value(\"annotations\")).exists()){\n            qCritical() << \"Annotations folder doesn't exist\";\n            return false;\n        }\n\n        if(!QDir(parser.value(\"images\")).exists()){\n            qCritical() << \"Image sequence folder doesn't exist\";\n            return false;\n        }\n\n        BirdsAIImporter importer(&project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_unlabelled);\n        importer.loadClasses(parser.value(\"names\"));\n        importer.import(parser.value(\"images\"),\n                        parser.value(\"annotations\"));\n    }else if(parser.value(\"format\") == \"tfrecord\"){\n\n        TFRecordImporter importer(&project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_unlabelled);\n\n        importer.import_records(parser.value(\"records\"));\n    }else if(parser.value(\"format\") == \"pascalvoc\"){\n\n        PascalVOCImporter importer(&project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_unlabelled);\n        importer.import(parser.value(\"images\"), parser.value(\"annotations\"));\n    }\n\n    return true;\n}\n\nbool CliParser::handleExport(){\n\n    QThread* export_thread = new QThread;\n    BaseExporter* exporter = nullptr;\n\n    LabelProject project;\n    QString database = parser.value(\"input\");\n\n    if(!QFileInfo(database).exists()){\n        qCritical() << \"Database file does not exist.\";\n        return false;\n    }\n\n    if(!parser.isSet(\"input\") || parser.value(\"input\") == \"\"){\n        qCritical() << \"No input file specified\";\n        return false;\n    }else{\n        qInfo() << \"Attemting to load: \" << parser.value(\"input\");\n        project.loadDatabase(parser.value(\"input\"));\n    }\n\n    if(parser.value(*exportFormatOption) == \"kitti\"){\n        exporter = new KittiExporter(&project);\n    }else if(parser.value(*exportFormatOption) == \"darknet\"){\n        exporter = new DarknetExporter(&project);\n        if(parser.isSet(*exportNamesFile)){\n            static_cast<DarknetExporter*>(exporter)->generateLabelIds(parser.value(*exportNamesFile));\n        }else{\n            qCritical() << \"No names file specifed.\";\n            return false;\n        }\n\n    }else if(parser.value(*exportFormatOption) == \"voc\"){\n        exporter = new PascalVocExporter(&project);\n        if(parser.isSet(*exportPascalVOCLabelMap)){\n            static_cast<PascalVocExporter*>(exporter)->setExportMap(true);\n        }\n        exporter->process();\n    }else if(parser.value(*exportFormatOption) == \"coco\"){\n        exporter = new CocoExporter(&project);\n    }else if(parser.value(*exportFormatOption) == \"gcp\"){\n        exporter = new GCPExporter(&project);\n        if(parser.isSet(*exportGCPLocal)){\n            bool local = true;\n            static_cast<GCPExporter*>(exporter)->setBucket(\"\", local);\n        }else if(parser.isSet(*exportGCPBucket)){\n            static_cast<GCPExporter*>(exporter)->setBucket(parser.value(*exportGCPBucket));\n        }else{\n            qCritical() << \"Using bucket and no path specifed.\";\n            return false;\n        }\n    }else if(parser.value(*exportFormatOption) == \"tfrecord\"){\n        exporter = new TFRecordExporter(&project);\n        if(parser.isSet(*exportNamesFile)){\n            static_cast<TFRecordExporter*>(exporter)->generateLabelIds(parser.value(*exportNamesFile));\n        }else{\n            qCritical() << \"No names file specifed.\";\n            return false;\n        }\n    }else if(parser.value(*exportFormatOption) == \"video\"){\n        exporter = new VideoExporter(&project);\n        auto filename = parser.value(*exportVideoFilename);\n        auto fourcc = parser.value(*exportVideoFourcc);\n\n        bool ok = false;\n        auto fps = parser.value(*exportVideoFps).toDouble(&ok);\n        if(!ok){\n            qCritical() << \"Invalid FPS, you specified: \" << parser.value(*exportVideoFps);\n            return false;\n        }\n\n        // Check video size\n        auto videosize = parser.value(*exportVideoSize).split(\",\");\n        if(videosize.size() != 2){\n            qCritical() << \"Video size should be two comma separated values: w,h\";\n            return false;\n        }\n\n        auto width = parser.value(*exportVideoSize).split(\",\").at(0).toInt(&ok);\n        if(!ok){\n            qCritical() << \"Invalid video width, you specified: \" << parser.value(*exportVideoSize).split(\",\").at(0);\n            return false;\n        }\n\n        auto height = parser.value(*exportVideoSize).split(\",\").at(1).toInt(&ok);\n        if(!ok){\n            qCritical() << \"Invalid video height, you specified: \" << parser.value(*exportVideoSize).split(\",\").at(1);\n            return false;\n        }\n\n        auto colourmap = parser.value(*exportVideoColourmap);\n\n        static_cast<VideoExporter*>(exporter)->videoConfig(filename, fourcc, fps, {width, height}, colourmap);\n        static_cast<VideoExporter*>(exporter)->labelConfig(parser.value(*exportVideoDisplayNames) == \"on\",\n                                                           parser.value(*exportVideoDisplayBoxes) == \"on\");\n\n\n        if(!parser.isSet(*exportOutputFolder) || parser.value(*exportOutputFolder) == \"\"){\n            qCritical() << \"Please specify an output folder\";\n            return false;\n        }else{\n            exporter->setOutputFolder(parser.value(*exportOutputFolder), true);\n        }\n\n        exporter->process();\n        return true;\n\n    }else{\n        qCritical() << \"Invalid exporter type specified\";\n        return false;\n    }\n\n    if(exporter != nullptr){\n        exporter->moveToThread(export_thread);\n\n        // No progress bar\n        exporter->disableProgress(true);\n        exporter->setExportUnlabelled(parser.isSet(*exportUnlabelledImages));\n\n        if(parser.isSet(*exportFilePrefix)){\n            exporter->setFilenamePrefix(parser.value(*exportFilePrefix));\n        }\n\n        exporter->setAppendLabels(parser.isSet(*exportAppendLabels));\n\n        if(parser.isSet(*exportValidationSplit)){\n            exporter->setValidationSplit(true);\n            exporter->splitData(parser.value(*exportValidationSplit).toFloat(), parser.isSet(*exportShuffleImages));\n        }else{\n            exporter->setValidationSplit(false);\n            exporter->splitData(0, parser.isSet(*exportShuffleImages));\n        }\n\n        if(!parser.isSet(*exportOutputFolder) || parser.value(*exportOutputFolder) == \"\"){\n            qCritical() << \"Please specify an output folder\";\n            return false;\n        }else{\n            exporter->setOutputFolder(parser.value(*exportOutputFolder), parser.isSet(*exportNoSubfolders));\n        }\n\n        exporter->process();\n\n    }else{\n        qFatal(\"Failed to instantiate exporter\");\n        return false;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "src/cliparser.h",
    "content": "#ifndef CLIPARSER_H\n#define CLIPARSER_H\n\n#include <QObject>\n#include <QCommandLineParser>\n#include <exporter.h>\n#include <importer.h>\n#include <detection/detectoropencv.h>\n\n\nclass CliParser : public QObject\n{\n    Q_OBJECT\npublic:\n    explicit CliParser(QObject *parent = nullptr);\n    bool Run();\n\nprivate:\n    void SetupOptions();\n    bool handleExport();\n    bool handleImport();\n    bool handleMerge();\n    bool handleDetect();\n\n    LabelProject getProject(bool &ok);\n\n    QCommandLineParser parser;\n\n    QCommandLineOption *exportFormatOption;\n    QCommandLineOption *exportOutputFolder;\n    QCommandLineOption *exportInputFile;\n    QCommandLineOption *exportValidationSplit;\n    QCommandLineOption *exportNoSubfolders;\n    QCommandLineOption *exportFilePrefix;\n    QCommandLineOption *exportNamesFile;\n    QCommandLineOption *exportGCPBucket;\n    QCommandLineOption *exportGCPLocal;\n    QCommandLineOption *exportPascalVOCLabelMap;\n    QCommandLineOption *exportShuffleImages;\n    QCommandLineOption *exportAppendLabels;\n    QCommandLineOption *exportUnlabelledImages;\n\n    QCommandLineOption *exportVideoFilename;\n    QCommandLineOption *exportVideoFourcc;\n    QCommandLineOption *exportVideoFps;\n    QCommandLineOption *exportVideoColourmap;\n    QCommandLineOption *exportVideoSize;\n    QCommandLineOption *exportVideoDisplayNames;\n    QCommandLineOption *exportVideoDisplayBoxes;\n\n    QCommandLineOption *importImages;\n    QCommandLineOption *importAnnotations;\n    QCommandLineOption *importUnlabelledImages;\n    QCommandLineOption *importOverwrite;\n    QCommandLineOption *importTFRecordMask;\n    QCommandLineOption *importRelativePath;\n\n    QCommandLineOption *detectChannels;\n    QCommandLineOption *detectTarget;\n    QCommandLineOption *detectFramework;\n    QCommandLineOption *detectConvertDepth;\n    QCommandLineOption *detectGrayToRGB;\n    QCommandLineOption *detectNMSThresh;\n    QCommandLineOption *detectConfThresh;\n    QCommandLineOption *detectConfig;\n    QCommandLineOption *detectWeights;\n\n    QCommandLineOption *configSilence;\n\n\nsignals:\n\n};\n\n#endif // CLIPARSER_H\n"
  },
  {
    "path": "src/cliprogressbar.h",
    "content": "#ifndef CLIPROGRESSBAR_H\n#define CLIPROGRESSBAR_H\n\n#include <iostream>\n\n// Thanks to https://github.com/GregoryConrad/pBar\n\nclass cliProgressBar {\npublic:\n    void update(double newProgress) {\n        currentProgress = std::ceil(newProgress);\n        amountOfFiller = (int)((currentProgress / neededProgress)*(double)pBarLength);\n    }\n    void print() {\n        currUpdateVal %= pBarUpdater.length();\n        std::cout << \"\\r\" //Bring cursor to start of line\n            << firstPartOfpBar; //Print out first part of pBar\n        for (int a = 0; a < amountOfFiller; a++) { //Print out current progress\n            std::cout << pBarFiller;\n        }\n        std::cout << pBarUpdater[currUpdateVal];\n        for (int b = 0; b < pBarLength - amountOfFiller; b++) { //Print out spaces\n            std::cout << \" \";\n        }\n        std::cout << lastPartOfpBar //Print out last part of progress bar\n            << \" (\" << (int)(100*(currentProgress/neededProgress)) << \"%)\" //This just prints out the percent\n            << std::flush;\n        currUpdateVal += 1;\n    }\n    std::string firstPartOfpBar = \"[\", //Change these at will (that is why I made them public)\n        lastPartOfpBar = \"]\",\n        pBarFiller = \"|\",\n        pBarUpdater = \"/-\\\\|\";\nprivate:\n    int amountOfFiller,\n        pBarLength = 50, //I would recommend NOT changing this\n        currUpdateVal = 0; //Do not change\n    double currentProgress = 0, //Do not change\n        neededProgress = 100; //I would recommend NOT changing this\n};\n\n#endif // CLIPROGRESSBAR_H\n"
  },
  {
    "path": "src/cocoexporter.cpp",
    "content": "#include \"cocoexporter.h\"\n\nbool CocoExporter::processImages(const QString folder, const QString label_filename, const QList<QString> images){\n\n    QString image_path;\n    QList<BoundingBox> labels;\n\n    auto now = QDateTime::currentDateTime();\n    QJsonObject label_file;\n\n    QJsonObject label_info;\n    label_info[\"year\"] = now.date().year();\n    label_info[\"version\"] = \"1\";\n    label_info[\"description\"] = \"Description\";\n    label_info[\"contributor\"] = \"Contributor\";\n    label_info[\"date_created\"] = now.date().toString(Qt::ISODate);\n    label_info[\"url\"] = \"\";\n\n    label_file[\"info\"] = label_info;\n\n    QJsonArray annotations_array;\n    QJsonArray licenses_array;\n    QJsonArray image_array;\n    QJsonArray category_array;\n    QList<QString> classnames;\n\n    project->getClassList(classnames);\n\n    for(auto &classname : classnames){\n        QJsonObject category;\n        category[\"id\"] = project->getClassId(classname);\n        category[\"name\"] = classname;\n        category[\"supercategory\"] = classname;\n\n        category_array.append(category);\n    }\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setWindowTitle(\"Exporting images\");\n\n    if(disable_progress){\n        progress.hide();\n    }\n\n    int i = 0;\n\n    foreach(image_path, images){\n        if(progress.wasCanceled())\n            break;\n\n        project->getLabels(image_path, labels);\n\n        if(!export_unlabelled && labels.size() == 0){\n            if(!disable_progress){\n                progress.setValue(i);\n                progress.setLabelText(QString(\"%1 is unlabelled\").arg(image_path));\n                progress.repaint();\n                QApplication::processEvents();\n            }\n            continue;\n        }\n\n        QString abs_image_path = project->getDbFolder().absoluteFilePath(image_path);\n        QString extension = QFileInfo(image_path).suffix();\n        QString filename_noext = QFileInfo(image_path).completeBaseName();\n\n        QString image_filename = QString(\"%1/%2%3.%4\")\n                                        .arg(folder)\n                                        .arg(filename_prefix)\n                                        .arg(filename_noext)\n                                        .arg(extension);\n\n        // Correct for duplicate file names in output\n        int dupe_file = 1;\n        while(QFile(image_filename).exists()){\n            image_filename = QString(\"%1/%2%3_%4.%5\")\n                                    .arg(folder)\n                                    .arg(filename_prefix)\n                                    .arg(filename_noext)\n                                    .arg(dupe_file++)\n                                    .arg(extension);\n        }\n\n        cv::Mat image = cv::imread(abs_image_path.toStdString());\n        //saveImage(image, image_filename);\n\n        if(image.empty()){\n            qWarning() << \"Failed top open image\" << abs_image_path;\n            continue;\n        }\n\n        auto copied = QFile::copy(abs_image_path, image_filename);\n        if(!copied){\n            qWarning() << \"Failed to copy image\" << image_filename;\n        }\n\n        QJsonObject image_info;\n        image_info[\"id\"] = image_id;\n        image_info[\"width\"] = image.cols;\n        image_info[\"height\"] = image.rows;\n        image_info[\"file_name\"] = image_filename;\n        image_info[\"license\"] = 0;\n        image_info[\"flickr_url\"] = \"\";\n        image_info[\"coco_url\"] = \"\";\n        image_info[\"date_captured\"] = now.date().toString(Qt::ISODate);\n\n        image_array.append(image_info);\n\n        for(auto &label : labels){\n            QJsonObject annotation;\n            annotation[\"id\"] = label_id;\n            annotation[\"image_id\"] = image_id;\n            annotation[\"category_id\"] = label.classid;\n\n            QJsonArray segmentation;\n            segmentation.append(QString::number(label.rect.topLeft().x()));\n            segmentation.append(QString::number(label.rect.topLeft().y()));\n            segmentation.append(QString::number(label.rect.topRight().x()));\n            segmentation.append(QString::number(label.rect.topRight().y()));\n            segmentation.append(QString::number(label.rect.bottomRight().x()));\n            segmentation.append(QString::number(label.rect.bottomRight().y()));\n            segmentation.append(QString::number(label.rect.bottomLeft().x()));\n            segmentation.append(QString::number(label.rect.bottomLeft().y()));\n\n            QJsonArray segmentation_array;\n            segmentation_array.append(segmentation);\n            annotation[\"segmentation\"] = segmentation_array;\n\n            annotation[\"area\"] = label.rect.width()*label.rect.height();\n\n            QJsonArray bbox;\n            bbox.append(QString::number(label.rect.x()));\n            bbox.append(QString::number(label.rect.y()));\n            bbox.append(QString::number(label.rect.width()));\n            bbox.append(QString::number(label.rect.height()));\n\n            annotation[\"bbox\"] = bbox;\n            annotation[\"iscrowd\"] = 0;\n\n            annotations_array.append(annotation);\n            label_id++;\n        }\n\n        image_id++;\n\n        if(!disable_progress){\n            progress.setValue(++i);\n            progress.setLabelText(image_path);\n        }\n    }\n\n    QJsonObject license;\n    license[\"id\"] = 0;\n    license[\"name\"] = \"\";\n    license[\"url\"] = \"\";\n\n    licenses_array.append(license);\n\n    label_file[\"images\"] = image_array;\n    label_file[\"annotations\"] = annotations_array;\n    label_file[\"license\"] = licenses_array;\n    label_file[\"categories\"] = category_array;\n\n    QString label_filepath = QString(\"%1/%2.json\").arg(output_folder).arg(label_filename);\n\n    QJsonDocument json_output(label_file);\n    QFile f(label_filepath);\n\n    f.open(QIODevice::WriteOnly | QIODevice::Truncate);\n\n    if(f.isOpen()){\n        f.write(json_output.toJson());\n    }\n\n    f.close();\n\n    return true;\n}\n\nvoid CocoExporter::process(){\n    image_id = 0;\n    label_id = 0;\n\n    processImages(train_folder, \"train\", train_set);\n    processImages(val_folder, \"val\", validation_set);\n}\n"
  },
  {
    "path": "src/cocoexporter.h",
    "content": "#ifndef COCOEXPORTER_H\n#define COCOEXPORTER_H\n\n#include <baseexporter.h>\n\n#include <QJsonDocument>\n#include <QJsonArray>\n#include <QJsonObject>\n\nclass CocoExporter : public BaseExporter\n{\n    Q_OBJECT\npublic:\n    explicit CocoExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n\npublic slots:\n    void process();\n\nprivate:\n\n    bool processImages(const QString folder, const QString filename, const QList<QString> images);\n};\n\n#endif // COCOEXPORTER_H\n"
  },
  {
    "path": "src/cocoimporter.cpp",
    "content": "#include \"cocoimporter.h\"\n\nvoid CocoImporter::import(QString annotation_file, QString image_folder){\n    // Load Json\n    qDebug() << \"Loading COCO JSON\";\n    QFile loadFile(annotation_file);\n    loadFile.open(QIODevice::ReadOnly | QIODevice::Text);\n    QByteArray json_data = loadFile.readAll();\n    auto json = QJsonDocument::fromJson(json_data);\n\n    // Load classes (categories)\n    auto categories = json.object().value(\"categories\");\n    if(categories == QJsonValue::Undefined){\n        qCritical() << \"Categories not found\";\n        return;\n    }\n\n    QMap<int, QString> classes;\n    QSet<QString> class_set;\n    QJsonValue item;\n    foreach(item, categories.toArray()){\n\n        auto item_name = item.toObject().value(\"name\");\n        if(item_name == QJsonValue::Undefined || !item_name.isString())\n            continue;\n\n        auto item_id = item.toObject().value(\"id\");\n        if(item_id == QJsonValue::Undefined || !item_id.isDouble())\n            continue;\n\n        classes.insert(item_id.toInt(), item_name.toString());\n        qDebug() << \"added: \" << item_id.toInt() << \" \" << item_name.toString();\n\n        class_set.insert(item_name.toString());\n    }\n\n    for(auto &cls : class_set){\n        project->addClass(cls);\n    }\n\n    // Load images\n    auto images = json.object().value(\"images\");\n    if(images == QJsonValue::Undefined){\n        qCritical() << \"No images found\";\n        return;\n    }\n\n    QJsonValue image;\n    QList<QString> image_list;\n    QList<QList<BoundingBox>> label_list;\n    QMap<int, int> image_index;\n    QMap<int, QString> image_map;\n    int i=0;\n    foreach(image, images.toArray()){\n        auto file_name = image.toObject().value(\"file_name\");\n        if(file_name == QJsonValue::Undefined || !file_name.isString())\n            continue;\n\n        auto file_id = image.toObject().value(\"id\");\n        if(file_id == QJsonValue::Undefined || !file_id.isDouble())\n            continue;\n\n        auto abs_file_name = QDir(image_folder).absoluteFilePath(file_name.toString());\n        image_map.insert(file_id.toInt(), abs_file_name);\n\n        image_list.append(abs_file_name);\n        label_list.append(QList<BoundingBox> {});\n        image_index.insert(file_id.toInt(), i++);\n    }\n\n    // Load labels\n    auto annotations = json.object().value(\"annotations\");\n    if(annotations == QJsonValue::Undefined){\n        qCritical() << \"No annotations found\";\n        return;\n    }\n\n    QJsonValue annotation;\n\n    foreach(annotation, annotations.toArray()){\n\n        auto image_id = annotation.toObject().value(\"image_id\");\n        if(image_id == QJsonValue::Undefined || !image_id.isDouble())\n            continue;\n\n        auto category_id = annotation.toObject().value(\"category_id\");\n        if(category_id == QJsonValue::Undefined || !category_id.isDouble())\n            continue;\n\n        auto image_filename = image_map[image_id.toInt()];\n\n        auto bbox = annotation.toObject().value(\"bbox\");\n        if(bbox == QJsonValue::Undefined || !bbox.isArray()){\n            qWarning() << \"No bounding box found for\" << image_id;\n            continue;\n        }\n\n        auto bbox_array = bbox.toArray();\n\n        if(bbox_array.size() != 4)\n            continue;\n\n        int x = static_cast<int>(bbox_array.at(0).toDouble());\n        int y = static_cast<int>(bbox_array.at(1).toDouble());\n        int w = static_cast<int>(bbox_array.at(2).toDouble());\n        int h = static_cast<int>(bbox_array.at(3).toDouble());\n\n        if(w == 0){\n            qWarning() << \"Bounding box for\" << image_filename << \"has zero width\";\n            continue;\n        }\n\n        if(h == 0){\n            qWarning() << \"Bounding box for\" << image_filename << \"has zero height\";\n            continue;\n        }\n\n        BoundingBox new_bbox;\n        new_bbox.rect.setX(x);\n        new_bbox.rect.setY(y);\n        new_bbox.rect.setWidth(w);\n        new_bbox.rect.setHeight(h);\n        int cat_id = static_cast<int>(category_id.toDouble());\n        new_bbox.classname = classes[cat_id];\n        new_bbox.classid = project->getClassId(new_bbox.classname);\n\n        label_list[image_index[image_id.toInt()]].push_back(new_bbox);\n    }\n\n    project->addLabelledAssets(image_list, label_list);\n\n    return;\n}\n"
  },
  {
    "path": "src/cocoimporter.h",
    "content": "#ifndef COCOIMPORTER_H\n#define COCOIMPORTER_H\n\n#include<baseimporter.h>\n#include <QJsonDocument>\n#include <QJsonArray>\n#include <QJsonObject>\n\nclass CocoImporter : public BaseImporter\n{\npublic:\n    using BaseImporter::import;\n\n    explicit CocoImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){\n        this->project = project;\n    }\n\n    void import(QString annotations_file, QString image_folder);\n};\n\n#endif // COCOIMPORTER_H\n"
  },
  {
    "path": "src/crc32.cpp",
    "content": "#include \"crc32.h\"\n#include <stdint.h>\n\nnamespace tf_crc{\n\nstatic const uint32_t table0_[256] = {\n    0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c,\n    0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,\n    0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c,\n    0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,\n    0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc,\n    0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,\n    0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512,\n    0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,\n    0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad,\n    0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,\n    0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf,\n    0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,\n    0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f,\n    0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,\n    0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f,\n    0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,\n    0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e,\n    0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,\n    0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e,\n    0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,\n    0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de,\n    0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,\n    0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4,\n    0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,\n    0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b,\n    0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,\n    0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5,\n    0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,\n    0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975,\n    0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,\n    0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905,\n    0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,\n    0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8,\n    0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,\n    0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8,\n    0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,\n    0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78,\n    0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,\n    0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6,\n    0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,\n    0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69,\n    0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,\n    0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351};\nstatic const uint32_t table1_[256] = {\n    0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, 0x4e8a61dc, 0x5d28f9ab,\n    0x69cf5132, 0x7a6dc945, 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21,\n    0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, 0x3fc5f181, 0x2c6769f6,\n    0x1880c16f, 0x0b225918, 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4,\n    0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, 0xec5b53e5, 0xfff9cb92,\n    0xcb1e630b, 0xd8bcfb7c, 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b,\n    0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, 0xe29f20ba, 0xf13db8cd,\n    0xc5da1054, 0xd6788823, 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff,\n    0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, 0x0ec4735f, 0x1d66eb28,\n    0x298143b1, 0x3a23dbc6, 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2,\n    0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, 0xff17c604, 0xecb55e73,\n    0xd852f6ea, 0xcbf06e9d, 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41,\n    0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, 0x2c896460, 0x3f2bfc17,\n    0x0bcc548e, 0x186eccf9, 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c,\n    0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, 0x5dc6f43d, 0x4e646c4a,\n    0x7a83c4d3, 0x69215ca4, 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78,\n    0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, 0xce1644da, 0xddb4dcad,\n    0xe9537434, 0xfaf1ec43, 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27,\n    0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, 0xbf59d487, 0xacfb4cf0,\n    0x981ce469, 0x8bbe7c1e, 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2,\n    0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, 0x6cc776e3, 0x7f65ee94,\n    0x4b82460d, 0x5820de7a, 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260,\n    0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, 0x66d73941, 0x7575a136,\n    0x419209af, 0x523091d8, 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004,\n    0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, 0x8a8c6aa4, 0x992ef2d3,\n    0xadc95a4a, 0xbe6bc23d, 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059,\n    0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, 0x844819fb, 0x97ea818c,\n    0xa30d2915, 0xb0afb162, 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be,\n    0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, 0x57d6bb9f, 0x447423e8,\n    0x70938b71, 0x63311306, 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3,\n    0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, 0x26992bc2, 0x353bb3b5,\n    0x01dc1b2c, 0x127e835b, 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287,\n    0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, 0x4a5e5d21, 0x59fcc556,\n    0x6d1b6dcf, 0x7eb9f5b8, 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc,\n    0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, 0x3b11cd7c, 0x28b3550b,\n    0x1c54fd92, 0x0ff665e5, 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439,\n    0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, 0xe88f6f18, 0xfb2df76f,\n    0xcfca5ff6, 0xdc68c781, 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766,\n    0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, 0xe64b1c47, 0xf5e98430,\n    0xc10e2ca9, 0xd2acb4de, 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502,\n    0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, 0x0a104fa2, 0x19b2d7d5,\n    0x2d557f4c, 0x3ef7e73b, 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f,\n    0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483};\nstatic const uint32_t table2_[256] = {\n    0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, 0x9edea41a, 0x3b9f3664,\n    0xd1b1f617, 0x74f06469, 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6,\n    0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, 0x70a27d8a, 0xd5e3eff4,\n    0x3fcd2f87, 0x9a8cbdf9, 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3,\n    0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, 0xd62de755, 0x736c752b,\n    0x9942b558, 0x3c032726, 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67,\n    0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, 0xd915c5d1, 0x7c5457af,\n    0x967a97dc, 0x333b05a2, 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8,\n    0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, 0x0f382284, 0xaa79b0fa,\n    0x40577089, 0xe516e2f7, 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828,\n    0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, 0xc76580d9, 0x622412a7,\n    0x880ad2d4, 0x2d4b40aa, 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0,\n    0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, 0x61ea1a06, 0xc4ab8878,\n    0x2e85480b, 0x8bc4da75, 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20,\n    0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, 0x8f96c396, 0x2ad751e8,\n    0xc0f9919b, 0x65b803e5, 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff,\n    0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, 0xb8ffdfd7, 0x1dbe4da9,\n    0xf7908dda, 0x52d11fa4, 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b,\n    0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, 0x56830647, 0xf3c29439,\n    0x19ec544a, 0xbcadc634, 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e,\n    0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, 0xf00c9c98, 0x554d0ee6,\n    0xbf63ce95, 0x1a225ceb, 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730,\n    0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, 0xb3764986, 0x1637dbf8,\n    0xfc191b8b, 0x595889f5, 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def,\n    0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, 0x655baed3, 0xc01a3cad,\n    0x2a34fcde, 0x8f756ea0, 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f,\n    0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, 0x6a638c57, 0xcf221e29,\n    0x250cde5a, 0x804d4c24, 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e,\n    0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, 0xccec1688, 0x69ad84f6,\n    0x83834485, 0x26c2d6fb, 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae,\n    0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, 0x2290cf18, 0x87d15d66,\n    0x6dff9d15, 0xc8be0f6b, 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71,\n    0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, 0xd29c5380, 0x77ddc1fe,\n    0x9df3018d, 0x38b293f3, 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c,\n    0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, 0x3ce08a10, 0x99a1186e,\n    0x738fd81d, 0xd6ce4a63, 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79,\n    0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, 0x9a6f10cf, 0x3f2e82b1,\n    0xd50042c2, 0x7041d0bc, 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd,\n    0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, 0x9557324b, 0x3016a035,\n    0xda386046, 0x7f79f238, 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622,\n    0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, 0x437ad51e, 0xe63b4760,\n    0x0c158713, 0xa954156d, 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2,\n    0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8};\nstatic const uint32_t table3_[256] = {\n    0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, 0x7b2231f3, 0xa6679b4b,\n    0xc4451272, 0x1900b8ca, 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf,\n    0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, 0xe964b13d, 0x34211b85,\n    0x560392bc, 0x8b463804, 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7,\n    0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, 0x6402e328, 0xb9474990,\n    0xdb65c0a9, 0x06206a11, 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2,\n    0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, 0x2161776d, 0xfc24ddd5,\n    0x9e0654ec, 0x4343fe54, 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7,\n    0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, 0x45639445, 0x98263efd,\n    0xfa04b7c4, 0x27411d7c, 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69,\n    0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, 0xaba65fe7, 0x76e3f55f,\n    0x14c17c66, 0xc984d6de, 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d,\n    0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, 0x26c00df2, 0xfb85a74a,\n    0x99a72e73, 0x44e284cb, 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3,\n    0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, 0xb4868d3c, 0x69c32784,\n    0x0be1aebd, 0xd6a40405, 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6,\n    0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, 0x07a17a9f, 0xdae4d027,\n    0xb8c6591e, 0x6583f3a6, 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3,\n    0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, 0x95e7fa51, 0x48a250e9,\n    0x2a80d9d0, 0xf7c57368, 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b,\n    0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, 0x1881a844, 0xc5c402fc,\n    0xa7e68bc5, 0x7aa3217d, 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006,\n    0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, 0xa4e4aad9, 0x79a10061,\n    0x1b838958, 0xc6c623e0, 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213,\n    0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, 0xc0e649f1, 0x1da3e349,\n    0x7f816a70, 0xa2c4c0c8, 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd,\n    0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, 0x8585ddb4, 0x58c0770c,\n    0x3ae2fe35, 0xe7a7548d, 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e,\n    0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, 0x08e38fa1, 0xd5a62519,\n    0xb784ac20, 0x6ac10698, 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0,\n    0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, 0x9aa50f6f, 0x47e0a5d7,\n    0x25c22cee, 0xf8878656, 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5,\n    0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, 0x8224a72b, 0x5f610d93,\n    0x3d4384aa, 0xe0062e12, 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07,\n    0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, 0x106227e5, 0xcd278d5d,\n    0xaf050464, 0x7240aedc, 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f,\n    0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, 0x9d0475f0, 0x4041df48,\n    0x22635671, 0xff26fcc9, 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a,\n    0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, 0xd867e1b5, 0x05224b0d,\n    0x6700c234, 0xba45688c, 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f,\n    0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, 0xbc65029d, 0x6120a825,\n    0x0302211c, 0xde478ba4, 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1,\n    0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842};\n\n// Used to fetch a naturally-aligned 32-bit word in little endian byte-order\nstatic inline uint32_t LE_LOAD32(const uint8_t *p) {\n    // Taken from tensorflow/tensorflow/core/platform/raw_coding.h\n    return ((static_cast<uint32_t>(static_cast<unsigned char>(p[0]))) |\n               (static_cast<uint32_t>(static_cast<unsigned char>(p[1])) << 8) |\n               (static_cast<uint32_t>(static_cast<unsigned char>(p[2])) << 16) |\n               (static_cast<uint32_t>(static_cast<unsigned char>(p[3])) << 24));\n}\n\nuint32_t Extend(uint32_t crc, const char *buf, size_t size) {\n  const uint8_t *p = reinterpret_cast<const uint8_t *>(buf);\n  const uint8_t *e = p + size;\n  uint32_t l = crc ^ 0xffffffffu;\n\n#define STEP1                  \\\n  do {                         \\\n    int c = (l & 0xff) ^ *p++; \\\n    l = table0_[c] ^ (l >> 8); \\\n  } while (0)\n\n#define STEP4                                          \\\n  do {                                                 \\\n    uint32_t c = l ^ LE_LOAD32(p);                       \\\n    p += 4;                                            \\\n    l = table3_[c & 0xff] ^ table2_[(c >> 8) & 0xff] ^ \\\n        table1_[(c >> 16) & 0xff] ^ table0_[c >> 24];  \\\n  } while (0)\n\n  // Point x at first 4-byte aligned byte in string.  This might be\n  // just past the end of the string.\n  const uintptr_t pval = reinterpret_cast<uintptr_t>(p);\n  const uint8_t *x = reinterpret_cast<const uint8_t *>(((pval + 3) >> 2) << 2);\n  if (x <= e) {\n    // Process bytes until finished or p is 4-byte aligned\n    while (p != x) {\n      STEP1;\n    }\n  }\n  // Process bytes 16 at a time\n  while ((e - p) >= 16) {\n    STEP4;\n    STEP4;\n    STEP4;\n    STEP4;\n  }\n  // Process bytes 4 at a time\n  while ((e - p) >= 4) {\n    STEP4;\n  }\n  // Process the last few bytes\n  while (p != e) {\n    STEP1;\n  }\n#undef STEP4\n#undef STEP1\n  return l ^ 0xffffffffu;\n}\n}\n"
  },
  {
    "path": "src/crc32.h",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef CRC32_H\n#define CRC32_H\n\n#include <cstdint>\n#include <stddef.h>\n\nnamespace tf_crc{\n\n    // Return the crc32c of concat(A, data[0,n-1]) where init_crc is the\n    // crc32c of some string A.  Extend() is often used to maintain the\n    // crc32c of a stream of data.\n    extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n);\n\n    // Return the crc32c of data[0,n-1]\n    inline uint32_t Value(const char* data, size_t n) { return Extend(0, data, n); }\n\n    #if defined(PLATFORM_GOOGLE)\n    extern uint32_t Extend(uint32_t init_crc, const absl::Cord& cord);\n    inline uint32_t Value(const absl::Cord& cord) { return Extend(0, cord); }\n    #endif\n\n    static const uint32_t kMaskDelta = 0xa282ead8ul;\n\n    // Return a masked representation of crc.\n    //\n    // Motivation: it is problematic to compute the CRC of a string that\n    // contains embedded CRCs.  Therefore we recommend that CRCs stored\n    // somewhere (e.g., in files) should be masked before being stored.\n    inline uint32_t Mask(uint32_t crc) {\n      // Rotate right by 15 bits and add a constant.\n      return ((crc >> 15) | (crc << 17)) + kMaskDelta;\n    }\n\n    // Return the crc whose masked representation is masked_crc.\n    inline uint32_t Unmask(uint32_t masked_crc) {\n      uint32_t rot = masked_crc - kMaskDelta;\n      return ((rot >> 17) | (rot << 15));\n    }\n}\n\n#endif  // TENSORFLOW_CORE_LIB_HASH_CRC32C_H_\n"
  },
  {
    "path": "src/darknetexporter.cpp",
    "content": "#include \"darknetexporter.h\"\n\nvoid DarknetExporter::generateLabelIds(const QString names_file){\n    id_map.clear();\n\n    // Force sort list\n    QStringList class_list;\n    QFile fh(names_file);\n\n    if (fh.open(QIODevice::ReadOnly)) {\n\n        while (!fh.atEnd()) {\n            // Darknet name file is just a newline delimited list of classes\n            QByteArray line = fh.readLine();\n            class_list.append(line);\n        }\n    }\n\n    if(class_list.size() == 0){\n        qWarning() << \"No classes found in names file.\";\n        return;\n    }\n\n    int i = 0;\n    for(auto &name : class_list){\n        auto cleaned_name = name.simplified().toLower();\n        id_map[cleaned_name] = i++;\n        qDebug() << \"Adding: \" << cleaned_name << \" (\" << i << \")\";\n    }\n}\n\ndouble clamp(double val, double min, double max){\n    if(val < min) val = min;\n    if(val > max) val = max;\n    return val;\n}\n\nvoid DarknetExporter::writeLabels(const cv::Mat &image, const QString label_filename, const QList<BoundingBox> labels){\n\n    // Still make a label file even if there are no detections. This is important\n    // for background class detection.\n\n    QFile f(label_filename);\n\n    // Delete existing files for simplicity.\n    if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {\n        BoundingBox label;\n        foreach(label, labels){\n            QString text;\n\n            // Check if this label exists in the database\n            if(id_map.find(label.classname.toLower()) == id_map.end()){\n                qWarning() << \"Couldn't find this label in the names file: \" << label.classname.toLower();\n                continue;\n            }\n\n            double x = clamp(static_cast<double>(label.rect.center().x())/image.cols, 0.0, 0.999);\n            double y = clamp(static_cast<double>(label.rect.center().y())/image.rows, 0.0, 0.999);\n            double width = clamp(static_cast<double>(label.rect.width())/image.cols, 0.0, 0.999);\n            double height = clamp(static_cast<double>(label.rect.height())/image.rows, 0.0, 0.999);\n\n            text += QString(\"%1\").arg(id_map[label.classname.toLower()]);\n            text += QString(\" %1\").arg(x);\n            text += QString(\" %1\").arg(y);\n            text += QString(\" %1\").arg(width);\n            text += QString(\" %1\").arg(height);\n            text += \"\\n\";\n\n            f.write(text.toUtf8());\n\n        }\n    }\n}\n\nbool DarknetExporter::processImages(const QString folder, const QList<QString> images, export_image_type split_type){\n\n    QString image_path;\n    QList<BoundingBox> labels;\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n\n    if(folder == \"\"){\n        qCritical() << \"Invalid folder specified.\";\n        return false;\n    }\n\n    QString split_text = \"\";\n    if(split_type == EXPORT_VAL){\n        split_text = \"VAL\";\n        progress.setWindowTitle(\"Exporting validation images\");\n    }else if(split_type == EXPORT_TRAIN){\n        split_text = \"TRAIN\";\n        progress.setWindowTitle(\"Exporting train images\");\n    }else if(split_type == EXPORT_TEST){\n        split_text = \"TEST\";\n        progress.setWindowTitle(\"Exporting test images\");\n    }else{\n        split_text = \"UNASSIGNED\";\n    }\n\n    if(!disable_progress){\n        progress.hide();\n    }\n\n    int i = 0;\n\n    foreach(image_path, images){\n\n        if(progress.wasCanceled()){\n            break;\n        }\n\n        project->getLabels(image_path, labels);\n\n        if(!export_unlabelled && labels.size() == 0){\n            if(!disable_progress){\n                progress.setValue(i);\n                progress.setLabelText(QString(\"%1 is unlabelled\").arg(image_path));\n                progress.repaint();\n                QApplication::processEvents();\n            }\n            qDebug() << image_path << \"is unlabelled\";\n\n            continue;\n        }else{\n            qDebug() << \"Processing:\" << image_path;\n        }\n\n        QString extension = QFileInfo(image_path).suffix();\n        QString filename_noext = QFileInfo(image_path).completeBaseName();\n        filename_noext = filename_noext.prepend(filename_prefix);\n\n        QString image_filename = QString(\"%1.%2\").arg(filename_noext).arg(extension);\n        QString label_filename = QString(\"%1.txt\").arg(filename_noext);\n\n        // Correct for duplicate file names in output\n        int dupe_file = 1;\n        while(QFile(image_filename).exists()){\n            image_filename = QString(\"%1%2.%3\")\n                                    .arg(filename_noext)\n                                    .arg(dupe_file)\n                                    .arg(extension);\n\n            label_filename = QString(\"%1.txt\").arg(filename_noext).arg(dupe_file);\n\n            dupe_file++;\n        }\n\n        image_filename = QString(\"%1/%2\").arg(folder).arg(image_filename);\n        label_filename = QString(\"%1/%2\").arg(folder).arg(label_filename);\n\n\n        auto db_dir = project->getDbFolder();\n        auto abs_path = QFileInfo(QDir::cleanPath(db_dir.filePath(image_path))).absoluteFilePath();\n\n        qDebug() << \"Copy:\" << abs_path << \" to: \" << image_filename;\n\n        auto copied = QFile::copy(abs_path, image_filename);\n        if(!copied){\n            qWarning() << \"Failed to copy image\" << image_filename;\n        }\n\n        cv::Mat image = cv::imread(abs_path.toStdString());\n        if(image.empty()){\n            qCritical() << \"Failed to load image!\" << abs_path;\n        }else{\n            writeLabels(image, label_filename, labels);\n        }\n\n\n        if(!disable_progress){\n            progress.setValue(i++);\n            progress.setLabelText(image_filename);\n        }\n\n    }\n\n    return true;\n}\n\nvoid DarknetExporter::process(){\n    processImages(train_folder, train_set, EXPORT_TRAIN);\n    processImages(val_folder, validation_set, EXPORT_VAL);\n}\n"
  },
  {
    "path": "src/darknetexporter.h",
    "content": "#ifndef DARKNETEXPORTER_H\n#define DARKNETEXPORTER_H\n\n#include <baseexporter.h>\n#include <algorithm>\n\nclass DarknetExporter : public BaseExporter\n{\n    Q_OBJECT\npublic:\n    explicit DarknetExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n\nsignals:\n    void export_progress(int);\n\npublic slots:\n    void generateLabelIds(const QString names_file);\n    void process();\n\nprotected:\n    void writeLabels(const cv::Mat &image, const QString file, const QList<BoundingBox> labels);\n    bool processImages(const QString folder, const QList<QString> images, export_image_type split_type=EXPORT_TRAIN);\n};\n\n#endif // DARKNETEXPORTER_H\n"
  },
  {
    "path": "src/darknetimporter.cpp",
    "content": "#include \"darknetimporter.h\"\n\nvoid DarknetImporter::import(QString image_list, QString names_file, QString root_folder)\n{\n    loadClasses(names_file);\n\n    // Get image filenames\n    auto filenames = readLines(image_list);\n    filenames.sort();\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, filenames.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setWindowTitle(\"Loading images and labels\");\n    int i = 0;\n\n    QList<QList<BoundingBox>> bboxes;\n\n    if (root_folder != \"\")\n        qInfo() << \"Using relative pathnames, relative to: \" << root_folder;\n\n    for(auto &image_path : filenames){\n\n        if(progress.wasCanceled())\n            break;\n\n        if (root_folder != \"\") {\n            auto abs_path = QDir(root_folder).absoluteFilePath(image_path);\n\n            if (!QFileInfo(abs_path).exists()) {\n                qWarning() << \"Image: \" << abs_path << \" doesn't exist. Check your root folder.\";\n                continue;\n            } else {\n                image_path = QFileInfo(abs_path).canonicalFilePath();\n            }\n        } else {\n            if (QFileInfo(image_path).isRelative()) {\n                qWarning() << \"Relative image path: \" << image_path\n                           << \" provided, but no root folder specified\";\n                continue;\n            }\n        }\n\n        bboxes.append(loadLabels(image_path));\n\n        progress.setValue(++i);\n        progress.setLabelText(QString(\"%1/%2: %3\").arg(i).arg(filenames.size()).arg(image_path));\n\n    }\n\n    if(progress.wasCanceled())\n        project->addLabelledAssets(filenames.mid(0, bboxes.size()), bboxes);\n    else\n        project->addLabelledAssets(filenames, bboxes);\n}\n\nQList<BoundingBox> DarknetImporter::loadLabels(QString image_path){\n\n    QList<BoundingBox> boxes = {};\n    auto info = QFileInfo(image_path);\n    QString filename_noext = QDir(info.absolutePath()).filePath(info.baseName());\n    QString label_filename = QString(\"%1.txt\").arg(filename_noext);\n\n    auto image = cv::imread(image_path.toStdString());\n    auto width = image.cols;\n    auto height = image.rows;\n\n    if(width <= 0 || height <= 0) return boxes;\n\n    auto lines = readLines(label_filename);\n    for(auto &line : lines){\n        BoundingBox bbox;\n        auto label = line.simplified().split(\" \");\n\n        if(label.size() != 5) continue;\n\n        bbox.classid = label.at(0).toInt() + 1; // Since in the database they're 1-indexed\n        bbox.classname = project->getClassName(bbox.classid);\n\n        if(bbox.classname == \"\"){\n            qWarning() << \"Class\" << bbox.classid << \" not found in names file.\";\n        }\n\n        int center_x = static_cast<int>(label.at(1).toFloat() * width);\n        int center_y = static_cast<int>(label.at(2).toFloat() * height);\n        int box_width = static_cast<int>(label.at(3).toFloat() * width);\n        int box_height = static_cast<int>(label.at(4).toFloat() * height);\n\n        auto top_left = QPoint(std::min(std::max(center_x - box_width/2, 0), width),\n                               std::min(std::max(center_y - box_height/2, 0), height));\n\n        auto bottom_right = QPoint(std::min(std::max(center_x + box_width/2, 0), width),\n                                   std::min(std::max(center_y + box_height/2, 0), height));\n\n        bbox.rect = QRect(top_left, bottom_right);\n\n        boxes.append(bbox);\n    }\n\n    return boxes;\n}\n\nvoid DarknetImporter::loadClasses(QString names_file){\n    QFile fh(names_file);\n\n    if (fh.open(QIODevice::ReadOnly)) {\n\n        while (!fh.atEnd()) {\n            // Darknet name file is just a newline delimited list of classes\n            QByteArray line = fh.readLine();\n\n            if(QString(line) == \"\") continue;\n\n            project->addClass(line.simplified());\n            qInfo() << line.simplified();\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/darknetimporter.h",
    "content": "#ifndef DARKNETIMPORTER_H\n#define DARKNETIMPORTER_H\n\n#include<baseimporter.h>\n\nclass DarknetImporter : public BaseImporter\n{\npublic:\n    using BaseImporter::import;\n\n    explicit DarknetImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){\n        this->project = project;\n    }\n    void import(QString image_list, QString names_file, QString root_folder = \"\");\n\nprivate:\n    void loadClasses(QString names_file);\n    QList<BoundingBox> loadLabels(QString image);\n};\n\n#endif // DARKNETIMPORTER_H\n"
  },
  {
    "path": "src/detection/detectoropencv.cpp",
    "content": "#include \"detectoropencv.h\"\n\nDetectorOpenCV::DetectorOpenCV()\n{\n\n}\n\nmodel_framework DetectorOpenCV::frameworkFromString(QString framework_string){\n    model_framework framework = FRAMEWORK_DARKNET;\n\n    if(framework_string.toLower().startsWith(\"darknet\")){\n        framework = FRAMEWORK_DARKNET;\n        qInfo() << \"Detector framework: Darknet\";\n    }else if(framework_string.toLower().startsWith(\"tensorflow\")){\n        framework = FRAMEWORK_TENSORFLOW;\n        qInfo() << \"Detector framework: Tensorflow\";\n    }\n\n    return framework;\n}\n\nint DetectorOpenCV::targetFromString(QString target_string){\n\n    int target = cv::dnn::DNN_TARGET_CPU;\n\n    if(target_string == \"CPU\"){\n        target = cv::dnn::DNN_TARGET_CPU;\n    }else if(target_string == \"OpenCL\"){\n        target = cv::dnn::DNN_TARGET_OPENCL;\n    }else if(target_string == \"OpenCL FP16\"){\n        target = cv::dnn::DNN_TARGET_OPENCL_FP16;\n    }else if(target_string == \"CUDA\"){\n        target = cv::dnn::DNN_TARGET_CUDA;\n    }else if(target_string == \"CUDA FP16\"){\n        target = cv::dnn::DNN_TARGET_CUDA_FP16;\n    }\n\n    return target;\n\n}\n\nvoid DetectorOpenCV::setTarget(QString target){\n    setTarget(targetFromString(target));\n}\n\nvoid DetectorOpenCV::setFramework(QString framework){\n    setFramework(frameworkFromString(framework));\n}\n\nvoid DetectorOpenCV::setImageSize(int width, int height){\n    if(width > 0 && height > 0){\n        input_width = width;\n        input_height = height;\n    }\n}\n\nvoid DetectorOpenCV::readNamesFile(std::string class_file){\n\n    class_names.clear();\n\n    // Load names of classes\n    std::ifstream ifs(class_file.c_str());\n    std::string line;\n    int i=0;\n    while (std::getline(ifs, line)){\n        QString cleaned = QString::fromStdString(line).simplified().toLower();\n        class_names.push_back(cleaned.toStdString());\n        std::cout << \"Added detection class: \" << i++ << \" \" << class_names.back() << std::endl;\n    }\n}\n\nvoid DetectorOpenCV::setChannels(int channels){\n    input_channels = channels;\n}\n\nvoid DetectorOpenCV::setTarget(int target){\n    preferable_target = target;\n\n    if(preferable_target == cv::dnn::DNN_TARGET_OPENCL){\n        // Check for GPU\n        cv::ocl::Context context;\n\n        if(!cv::ocl::haveOpenCL()){\n            std::cout << \"OpenCL is not available. Falling back to CPU\" << std::endl;\n            preferable_target = cv::dnn::DNN_TARGET_CPU;\n        }\n\n        // Attempt to use a GPU\n        if(context.create(cv::ocl::Device::TYPE_GPU)){\n            std::cout << \"Found OpenCL capable GPU - we're going to use it!\" << std::endl;\n            cv::ocl::Device(context.device(1));\n        }\n    }\n#ifdef WITH_CUDA\n    else if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){\n        try{\n            // Check for GPU\n            if(cv::cuda::getCudaEnabledDeviceCount() == 0){\n                qCritical() << \"No CUDA enabled devices found\";\n                preferable_target = cv::dnn::DNN_TARGET_CPU;\n                return;\n            }else{\n                cv::cuda::setDevice(0);\n            }\n\n            auto devinfo = cv::cuda::DeviceInfo();\n            if (!devinfo.isCompatible()){\n                qWarning() << \"Device is not CUDA compatible. Falling back to CPU\";\n                preferable_target = cv::dnn::DNN_TARGET_CPU;\n            }else{\n                qInfo() << \"NVIDIA GPU detected: \" << devinfo.name();\n            }\n        }catch(...){\n            qCritical() << \"Problem calling GPU functions, defaulting to CPU inference mode.\";\n            qCritical() << \"Check that OpenCV was compiled with CUDA or try rebooting.\";\n            preferable_target = cv::dnn::DNN_TARGET_CPU;\n        }\n    }\n#endif\n}\n\nvoid DetectorOpenCV::loadNetwork(std::string names_file, std::string cfg_file, std::string model_file){\n    // Load the names\n    readNamesFile(names_file);\n\n    // Infer network type automatically\n    net = cv::dnn::readNet(model_file, cfg_file);\n\n    // Should default to DNN_BACKEND_OPENCV (otherwise Intel inference engine)\n    if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){\n        net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);\n        qDebug() << \"Set preferable backend and target to CUDA\";\n    }else{\n        net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);\n    }\n\n    net.setPreferableTarget(preferable_target);\n\n    getOutputClassNames();\n}\n\nvoid DetectorOpenCV::getOutputClassNames()\n{\n    output_names.clear();\n\n    //Get the indices of the output layers, i.e. the layers with unconnected outputs\n    std::vector<int> outLayers = net.getUnconnectedOutLayers();\n\n    //get the names of all the layers in the network\n    std::vector<std::string> layersNames = net.getLayerNames();\n\n    // Get the names of the output layers in names\n    output_names.resize(outLayers.size());\n\n    for (size_t i = 0; i < outLayers.size(); ++i){\n        output_names.at(i) = layersNames.at(static_cast<size_t>(outLayers[i]) - 1);\n    }\n\n}\n\nvoid DetectorOpenCV::postProcessTensorflow(cv::Mat& frame, const std::vector<cv::Mat>& outputs, std::vector<BoundingBox> &filtered_boxes){\n    std::vector<int> classIds;\n    std::vector<float> confidences;\n    std::vector<cv::Rect> boxes;\n\n    auto detections = outputs.at(0);\n    const int numDetections = detections.size[2];\n\n    std::cout << \"Outputs_size: \" << detections.total() << std::endl;\n    std::cout << \"Number of detections: \" << numDetections << std::endl;\n\n    // batch id, class id, confidence, bbox (x4)\n    detections = detections.reshape(1, static_cast<int>(detections.total()) / 7);\n\n    // There are top-k (= 100 typical) detections, most of which should have\n    // more or less zero confidence.\n    for (int i = 0; i < numDetections; ++i)\n    {\n        float confidence = detections.at<float>(i, 2);\n\n        if (confidence > confThreshold)\n        {\n\n            // Extract the bounding box\n            int classId = static_cast<int>(detections.at<float>(i, 1));\n            int left = static_cast<int>(frame.cols * detections.at<float>(i, 3));\n            int top = static_cast<int>(frame.rows * detections.at<float>(i, 4));\n            int right = static_cast<int>(frame.cols * detections.at<float>(i, 5));\n            int bottom = static_cast<int>(frame.rows * detections.at<float>(i, 6));\n\n            BoundingBox bbox;\n            bbox.rect.setLeft(std::max(0, std::min(left, frame.cols - 1)));\n            bbox.rect.setTop(std::max(0, std::min(top, frame.rows - 1)));\n            bbox.rect.setRight(std::max(0, std::min(right, frame.cols - 1)));\n            bbox.rect.setBottom(std::max(0, std::min(bottom, frame.rows - 1)));\n            bbox.confidence = static_cast<double>(confidence);\n            bbox.classid = classId;\n            bbox.classname = QString::fromStdString(class_names.at(static_cast<size_t>(bbox.classid)));\n\n            std::cout << \"Found (\" << bbox.classid << \") \" << bbox.classname.toStdString()\n                      << \" at\" << \" (\" << bbox.rect.center().x() << \", \" << bbox.rect.center().y()\n                      << \"), conf: \" << bbox.confidence\n                      << \", size (\" << bbox.rect.width() << \"x\" << bbox.rect.height() << \")\"\n                      << std::endl;\n\n            filtered_boxes.push_back(bbox);\n        }else{\n            //\n        }\n    }\n}\n\nvoid DetectorOpenCV::postProcess(cv::Mat& frame, const std::vector<cv::Mat>& outputs, std::vector<BoundingBox> &filtered_boxes)\n{\n    std::vector<int> classIds;\n    std::vector<float> confidences;\n    std::vector<cv::Rect> boxes;\n\n    // Debug: this should be three because there are three scales that Yolo searches over\n    //std::cout << \"Outputs: \" << outputs.size() << std::endl;\n\n    for (size_t i = 0; i < outputs.size(); ++i)\n    {\n        // Scan through all the bounding boxes output from the network and keep only the\n        // ones with high confidence scores. Assign the box's class label as the class\n        // with the highest score for the box.\n        float* data = reinterpret_cast<float*>(outputs[i].data);\n        for (int j = 0; j < outputs[i].rows; ++j, data += outputs[i].cols)\n        {\n            cv::Mat scores = outputs[i].row(j).colRange(5, outputs[i].cols);\n            cv::Point classIdPoint;\n            double confidence;\n            // Get the value and location of the maximum score\n            minMaxLoc(scores, nullptr, &confidence, nullptr, &classIdPoint);\n\n            if (confidence > 0)\n            {\n\n                // Output is a percentage of the frame width/height\n                // so it doesn't matter that we're transforming boxes\n                // between the resized and full-size image.\n                int centerX = static_cast<int>(data[0] * frame.cols);\n                int centerY = static_cast<int>(data[1] * frame.rows);\n                int width = static_cast<int>(data[2] * frame.cols);\n                int height = static_cast<int>(data[3] * frame.rows);\n                int left = centerX - width / 2;\n                int top = centerY - height / 2;\n\n                classIds.push_back(classIdPoint.x);\n                confidences.push_back(static_cast<float>(confidence));\n                boxes.push_back(cv::Rect(left, top, width, height));\n\n            }else{\n                if(confidence == 0.0)\n                    continue;\n\n                std::cout << \"Detected \"\n                          << class_names.at(static_cast<size_t>(classIdPoint.x))\n                          << \" with low confidence: \" << confidence << std::endl;\n\n            }\n        }\n    }\n\n    std::vector<int> indices;\n\n    // Perform non maximum suppression to eliminate redundant overlapping boxes with\n    // lower confidences\n\n    // We set the confidence threshold to zero here, we'll filter the boxes out later.\n    // This lets us provide some feedback to the user if their threshold is too high.\n    cv::dnn::NMSBoxes(boxes, confidences, static_cast<float>(confThreshold), static_cast<float>(nmsThreshold), indices);\n\n    for (size_t i = 0; i < indices.size(); ++i)\n    {\n        auto idx = static_cast<size_t>(indices.at(i));\n\n        BoundingBox box;\n        cv::Rect rect = boxes.at(idx);\n\n        box.confidence = static_cast<double>(confidences.at(idx));\n        box.classid = classIds.at(idx);\n        box.classname = QString::fromStdString(class_names.at(static_cast<size_t>(box.classid)));\n\n        // Darknet predicts box centres and half-width/height, so the\n        // box can go outside the image.  Clamp to the image size:\n        QPoint top_left = {std::max(0, rect.x), std::max(0, rect.y)};\n        QPoint bottom_right = top_left + QPoint({rect.width, rect.height});\n\n        bottom_right.setX(std::min(bottom_right.x(), frame.cols));\n        bottom_right.setY(std::min(bottom_right.y(), frame.rows));\n\n        box.rect.setBottomRight(bottom_right);\n        box.rect.setTopLeft(top_left);\n\n        std::cout << \"Found \" << box.classname.toStdString()\n                  << \" at\" << \" (\" << box.rect.center().x() << \", \" << box.rect.center().y()\n                  << \"), conf: \" << box.confidence\n                  << \", size (\" << box.rect.width() << \"x\" << box.rect.height() << \")\"\n                  << std::endl;\n\n        filtered_boxes.push_back(box);\n\n    }\n\n    return;\n}\n\nstd::vector<BoundingBox> DetectorOpenCV::infer(cv::Mat image){\n\n    std::vector<BoundingBox> detections;\n\n    // Assume we have an alpha image if 4 channels\n    if(image.channels() == 4){\n        cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);\n    }\n\n    // 16-bit conversion, min-max scaling\n    if(convert_depth && image.elemSize() == 2){\n        double minval, maxval;\n        cv::minMaxIdx(image, &minval, &maxval);\n\n        double range = maxval-minval;\n        double scale_factor = 255.0/range;\n\n        image.convertTo(image, CV_32FC1);\n        image -= minval;\n        image *= scale_factor;\n        image.convertTo(image, CV_8UC1);\n    }\n\n    if(convert_grayscale && image.channels() == 1){\n        cv::cvtColor(image, image, cv::COLOR_GRAY2RGB);\n    }\n\n    if(image.channels() != input_channels){\n        std::cout << \"Input channel mismatch. Expecting \"\n                 << input_channels\n                 << \" channels but image has \" << image.channels()\n                 << \" channels.\";\n\n        return detections;\n    }\n\n    if(framework == FRAMEWORK_TENSORFLOW){\n        detections = inferTensorflow(image);\n    }else if(framework == FRAMEWORK_DARKNET){\n        detections =  inferDarknet(image);\n    }\n\n    return detections;\n\n}\n\nstd::vector<BoundingBox> DetectorOpenCV::inferTensorflow(cv::Mat image){\n\n        std::vector<BoundingBox> results;\n\n        auto mean = cv::Scalar(0,0,0);\n        if(image.channels() == 1){\n            mean = cv::Scalar(0);\n        }\n\n        // Check for 16-bit\n        double scale_factor = 1/255.0;\n\n        if(image.elemSize() == 2){\n            scale_factor = 1/65535.0;\n        }\n\n        auto input_size = cv::Size(image.cols, image.rows);\n\n        bool swap_rb = false; // BGR->RGB?\n        bool crop = false; // Use the entire image\n\n        // No normalising! The model will handle it.\n        auto blob = cv::dnn::blobFromImage(image, 1.0, input_size, mean, swap_rb, crop);\n\n        //Sets the input to the network\n        net.setInput(blob);\n\n        // Runs the forward pass to get output of the output layers\n        std::vector<cv::Mat> outputs;\n        net.forward(outputs, output_names);\n\n        postProcessTensorflow(image, outputs, results);\n\n        std::vector<double> layersTimes;\n        double freq = cv::getTickFrequency() / 1000;\n        processing_time = net.getPerfProfile(layersTimes) / freq;\n\n        std::cout << \"Processed in: \" << processing_time << std::endl;\n\n        return results;\n}\n\nvoid DetectorOpenCV::setNormalisation(double scale_factor, cv::Scalar mean){\n    this->scale_factor = scale_factor;\n    this->mean = mean;\n}\n\nstd::vector<BoundingBox> DetectorOpenCV::inferDarknet(cv::Mat image){\n\n        std::vector<BoundingBox> results;\n\n        auto mean = cv::Scalar(0,0,0);\n        if(image.channels() == 1){\n            mean = cv::Scalar(0);\n        }\n\n        // Normalisation, check for 16-bit\n        // TODO: have this as a user option\n        if(image.elemSize() == 2){\n            scale_factor = 1/65535.0;\n        }else{\n            scale_factor= 1/255.0;\n        }\n\n        auto input_size = cv::Size(input_width, input_height);\n\n        bool swap_rb = true; // BGR->RGB?\n        bool crop = false; // Use the entire image\n        auto blob = cv::dnn::blobFromImage(image, scale_factor, input_size, mean, swap_rb, crop);\n\n        //Sets the input to the network\n        net.setInput(blob);\n\n        // Runs the forward pass to get output of the output layers\n        std::vector<cv::Mat> outputs;\n        net.forward(outputs, output_names);\n\n        // Put efficiency information. The function getPerfProfile returns the\n        // overall time for inference(t) and the timings for each of the layers(in layersTimes)\n        std::vector<double> layersTimes;\n        double freq = cv::getTickFrequency() / 1000;\n        processing_time = net.getPerfProfile(layersTimes) / freq;\n\n        std::cout << \"Processed in: \" << processing_time << std::endl;\n\n        // Remove the bounding boxes with low confidence\n        postProcess(image, outputs, results);\n\n        return results;\n}\n\nvoid DetectorOpenCV::annotateImage(cv::Mat &frame, std::vector<BoundingBox> boxes, cv::Scalar colour, cv::Scalar font_colour){\n    for(auto &box : boxes){\n        //Draw a rectangle displaying the bounding box\n\n        auto top_left = cv::Point(box.rect.left(), box.rect.top());\n\n        cv::rectangle(frame, top_left,\n                             cv::Point(box.rect.right(), box.rect.bottom()),\n                             colour);\n\n        //Get the label for the class name and its confidence\n        std::string label = cv::format(\"%.2f\", box.confidence);\n\n        //Display the label at the top of the bounding box\n        int baseLine;\n        cv::Size labelSize = getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);\n        cv::putText(frame, label, top_left, cv::FONT_HERSHEY_SIMPLEX, 0.5, font_colour);\n    }\n}\n\nvoid DetectorOpenCV::runOnProject(LabelProject *project){\n    QList<QString> images;\n    project->getImageList(images);\n\n    for(auto& image_path : images){\n        auto image = cv::imread(image_path.toStdString(), cv::IMREAD_UNCHANGED|cv::IMREAD_ANYDEPTH);\n\n        // Assume we have an alpha image if 4 channels\n        if(image.channels() == 4){\n            cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);\n        }\n\n        auto boxes = infer(image);\n\n        QList<BoundingBox> existing_boxes;\n        project->getLabels(image_path, existing_boxes);\n\n        for(auto &box : boxes){\n            if(!project->classInDB(box.classname)){\n                project->addClass(box.classname);\n            }\n\n            // Strip out boxes which are already in the image\n            // assume detector is deterministic\n            bool exists = false;\n            for(auto &existing : existing_boxes){\n                if(existing.rect == box.rect && existing.classname == box.classname){\n                    exists = true;\n                }\n            }\n\n            if(!exists){\n                qDebug() << \"Adding label\";\n                project->addLabel(image_path, box);\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/detection/detectoropencv.h",
    "content": "#ifndef DETECTOROPENCV_H\n#define DETECTOROPENCV_H\n\n#include<iostream>\n#include<fstream>\n#include<string>\n#include<vector>\n\n#include<opencv2/opencv.hpp>\n#include<opencv2/core/ocl.hpp>\n#ifdef WITH_CUDA\n#include <opencv2/core/cuda.hpp>\n#endif\n#include<opencv2/dnn.hpp>\n\n#include<boundingbox.h>\n#include<imagedisplay.h>\n#include<labelproject.h>\n\ntypedef enum {\n    FRAMEWORK_TENSORFLOW,\n    FRAMEWORK_DARKNET\n} model_framework;\n\nclass DetectorOpenCV\n{\n\npublic:\n    DetectorOpenCV();\n\n    void setImageSize(int width, int height);\n\n    void loadNetwork(std::string names_file, std::string cfg_file, std::string model_file);\n\n    void annotateImage(cv::Mat &image,\n                       std::vector<BoundingBox> boxes,\n                       cv::Scalar colour = cv::Scalar(0,0,255),\n                       cv::Scalar font_colour = cv::Scalar(255,255,255));\n\n    std::vector<BoundingBox> infer(cv::Mat image);\n    std::vector<BoundingBox> inferDarknet(cv::Mat image);\n    std::vector<BoundingBox> inferTensorflow(cv::Mat image);\n\n    void setFramework(model_framework framework){this->framework = framework;}\n    void setFramework(QString framework);\n    void setConfidenceThreshold(double thresh){confThreshold = std::max(0.0, thresh);}\n    void setNMSThreshold(double thresh){nmsThreshold = std::max(0.0, thresh);}\n    void setConvertGrayscale(bool convert){convert_grayscale = convert;}\n    void setConvertDepth(bool convert){convert_depth = convert;}\n    double getConfidenceThreshold(void){ return confThreshold;}\n    double getNMSThreshold(void){ return nmsThreshold;}\n    void setTarget(int target);\n    void setTarget(QString target);\n    void setChannels(int channels);\n    void setNormalisation(double scale_factor, cv::Scalar mean);\n    int getChannels(void){return input_channels;}\n\n    static model_framework frameworkFromString(QString framework_string);\n    static int targetFromString(QString target_string);\n\n    void runOnProject(LabelProject *project);\n\nprivate:\n\n    void postProcess(cv::Mat& frame, const std::vector<cv::Mat>& outs, std::vector<BoundingBox> &filtered_outputs);\n    void readNamesFile(std::string class_file = \"coco.names\");\n    void getOutputClassNames(void);\n\n    bool convert_grayscale = true;\n    bool convert_depth = true;\n    double processing_time;\n    double confThreshold = 0.5; // Confidence threshold\n    double nmsThreshold = 0.4;  // Non-maximum suppression threshold\n    int input_width = 416;        // Width of network's input image\n    int input_height = 416;       // Height of network's input image\n    int input_channels = 3;\n    int preferable_target = cv::dnn::DNN_TARGET_OPENCL;\n    model_framework framework = FRAMEWORK_DARKNET;\n    cv::Scalar mean = {0,0,0};\n    double scale_factor = 1/255.;\n\n    std::vector<std::string> class_names;\n    std::vector<std::string> output_names;\n    cv::dnn::Net net;\n    void postProcessTensorflow(cv::Mat &frame, const std::vector<cv::Mat> &outputs, std::vector<BoundingBox> &filtered_boxes);\n};\n\n#endif // DETECTOROPENCV_H\n"
  },
  {
    "path": "src/detection/detectorsetupdialog.cpp",
    "content": "#include \"detectorsetupdialog.h\"\n#include \"ui_detectorsetupdialog.h\"\n\nDetectorSetupDialog::DetectorSetupDialog(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::DetectorSetupDialog)\n{\n    ui->setupUi(this);\n\n    connect(ui->cfgPathButton, SIGNAL(clicked()), this, SLOT(setCfgFile()));\n    connect(ui->cfgPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));\n    connect(ui->weightPathButton, SIGNAL(clicked(bool)), this, SLOT(setWeightsFile()));\n    connect(ui->weightPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));\n    connect(ui->namesPathButton, SIGNAL(clicked(bool)), this, SLOT(setNamesFile()));\n    connect(ui->frameworkComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setFramework()));\n    connect(ui->targetComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setTarget()));\n    connect(ui->namesPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));\n    connect(ui->convertGrayscaleCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertGrayscale()));\n    connect(ui->convertDepthCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertDepth()));\n\n    settings = new QSettings(\"DeepLabel\", \"DeepLabel\");\n\n    cfg_file = settings->value(\"model_cfg\", \"\").toString();\n    weight_file = settings->value(\"model_weights\", \"\").toString();\n    names_file = settings->value(\"model_names\", \"\").toString();\n\n    convert_grayscale = settings->value(\"model_convert_grayscale\", true).toBool();\n    ui->convertGrayscaleCheckbox->setChecked(convert_grayscale);\n\n    convert_depth = settings->value(\"model_convert_depth\", true).toBool();\n    ui->convertDepthCheckbox->setChecked(convert_depth);\n\n    image_width = settings->value(\"model_width\", 0).toInt();\n    image_height = settings->value(\"model_height\", 0).toInt();\n    image_channels = settings->value(\"model_channels\", 3).toInt();\n\n    framework = static_cast<model_framework>(settings->value(\"model_framework\", 0).toInt());\n    if(framework == FRAMEWORK_DARKNET){\n        ui->frameworkComboBox->setCurrentText(\"Darknet (YOLO)\");\n        ui->imageHeightLabel->show();\n        ui->imageWidthLabel->show();\n    }else if(framework == FRAMEWORK_TENSORFLOW){\n        ui->frameworkComboBox->setCurrentText(\"Tensorflow\");\n        ui->imageHeightLabel->hide();\n        ui->imageWidthLabel->hide();\n    }\n\n    target = settings->value(\"model_target\", cv::dnn::DNN_TARGET_CPU).toInt();\n    if(target == cv::dnn::DNN_TARGET_CPU){\n        ui->targetComboBox->setCurrentText(\"CPU\");\n    }else if(target == cv::dnn::DNN_TARGET_OPENCL){\n        ui->targetComboBox->setCurrentText(\"OpenCL\");\n    }else if(target == cv::dnn::DNN_TARGET_OPENCL_FP16){\n        ui->targetComboBox->setCurrentText(\"OpenCL FP16\");\n    }\n#ifdef WITH_CUDA\n    else if(target == cv::dnn::DNN_TARGET_CUDA){\n        ui->targetComboBox->setCurrentText(\"CUDA\");\n    }else if(target == cv::dnn::DNN_TARGET_CUDA_FP16){\n        ui->targetComboBox->setCurrentText(\"CUDA FP16\");\n    }\n#endif\n    updateFields();\n    checkForm();\n\n}\n\nvoid DetectorSetupDialog::updateFields(){\n    ui->cfgPathLineEdit->setText(cfg_file);\n    ui->weightPathLineEdit->setText(weight_file);\n    ui->namesPathLineEdit->setText(names_file);\n\n    ui->imageWidthLabel->setText(QString::number(image_width));\n    ui->imageHeightLabel->setText(QString::number(image_height));\n    ui->imageChannelsLabel->setText(QString::number(image_channels));\n}\n\nvoid DetectorSetupDialog::setConvertGrayscale(void){\n    convert_grayscale = ui->convertGrayscaleCheckbox->isChecked();\n    settings->setValue(\"model_convert_grayscale\", convert_grayscale);\n}\n\nbool DetectorSetupDialog::getConvertGrayscale(void){\n    return convert_grayscale;\n}\n\nvoid DetectorSetupDialog::setConvertDepth(void){\n    convert_depth = ui->convertDepthCheckbox->isChecked();\n    settings->setValue(\"model_convert_depth\", convert_depth);\n}\n\nbool DetectorSetupDialog::getConvertDepth(void){\n    return convert_depth;\n}\n\nvoid DetectorSetupDialog::checkForm(void){\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);\n\n    cfg_file = ui->cfgPathLineEdit->text();\n    weight_file = ui->weightPathLineEdit->text();\n    names_file = ui->namesPathLineEdit->text();\n\n    if(cfg_file == \"\") return;\n    if(weight_file == \"\") return;\n    if(names_file == \"\") return;\n\n    if(!QFile(cfg_file).exists()){\n        qDebug() << \"Config file doesn't exist\";\n        return;\n    }else if(!getParamsFromConfig()){\n        return;\n    }\n\n    if(!QFile(weight_file).exists()){\n        qDebug() << \"Weight file doesn't exist\";\n        return;\n    }\n\n    if(!QFile(names_file).exists()){\n        qDebug() << \"Names file doesn't exist\";\n        return;\n    }\n\n    // At this point, all good.\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n    settings->setValue(\"model_width\", image_width);\n    settings->setValue(\"model_height\", image_height);\n    settings->setValue(\"modelchannels\", image_channels);\n\n    settings->setValue(\"model_cfg\", cfg_file);\n    settings->setValue(\"model_weights\", weight_file);\n    settings->setValue(\"model_names\", names_file);\n\n}\n\nbool DetectorSetupDialog::getParamsFromConfig(void){\n\n    qDebug() << \"Checking config file\";\n\n    if(framework == FRAMEWORK_DARKNET){\n        QSettings darknet_settings(cfg_file, QSettings::IniFormat);\n\n        darknet_settings.beginGroup(\"net\");\n\n        auto keys = darknet_settings.childKeys();\n\n        if(!darknet_settings.contains(\"width\")){\n            qDebug() << \"No width parameter\";\n            return false;\n        }\n        if(!darknet_settings.contains(\"height\")){\n            qDebug() << \"No height parameter\";\n            return false;\n        }\n        if(!darknet_settings.contains(\"channels\")){\n            qDebug() << \"No channels parameter\";\n            return false;\n        }\n\n        auto width = darknet_settings.value(\"width\").toInt();\n        auto height = darknet_settings.value(\"height\").toInt();\n        auto channels = darknet_settings.value(\"channels\").toInt();\n\n        darknet_settings.endGroup();\n\n        qDebug() << width << height << channels;\n\n        if(width > 0 && height > 0 && channels > 0){\n\n            qDebug() << width << height << channels;\n\n            image_width = width;\n            image_height = height;\n            image_channels = channels;\n\n        }else{\n            return false;\n        }\n\n    }else if(framework == FRAMEWORK_TENSORFLOW){\n        // In theory we can parse the .pbtxt file to figure out\n        // the input layer parameters, but that either means bringing in\n        // protobuf or loading the entire network via OpenCV.\n\n    }\n\n    updateFields();\n\n    return true;\n\n}\n\nQString DetectorSetupDialog::openFile(QString title, QString search_path, QString filter){\n    QString openDir;\n\n    if(search_path == \"\"){\n         openDir = settings->value(\"project_folder\").toString();\n    }else{\n         openDir = QFileInfo(search_path).absoluteDir().absolutePath();\n    }\n\n    auto path = QFileDialog::getOpenFileName(this, title,\n                                             openDir,\n                                             QString(\"All files (*.*);;%1\").arg(filter),\n                                             &filter);\n\n    return path;\n}\n\nvoid DetectorSetupDialog::setCfgFile(void){\n\n    QString filter, title;\n\n    if(framework == FRAMEWORK_DARKNET){\n        filter = tr(\"Config (*.cfg)\");\n        title = tr(\"Select darknet config file\");\n    }else if(framework == FRAMEWORK_TENSORFLOW){\n        filter = tr(\"Config (*.pbtxt)\");\n        title = tr(\"Select tensorflow config file\");\n    }else{\n        return;\n    }\n\n    auto path = openFile(title, cfg_file, filter);\n\n    if(path != \"\"){\n        ui->cfgPathLineEdit->setText(path);\n    }\n\n    checkForm();\n}\n\n\nvoid DetectorSetupDialog::setWeightsFile(void){\n\n    QString filter, title;\n\n    if(framework == FRAMEWORK_DARKNET){\n        filter = tr(\"Weights (*.weights)\");\n        title = tr(\"Select darknet weights file\");\n    }else if(framework == FRAMEWORK_TENSORFLOW){\n        filter = tr(\"Config (*.pb)\");\n        title = tr(\"Select tensorflow frozen graph\");\n    }else{\n        return;\n    }\n\n    auto path = openFile(title, weight_file, filter);\n\n    if(path != \"\"){\n        ui->weightPathLineEdit->setText(path);\n    }\n\n    checkForm();\n}\n\nvoid DetectorSetupDialog::setFramework(void){\n\n    qDebug() << \"Attempting to set framework to \" << ui->frameworkComboBox->currentText();\n    auto framework = DetectorOpenCV::frameworkFromString(ui->frameworkComboBox->currentText());\n    settings->setValue(\"model_framework\", framework);\n\n}\n\nvoid DetectorSetupDialog::setTarget(void){\n\n    qInfo() << \"Attempting to set target to \" << ui->targetComboBox->currentText();\n    auto target = DetectorOpenCV::targetFromString(ui->targetComboBox->currentText());\n    settings->setValue(\"model_target\", target);\n\n\n\n}\n\nint DetectorSetupDialog::getTarget(void){\n    return target;\n}\n\nvoid DetectorSetupDialog::setNamesFile(void){\n\n    QString filter = tr(\"Names (*.names)\");\n    QString title = tr(\"Select darknet names file\");\n\n    auto path = openFile(title, names_file, filter);\n\n    if(path != \"\"){\n        ui->namesPathLineEdit->setText(path);\n    }\n\n    checkForm();\n}\n\nDetectorSetupDialog::~DetectorSetupDialog()\n{\n    delete ui;\n}\n"
  },
  {
    "path": "src/detection/detectorsetupdialog.h",
    "content": "#ifndef DETECTORSETUPDIALOG_H\n#define DETECTORSETUPDIALOG_H\n\n#include <QDialog>\n#include <QFileDialog>\n#include <QSettings>\n#include <QPushButton>\n#include <QtDebug>\n#include <QDir>\n\n#include <opencv2/dnn.hpp>\n#include <detection/detectoropencv.h>\n\nnamespace Ui {\nclass DetectorSetupDialog;\n}\n\nclass DetectorSetupDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit DetectorSetupDialog(QWidget *parent = nullptr);\n    int getWidth(void){return image_width;}\n    int getHeight(void){return image_height;}\n    int getChannels(void){return image_channels;}\n    QString getNames(void){return names_file;}\n    QString getWeights(void){return weight_file;}\n    QString getCfg(void){return cfg_file;}\n    bool getConvertGrayscale(void);\n    bool getConvertDepth(void);\n    model_framework getFramework(void){return framework;}\n    int getTarget();\n    ~DetectorSetupDialog();\n\nprivate slots:\n\n    void setCfgFile();\n    void setNamesFile();\n    void setWeightsFile();\n    void setFramework();\n    void setTarget();\n    void setConvertGrayscale(void);\n    void setConvertDepth(void);\n\n    void checkForm();\n\n\nprivate:\n    Ui::DetectorSetupDialog *ui;\n    bool getParamsFromConfig();\n    void updateFields();\n\n\n    QSettings* settings;\n\n    QString cfg_file;\n    QString weight_file;\n    QString names_file;\n    int image_width = 320;\n    int image_height = 240;\n    int image_channels = 3; // default\n    int target = 0;\n    bool convert_grayscale = true;\n    bool convert_depth = true;\n    model_framework framework = FRAMEWORK_TENSORFLOW;\n    QString openFile(QString title, QString search_path=\"\", QString filter=\"\");\n};\n\n#endif // DETECTORSETUPDIALOG_H\n"
  },
  {
    "path": "src/detection/detectorsetupdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DetectorSetupDialog</class>\n <widget class=\"QDialog\" name=\"DetectorSetupDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>500</width>\n    <height>314</height>\n   </rect>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>500</width>\n    <height>0</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Configure object detector</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Weight file</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"frameworkComboBox\">\n       <item>\n        <property name=\"text\">\n         <string>Darknet (YOLO)</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Tensorflow</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"targetComboBox\">\n       <item>\n        <property name=\"text\">\n         <string>CPU</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>CUDA</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>CUDA FP16</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>OpenCL</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>OpenCL FP16</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"3\">\n      <widget class=\"QToolButton\" name=\"namesPathButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Config file</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"weightPathLineEdit\"/>\n     </item>\n     <item row=\"9\" column=\"1\">\n      <widget class=\"QLabel\" name=\"imageHeightLabel\">\n       <property name=\"text\">\n        <string>-</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"10\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_6\">\n       <property name=\"text\">\n        <string>Channels</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"0\" colspan=\"2\">\n      <widget class=\"QCheckBox\" name=\"convertGrayscaleCheckbox\">\n       <property name=\"text\">\n        <string>Convert grayscale images to RGB</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_8\">\n       <property name=\"text\">\n        <string>Type</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"9\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>Image Height</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"10\" column=\"1\">\n      <widget class=\"QLabel\" name=\"imageChannelsLabel\">\n       <property name=\"text\">\n        <string>-</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"3\">\n      <widget class=\"QToolButton\" name=\"cfgPathButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"7\" column=\"0\" colspan=\"4\">\n      <widget class=\"QLabel\" name=\"label_5\">\n       <property name=\"text\">\n        <string>Loaded Network Parameters:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"8\" column=\"1\">\n      <widget class=\"QLabel\" name=\"imageWidthLabel\">\n       <property name=\"text\">\n        <string>-</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"3\">\n      <widget class=\"QToolButton\" name=\"weightPathButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"cfgPathLineEdit\"/>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_10\">\n       <property name=\"text\">\n        <string>Names file</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"namesPathLineEdit\"/>\n     </item>\n     <item row=\"8\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>Image Width</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_7\">\n       <property name=\"text\">\n        <string>Target</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"0\" colspan=\"2\">\n      <widget class=\"QCheckBox\" name=\"convertDepthCheckbox\">\n       <property name=\"text\">\n        <string>Convert 16-bit images to 8-bit</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>cfgPathLineEdit</tabstop>\n  <tabstop>cfgPathButton</tabstop>\n  <tabstop>weightPathLineEdit</tabstop>\n  <tabstop>weightPathButton</tabstop>\n  <tabstop>namesPathLineEdit</tabstop>\n  <tabstop>namesPathButton</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>DetectorSetupDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>DetectorSetupDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/exportdialog.cpp",
    "content": "#include \"exportdialog.h\"\n#include \"ui_exportdialog.h\"\n\nExportDialog::ExportDialog(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::ExportDialog)\n{\n    ui->setupUi(this);\n    toggleExporter();\n    do_shuffle = ui->randomSplitCheckbox->isChecked();\n    validation_split_pc = ui->validationSplitSpinbox->value();\n\n    connect(ui->exportSelectComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(toggleExporter()));\n    connect(ui->randomSplitCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleShuffle(bool)));\n    connect(ui->validationSplitSpinbox, SIGNAL(valueChanged(int)), this, SLOT(setValidationSplit(int)));\n    connect(ui->exportLabelledCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleExportUnlabelled(bool)));\n    connect(ui->trainValCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleValidationSplit(bool)));\n    connect(ui->gcpBucketLineEdit, SIGNAL(textEdited(QString)), SLOT(setBucketUri(QString)));\n\n    connect(ui->filePrefixLineEdit, SIGNAL(textEdited(QString)), SLOT(setFilePrefix(QString)));\n    connect(ui->namesFileLineEdit, SIGNAL(textEdited(QString)), SLOT(setNamesFile(QString)));\n    connect(ui->namesFilePushButton, SIGNAL(clicked()), this, SLOT(setNamesFile()));\n    connect(ui->outputFolderLineEdit, SIGNAL(textEdited(QString)), SLOT(setOutputFolder(QString)));\n    connect(ui->outputFolderPushButton, SIGNAL(clicked()), this, SLOT(setOutputFolder()));\n\n    settings = new QSettings(\"DeepLabel\", \"DeepLabel\");\n\n    ui->validationSplitSpinbox->setValue(settings->value(\"validation_split_pc\", 80).toInt());\n    toggleShuffle(settings->value(\"do_shuffle\", false).toBool());\n\n    toggleExportUnlabelled(settings->value(\"export_unlabelled\", false).toBool());\n    ui->exportLabelledCheckbox->setChecked(export_unlabelled);\n\n    toggleValidationSplit(settings->value(\"validation_split_enable\", false).toBool());\n    ui->trainValCheckbox->setChecked(validation_split_enable);\n\n    toggleAppendLabels(settings->value(\"append_labels\", false).toBool());\n    ui->trainValCheckbox->setChecked(append_labels);\n\n    if(settings->contains(\"output_folder\")){\n        auto path = settings->value(\"output_folder\").toString();\n        if(path != \"\"){\n            setOutputFolder(path);\n        }\n    }\n\n    if(settings->contains(\"names_file\")){\n        auto path = settings->value(\"names_file\").toString();\n        if(path != \"\"){\n            setNamesFile(path);\n         }\n    }\n\n    if(settings->contains(\"file_prefix\")){\n        setFilePrefix(settings->value(\"file_prefix\").toString());\n    }\n}\n\nExportDialog::~ExportDialog()\n{\n    delete settings;\n    delete ui;\n}\n\nbool ExportDialog::getExportUnlablled(void){\n    return export_unlabelled;\n}\n\nbool ExportDialog::getValidationSplitEnabled(void){\n    return validation_split_enable;\n}\n\n\nQString ExportDialog::getFilePrefix(void){\n    return file_prefix;\n}\n\n\nvoid ExportDialog::toggleExportUnlabelled(bool res){\n    export_unlabelled = res;\n    settings->setValue(\"export_unlabelled\", export_unlabelled);\n}\n\nvoid ExportDialog::setValidationSplit(int value){\n    if (value < 0 || value > 100) return;\n    validation_split_pc = value;\n    settings->setValue(\"validation_split_pc\", validation_split_pc);\n}\n\nvoid ExportDialog::toggleValidationSplit(bool enable){\n    validation_split_enable = enable;\n    settings->setValue(\"validation_split_enable\", enable);\n}\n\nvoid ExportDialog::toggleAppendLabels(bool enable){\n    append_labels = enable;\n    settings->setValue(\"append_labels\", enable);\n}\n\nvoid ExportDialog::toggleShuffle(bool shuffle){\n    do_shuffle = shuffle;\n    settings->setValue(\"do_shuffle\", do_shuffle);\n    ui->randomSplitCheckbox->setChecked(do_shuffle);\n}\n\nvoid ExportDialog::setFilePrefix(QString prefix){\n    if(prefix != \"\"){\n        file_prefix = prefix;\n    }\n    settings->setValue(\"validation_split_pc\", validation_split_pc);\n}\n\nvoid ExportDialog::setOutputFolder(QString path){\n\n    if(path == \"\"){\n        QString openDir;\n        if(output_folder == \"\"){\n             openDir = QDir::homePath();\n        }else{\n             openDir = output_folder;\n        }\n\n        path = QFileDialog::getExistingDirectory(this, tr(\"Select output folder\"),\n                                                        openDir);\n    }\n\n    if(path != \"\"){\n        output_folder = path;\n        ui->outputFolderLineEdit->setText(output_folder);\n        settings->setValue(\"output_folder\", output_folder);\n    }\n\n    checkOK();\n}\n\nvoid ExportDialog::setBucketUri(QString uri){\n    if(uri != \"\"){\n        bucket_uri = uri;\n    }\n}\n\nvoid ExportDialog::setNamesFile(QString path){\n\n    if(path == \"\"){\n        QString openDir;\n\n        if(names_file == \"\"){\n             openDir = QDir::homePath();\n        }else{\n             openDir = QFileInfo(names_file).absoluteDir().absolutePath();\n        }\n\n        path = QFileDialog::getOpenFileName(this, tr(\"Select darknet names file\"),\n                                                        openDir);\n    }\n\n    if(path != \"\"){\n        names_file = path;\n        ui->namesFileLineEdit->setText(names_file);\n        settings->setValue(\"names_file\", names_file);\n    }\n\n    checkOK();\n}\n\nbool ExportDialog::checkOK(){\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);\n\n    // If output folder exists\n    if(!QDir(output_folder).exists() || output_folder == \"\"){\n        qCritical() << \"Export output folder doesn't exist\";\n        return false;\n    }\n\n    if(ui->exportSelectComboBox->currentText() == \"GCP AutoML\"){\n        if(bucket_uri == \"\") return false;\n    }\n\n    if(ui->exportSelectComboBox->currentText() == \"Darknet\"){\n        // If we're using darknet, check the names\n        // file exists and contains something\n        if(!QFile::exists(names_file)){\n            qCritical() << \"Names file doesn't exist\";\n            return false;\n        }\n\n        QStringList class_list;\n        QFile fh(names_file);\n\n        if (fh.open(QIODevice::ReadOnly)) {\n\n            while (!fh.atEnd()) {\n                // Darknet name file is just a newline delimited list of classes\n                QByteArray line = fh.readLine();\n                class_list.append(line);\n            }\n        }\n\n        if(class_list.size() == 0){\n            qCritical() << \"No classes?\";\n            return false;\n        }\n    }\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n    return true;\n}\n\nbool ExportDialog::getCreateLabelMap(void){\n    return ui->labelMapCheckbox->isChecked();\n}\n\nvoid ExportDialog::toggleExporter(){\n\n    current_exporter = ui->exportSelectComboBox->currentText();\n\n    ui->namesFileLineEdit->setEnabled(current_exporter == \"Darknet\");\n    ui->namesFilePushButton->setEnabled(current_exporter == \"Darknet\");\n    ui->labelMapCheckbox->setEnabled(current_exporter == \"Pascal VOC\");\n    ui->gcpBucketLineEdit->setEnabled(current_exporter == \"GCP AutoML\");\n\n    checkOK();\n}\n"
  },
  {
    "path": "src/exportdialog.h",
    "content": "#ifndef EXPORTDIALOG_H\n#define EXPORTDIALOG_H\n\n#include <QDialog>\n#include <QFile>\n#include <QDir>\n#include <QFileDialog>\n#include <QSettings>\n#include <QtDebug>\n\nnamespace Ui {\nclass ExportDialog;\n}\n\nclass ExportDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ExportDialog(QWidget *parent = nullptr);\n    ~ExportDialog();\n\n    QString getExporter(){return current_exporter; }\n    QString getOutputFolder(){return output_folder; }\n    QString getNamesFile(){return names_file; }\n    int getShuffle(){return do_shuffle; }\n    double getValidationSplit(){\n        return static_cast<double>(validation_split_pc)/100.0;\n    }\n    bool getAppendLabels(){return append_labels;}\n\n    QString getBucket(){ return bucket_uri; }\n\n    bool getCreateLabelMap();\n    bool getExportUnlablled();\n    bool getValidationSplitEnabled();\n    QString getFilePrefix();\n\nprivate slots:\n    void setNamesFile(QString path=\"\");\n    void setOutputFolder(QString path=\"\");\n    void setValidationSplit(int value);\n    void toggleShuffle(bool shuffle);\n    void toggleAppendLabels(bool enable);\n    void toggleValidationSplit(bool enable);\n    void toggleExportUnlabelled(bool res);\n    void toggleExporter();\n    void setBucketUri(QString uri);\n    void setFilePrefix(QString prefix);\n\nprivate:\n    Ui::ExportDialog *ui;\n    bool checkOK();\n\n    QSettings *settings;\n\n    QString output_folder = \"\";\n    QString names_file = \"\";\n    QString bucket_uri = \"\";\n    QString file_prefix = \"\";\n    QString current_exporter = \"Darknet\";\n    bool do_shuffle = false;\n    bool append_labels = false;\n    bool validation_split_enable = false;\n    int validation_split_pc = 80;\n    bool export_unlabelled;\n\n};\n\n#endif // EXPORTDIALOG_H\n"
  },
  {
    "path": "src/exportdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ExportDialog</class>\n <widget class=\"QDialog\" name=\"ExportDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>338</width>\n    <height>410</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <item>\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>Names file</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\" colspan=\"2\">\n      <widget class=\"QComboBox\" name=\"exportSelectComboBox\">\n       <item>\n        <property name=\"text\">\n         <string>COCO (JSON)</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Darknet</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>GCP AutoML</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Kitti</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Pascal VOC</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>Validation Split</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_6\">\n       <property name=\"text\">\n        <string>File prefix</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Output Type: </string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"outputFolderLineEdit\">\n       <property name=\"text\">\n        <string/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"7\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_5\">\n       <property name=\"text\">\n        <string>Bucket URI</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"1\">\n      <widget class=\"QSpinBox\" name=\"validationSplitSpinbox\">\n       <property name=\"suffix\">\n        <string>%</string>\n       </property>\n       <property name=\"maximum\">\n        <number>100</number>\n       </property>\n       <property name=\"value\">\n        <number>80</number>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"0\" colspan=\"3\">\n      <widget class=\"QCheckBox\" name=\"trainValCheckbox\">\n       <property name=\"text\">\n        <string>Train/Val Split</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"filePrefixLineEdit\"/>\n     </item>\n     <item row=\"3\" column=\"2\">\n      <widget class=\"QPushButton\" name=\"namesFilePushButton\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>20</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>20</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"0\" colspan=\"3\">\n      <widget class=\"QCheckBox\" name=\"randomSplitCheckbox\">\n       <property name=\"text\">\n        <string>Randomise Split</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Output Folder</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"namesFileLineEdit\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"2\">\n      <widget class=\"QPushButton\" name=\"outputFolderPushButton\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>20</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>20</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"7\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"gcpBucketLineEdit\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string>gs://your/bucket/path</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"9\" column=\"0\" colspan=\"3\">\n      <widget class=\"QCheckBox\" name=\"exportLabelledCheckbox\">\n       <property name=\"text\">\n        <string>Export unlabelled images</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"10\" column=\"0\" colspan=\"3\">\n      <widget class=\"QCheckBox\" name=\"labelMapCheckbox\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"text\">\n        <string>Save VOC label maps</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"8\" column=\"0\" colspan=\"3\">\n      <widget class=\"QCheckBox\" name=\"appendLabelCheckbox\">\n       <property name=\"text\">\n        <string>Append existing label file</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ExportDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ExportDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/exporter.h",
    "content": "#ifndef EXPORTER_H\n#define EXPORTER_H\n\n#include <kittiexporter.h>\n#include <darknetexporter.h>\n#include <pascalvocexporter.h>\n#include <cocoexporter.h>\n#include <gcpexporter.h>\n#include <tfrecordexporter.h>\n#include <videoexporter.h>\n\n#endif // EXPORTER_H\n"
  },
  {
    "path": "src/gcpexporter.cpp",
    "content": "#include \"gcpexporter.h\"\n\nbool GCPExporter::processImages(const QString folder,\n                                const QString filename,\n                                const QList<QString> images,\n                                export_image_type split_type){\n    QString image_path;\n    QList<BoundingBox> labels;\n    QString label_filename = QString(\"%1/%2.txt\").arg(folder).arg(filename);\n\n    QFile f(label_filename);\n    if (!f.open(QIODevice::Append | QIODevice::Truncate)) {\n        return false;\n    }\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n\n\n    if(!disable_progress){\n        QString split_text = \"\";\n        if(split_type == EXPORT_VAL){\n            split_text = \"VAL\";\n            progress.setWindowTitle(\"Exporting validation images\");\n        }else if(split_type == EXPORT_TRAIN){\n            split_text = \"TRAIN\";\n            progress.setWindowTitle(\"Exporting train images\");\n        }else if(split_type == EXPORT_TEST){\n            split_text = \"TEST\";\n            progress.setWindowTitle(\"Exporting test images\");\n        }else{\n            split_text = \"UNASSIGNED\";\n        }\n    }else{\n        progress.hide();\n    }\n\n    int i=0;\n\n    foreach(image_path, images){\n\n        if(progress.wasCanceled()){\n            break;\n        }\n\n        // Check image\n        cv::Mat image = cv::imread(image_path.toStdString());\n        if(image.empty()) continue;\n\n        double width = static_cast<double>(image.cols);\n        double height = static_cast<double>(image.rows);\n\n        project->getLabels(image_path, labels);\n\n        // Check labels\n        if(!export_unlabelled && labels.size() == 0){\n            if(!disable_progress){\n                progress.setValue(i);\n                progress.setLabelText(QString(\"%1 is unlabelled\").arg(image_path));\n                progress.repaint();\n                QApplication::processEvents();\n            }\n            continue;\n        }\n\n        // Deal with duplicate filenames, etc\n        QString extension = QFileInfo(image_path).suffix();\n        QString filename_noext = QFileInfo(image_path).completeBaseName();\n\n        QString image_filename = QString(\"%1/%2.%3\").arg(folder).arg(filename_noext).arg(extension);\n\n        // Correct for duplicate file names in output\n        int dupe_file = 1;\n        while(QFile(image_filename).exists()){\n            image_filename = QString(\"%1/%2_%3.%4\").arg(folder).arg(filename_noext).arg(dupe_file++).arg(extension);\n        }\n\n        filename_noext = QFileInfo(image_filename).completeBaseName();\n\n        auto copied = QFile::copy(image_path, image_filename);\n        if(!copied){\n            qCritical() << \"Failed to copy image\" << image_filename;\n        }\n\n        auto bucket_path = QString(\"%1/%2.%3\").arg(bucket_uri).arg(filename_noext).arg(extension);\n\n        for(auto &label : labels){\n            auto gcp_label = QString(\"%1,%2,%3,%4,%5,,,%6,%7,,\\n\")\n                    .arg(split_type)\n                    .arg(bucket_path)\n                    .arg(label.classname)\n                    .arg(QString::number(static_cast<double>(label.rect.topLeft().x())/width))\n                    .arg(QString::number(static_cast<double>(label.rect.topLeft().y())/height))\n                    .arg(QString::number(static_cast<double>(label.rect.bottomRight().x())/width))\n                    .arg(QString::number(static_cast<double>(label.rect.bottomRight().x())/height));\n\n           qDebug() << gcp_label;\n\n           f.write(gcp_label.toUtf8());\n\n        }\n\n        if(!disable_progress){\n            progress.setValue(i++);\n            progress.setLabelText(image_filename);\n            QApplication::processEvents();\n        }\n    }\n\n    return true;\n}\n\nbool GCPExporter::setOutputFolder(const QString folder){\n\n    if(folder == \"\") return false;\n\n    output_folder = folder;\n\n    //Make output folder if it doesn't exist\n    if (!QDir(output_folder).exists()){\n        qInfo() << \"Making output folder\" << output_folder;\n        QDir().mkpath(output_folder);\n    }\n\n    image_folder = QDir::cleanPath(output_folder+\"/images\");\n    if (!QDir(image_folder).exists()){\n        qInfo() << \"Making validation folder\" << image_folder;\n        QDir().mkpath(image_folder);\n    }\n\n    image_folder = QDir::cleanPath(image_folder);\n\n    return true;\n\n}\n\nvoid GCPExporter::setBucket(QString uri, bool local){\n\n    if(!uri.startsWith(\"gs://\") && !local){\n        bucket_uri = QString(\"gs://%1\").arg(uri);\n    }else{\n        bucket_uri = uri;\n    }\n}\n\nvoid GCPExporter::process(){\n    processImages(image_folder, \"labels\", train_set, EXPORT_TRAIN);\n    processImages(image_folder, \"labels\", validation_set, EXPORT_VAL);\n}\n"
  },
  {
    "path": "src/gcpexporter.h",
    "content": "#ifndef GCPEXPORTER_H\n#define GCPEXPORTER_H\n\n#include<baseexporter.h>\n\nclass GCPExporter : public BaseExporter\n{\n    Q_OBJECT\npublic:\n    explicit GCPExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n\npublic slots:\n    void process();\n    void setBucket(QString uri, bool local=false);\n    bool setOutputFolder(const QString folder);\n\nprivate:\n    bool processImages(const QString output_folder, QString filename, const QList<QString> images, export_image_type split_type=EXPORT_TRAIN);\n    QString bucket_uri;\n    QString image_folder;\n};\n\n#endif // GCPEXPORTER_H\n"
  },
  {
    "path": "src/imagedisplay.cpp",
    "content": "#include \"imagedisplay.h\"\n#include \"ui_imagedisplay.h\"\n\nstd::unordered_map<std::string ,int> ImageDisplay::colour_hashmap{\n    { \"Cividis\", cv::COLORMAP_CIVIDIS },\n    { \"Inferno\", cv::COLORMAP_INFERNO },\n    { \"Magma\", cv::COLORMAP_MAGMA },\n    { \"Hot\", cv::COLORMAP_HOT },\n    { \"Bone\", cv::COLORMAP_BONE },\n    { \"Plasma\", cv::COLORMAP_PLASMA },\n    { \"Jet\", cv::COLORMAP_JET },\n    { \"Rainbow\", cv::COLORMAP_RAINBOW },\n    { \"Ocean\", cv::COLORMAP_OCEAN },\n    { \"Viridis\", cv::COLORMAP_VIRIDIS }\n};\n\nImageDisplay::ImageDisplay(QWidget *parent) :\n    QWidget(parent),\n    ui(new Ui::ImageDisplay)\n{\n    ui->setupUi(this);\n\n    imageLabel = new ImageLabel;\n    scrollArea = new QScrollArea(this);\n    ui->mainLayout->addWidget(scrollArea);\n\n    scrollArea->setWidget(imageLabel);\n\n    scrollArea->setAlignment(Qt::AlignCenter);\n    scrollArea->setBackgroundRole(QPalette::Shadow);\n\n    connect(ui->fitToWindowButton, SIGNAL(clicked()), this, SLOT(updateDisplay()));\n    connect(ui->resetZoomButton, SIGNAL(clicked()), this, SLOT(resetZoom()));\n    connect(ui->zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));\n    connect(ui->zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));\n    connect(ui->zoomSpinBox, SIGNAL(valueChanged(int)), this, SLOT(scaleImage()));\n\n    QtAwesome* awesome = new QtAwesome( qApp );\n    awesome->initFontAwesome();\n\n    ui->zoomInButton->setIcon(awesome->icon(fa::searchplus));\n    ui->zoomOutButton->setIcon(awesome->icon(fa::searchminus));\n    ui->resetZoomButton->setIcon(awesome->icon(fa::home));\n\n    updateDisplay();\n}\n\nImageDisplay::~ImageDisplay()\n{\n    delete ui;\n}\n\nvoid ImageDisplay::setImagePath(QString path){\n    if(path != \"\"){\n        current_imagepath = path;\n        loadPixmap();\n    }\n}\n\nvoid ImageDisplay::clearPixmap(void){\n    pixmap = QPixmap();\n    imageLabel->setPixmap(pixmap);\n}\n\nvoid ImageDisplay::convert16(cv::Mat &source, double minval, double maxval){\n\n    if(minval < 0 || maxval < 0){\n        cv::minMaxIdx(source, &minval, &maxval);\n    }\n\n    double range = maxval-minval;\n    double scale_factor = 255.0/range;\n\n    source.convertTo(source, CV_32FC1);\n    source -= minval;\n    source *= scale_factor;\n    source.convertTo(source, CV_8UC1);\n\n    return;\n}\n\ncv::Mat ImageDisplay::getOriginalImage(void){\n    return original_image;\n}\n\nvoid ImageDisplay::loadPixmap(){\n\n    if(current_imagepath == \"\")\n        return;\n\n    original_image = cv::imread(current_imagepath.toStdString(), cv::IMREAD_UNCHANGED|cv::IMREAD_ANYDEPTH);\n\n    if(original_image.empty()){\n        qCritical() << \"Failed to load image \" << current_imagepath;\n        return;\n    }\n\n    display_image = original_image.clone();\n\n    // Default to single channel 8-bit image\n    format = QImage::Format_Grayscale8;\n    bit_depth = 8;\n\n    if(original_image.elemSize() == 2){\n        convert16(display_image);\n        bit_depth = 16;\n    }\n\n    if(display_image.channels() == 3){\n        cv::cvtColor(display_image, display_image, cv::COLOR_BGR2RGB);\n        format = QImage::Format_RGB888;\n        bit_depth = 24;\n    }else if (display_image.channels() == 4){\n        cv::cvtColor(display_image, display_image, cv::COLOR_BGRA2RGBA);\n        format = QImage::Format_RGBA8888;\n        bit_depth = 32;\n    }else if(display_image.channels() == 1 && apply_colourmap){\n        cv::applyColorMap(display_image, display_image, colour_map);\n        cv::cvtColor(display_image, display_image, cv::COLOR_BGR2RGB);\n        format = QImage::Format_RGB888;\n    }\n\n    pixmap = QPixmap::fromImage(QImage(display_image.data, display_image.cols, display_image.rows, static_cast<int>(display_image.step), format));\n\n    imageLabel->setImage(display_image);\n    updateDisplay();\n    emit image_loaded();\n}\n\nvoid ImageDisplay::setColourMap(QString map){\n\n    auto map_str = map.toStdString();\n    if(colour_hashmap.count(map_str)){\n        colour_map = colour_hashmap[map_str];\n        loadPixmap();\n    }\n\n}\n\nvoid ImageDisplay::toggleColourMap(bool enable){\n    apply_colourmap = enable;\n    loadPixmap();\n}\n\nvoid ImageDisplay::adjustScrollBar(QScrollBar *scrollBar, double factor)\n{\n    scrollBar->setValue(int(factor * scrollBar->value()\n                            + ((factor - 1) * scrollBar->pageStep()/2)));\n}\n\nvoid ImageDisplay::scaleImage(void)\n{\n    if(pixmap.isNull()) return;\n\n    image_scale_factor = static_cast<double>(ui->zoomSpinBox->value()) / 100.0;\n\n    imageLabel->zoom(image_scale_factor);\n\n    adjustScrollBar(scrollArea->horizontalScrollBar(), image_scale_factor);\n    adjustScrollBar(scrollArea->verticalScrollBar(), image_scale_factor);\n\n    ui->zoomInButton->setEnabled(image_scale_factor <= 3.0);\n    ui->zoomOutButton->setEnabled(image_scale_factor >= 0.33);\n}\n\nvoid ImageDisplay::zoomIn()\n{\n    if(fit_to_window) return;\n    ui->zoomSpinBox->setValue(ui->zoomSpinBox->value()*1.2);\n}\n\nvoid ImageDisplay::zoomOut()\n{\n    if(fit_to_window) return;\n    ui->zoomSpinBox->setValue(ui->zoomSpinBox->value()*0.8);\n}\n\nvoid ImageDisplay::resetZoom(){\n\n    ui->zoomSpinBox->setValue(100);\n}\n\nvoid ImageDisplay::updateDisplay()\n{\n    if(pixmap.isNull()) return;\n\n    fit_to_window = ui->fitToWindowButton->isChecked();\n\n    // Decide whether to use scrollbars\n    scrollArea->setWidgetResizable(fit_to_window);\n\n    // Auto-scale the image to the size of the scrollarea,\n    // or leave it full-size\n    imageLabel->setScaledContents(fit_to_window);\n\n    imageLabel->setPixmap(pixmap);\n\n    repaint();\n\n}\n\n\n"
  },
  {
    "path": "src/imagedisplay.h",
    "content": "#ifndef IMAGEDISPLAY_H\n#define IMAGEDISPLAY_H\n\n#include <QWidget>\n#include <QScrollArea>\n#include <QScrollBar>\n#include <QTemporaryDir>\n#include <QApplication>\n\n#include <QtAwesome.h>\n#include <opencv2/opencv.hpp>\n#include <imagelabel.h>\n\n#include <unordered_map>\n\nnamespace Ui {\nclass ImageDisplay;\n}\n\nclass ImageDisplay : public QWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit ImageDisplay(QWidget *parent = nullptr);\n    ~ImageDisplay();\n\npublic slots:\n    void setImagePath(QString path);\n    ImageLabel* getImageLabel(void){return imageLabel;}\n    int getBitDepth(void){return bit_depth;}\n    void setColourMap(QString map);\n\n    void toggleColourMap(bool enable);\n    cv::Mat getOriginalImage();\n    void clearPixmap();\nprivate:\n    cv::Mat display_image;\n    cv::Mat original_image;\n    QPixmap pixmap;\n    Ui::ImageDisplay *ui;\n    QString current_imagepath;\n    void convert16(cv::Mat &source, double minval=-1, double maxval=-1);\n    ImageLabel* imageLabel;\n    QScrollArea* scrollArea;\n    bool fit_to_window = false;\n    double image_scale_factor = 1.0;\n    bool apply_colourmap = true;\n    int colour_map = cv::COLORMAP_MAGMA;\n    QImage::Format format = QImage::Format_Grayscale8;\n    int bit_depth = 8;\n\n    static std::unordered_map<std::string, int> colour_hashmap;\n\nprivate slots:\n    void loadPixmap();\n    void updateDisplay();\n    void resetZoom();\n    void scaleImage();\n\n    void adjustScrollBar(QScrollBar *scrollBar, double factor);\n    void zoomIn();\n    void zoomOut();\n\nsignals:\n    void image_loaded();\n};\n\n#endif // IMAGEDISPLAY_H\n"
  },
  {
    "path": "src/imagedisplay.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ImageDisplay</class>\n <widget class=\"QWidget\" name=\"ImageDisplay\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"mainLayout\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QCheckBox\" name=\"fitToWindowButton\">\n       <property name=\"text\">\n        <string>Fit to window</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"resetZoomButton\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>0</width>\n         <height>1</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>16777215</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"flat\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"zoomOutButton\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>16777215</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"flat\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"zoomInButton\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>16777215</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"flat\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QSpinBox\" name=\"zoomSpinBox\">\n       <property name=\"suffix\">\n        <string>%</string>\n       </property>\n       <property name=\"minimum\">\n        <number>33</number>\n       </property>\n       <property name=\"maximum\">\n        <number>300</number>\n       </property>\n       <property name=\"value\">\n        <number>100</number>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/imagelabel.cpp",
    "content": "#include \"imagelabel.h\"\n\nImageLabel::ImageLabel(QWidget *parent) :\n    QLabel(parent)\n{\n    setMinimumSize(1,1);\n    setAlignment(Qt::AlignCenter);\n    setMouseTracking(true);\n\n    // This is important to preserve the aspect ratio\n    setScaledContents(true);\n\n    // Dark background\n    setBackgroundRole(QPalette::Shadow);\n\n    // Size policy\n    setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);\n\n    setFocusPolicy(Qt::StrongFocus);\n\n    rubberBand = new QRubberBand(QRubberBand::Rectangle, this);\n\n    setDrawMode();\n}\n\nvoid ImageLabel::setPixmap ( QPixmap & p)\n{\n    base_pixmap = p;\n\n    drawLabels();\n\n    QPixmap pixmap;\n    resize(pixmap.size());\n}\n\nint ImageLabel::heightForWidth( int width ) const\n{\n    return base_pixmap.isNull() ? height() : static_cast<int>((static_cast<qreal>(base_pixmap.height())*width)/base_pixmap.width());\n}\n\nQSize ImageLabel::sizeHint() const\n{\n    int w = width();\n    return QSize( w, heightForWidth(w) );\n}\n\nvoid ImageLabel::resizeEvent(QResizeEvent * e)\n{\n    if(!base_pixmap.isNull()){\n        drawLabels();\n    }else{\n        e->ignore();\n    }\n}\n\nvoid ImageLabel::zoom(double factor){\n    zoom_factor = factor;\n    scaledPixmap();\n\n    if(zoom_factor == 1.0){\n        resize(base_pixmap.size());\n    }else{\n        resize(scaled_pixmap.size());\n    }\n\n}\n\nQPixmap ImageLabel::scaledPixmap(void)\n{\n\n    if(base_pixmap.isNull())\n        return QPixmap();\n\n    if(shouldScaleContents){\n        scaled_pixmap = base_pixmap.scaled( size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);\n    }else if(zoom_factor != 1.0){\n        scaled_pixmap = base_pixmap.scaled( zoom_factor*base_pixmap.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);\n    }else{\n        scaled_pixmap = base_pixmap;\n    }\n\n    scaled_width = scaled_pixmap.width();\n    scaled_height = scaled_pixmap.height();\n\n    scale_factor = static_cast<float>(scaled_height)/base_pixmap.height();\n\n    return scaled_pixmap;\n}\n\nvoid ImageLabel::setDrawMode()\n{\n    current_mode = MODE_DRAW;\n    setCursor(Qt::CrossCursor);\n    rubberBand->setGeometry(QRect(bbox_origin, QSize()));\n    rubberBand->show();\n    setBoundingBoxes(bboxes);\n}\n\nvoid ImageLabel::setDrawDragMode()\n{\n    current_mode = MODE_DRAW_DRAG;\n    rubberBand->setGeometry(QRect(bbox_origin, QSize()));\n    rubberBand->show();\n    setBoundingBoxes(bboxes);\n}\n\nvoid ImageLabel::setSelectMode(){\n    current_mode = MODE_SELECT;\n    setCursor(Qt::PointingHandCursor);\n    rubberBand->hide();\n    setBoundingBoxes(bboxes);\n}\n\nQPoint ImageLabel::getScaledImageLocation(QPoint location){\n    // If the image is fit to window\n\n    QPoint scaled_location = location;\n\n    if(scale_factor != 1.0){\n\n        // Get the location on the image, accounting for padding\n        scaled_location.setX(scaled_location.x() - (width() - scaled_width)/2);\n        scaled_location.setY(scaled_location.y() - (height() - scaled_height)/2);\n\n        scaled_location.setX(static_cast<int>(scaled_location.x() / scale_factor));\n        scaled_location.setY(static_cast<int>(scaled_location.y() / scale_factor));\n    }\n\n    return scaled_location;\n}\n\nvoid ImageLabel::mousePressEvent(QMouseEvent *ev){\n\n    if(base_pixmap.isNull()) return;\n\n    QPoint click_location = ev->pos();\n\n    if (current_mode == MODE_SELECT && ev->button() == Qt::LeftButton) {\n        bool clicked = true;\n        bool state_change = false;\n        auto active_box = checkBoundingBoxes(click_location, state_change, clicked);\n\n        if (state_change) {\n            drawLabels();\n        }\n\n        if (active_box.label_id > 0) {\n            edit_state = EDITING_BOX;\n            editing_bbox = active_box;\n            selected_bbox = active_box;\n            initial_edit_centre = click_location;\n\n        } else {\n            selected_bbox = BoundingBox();\n            edit_state = EDIT_WAIT;\n        }\n    } else if (current_mode == MODE_DRAW && ev->button() == Qt::LeftButton) {\n        if (bbox_state == WAIT_START) {\n            bbox_origin = click_location;\n\n            rubberBand->setGeometry(QRect(bbox_origin, QSize()));\n            rubberBand->show();\n\n            bbox_state = DRAWING_BBOX;\n        } else if (bbox_state == DRAWING_BBOX) {\n            bbox_final = click_location;\n\n            QRect bbox(bbox_origin, bbox_final);\n\n            bbox = clip(bbox.normalized());\n            bbox_origin = bbox.topLeft();\n            bbox_final = bbox.bottomRight();\n\n            rubberBand->setGeometry(bbox);\n\n            bbox_state = WAIT_START;\n        }\n    }\n}\n\nvoid ImageLabel::mouseReleaseEvent(QMouseEvent *ev)\n{\n    if (current_mode == MODE_SELECT && edit_state == EDITING_BOX) {\n        auto temp = editing_bbox;\n        editing_bbox = BoundingBox();\n        updateLabel(selected_bbox, temp);\n        edit_state = EDIT_WAIT;\n        setBoundingBoxes(bboxes);\n\n    } else {\n        ev->ignore();\n    }\n}\n\nvoid ImageLabel::mouseMoveEvent(QMouseEvent *ev)\n{\n    if (base_pixmap.isNull())\n        return;\n\n    if (bbox_state == DRAWING_BBOX && current_mode == MODE_DRAW) {\n        QRect bbox = QRect(bbox_origin, ev->pos()).normalized();\n        rubberBand->setGeometry(bbox);\n    }\n\n    if (current_mode == MODE_SELECT) {\n        if (edit_state == EDIT_WAIT) {\n            bool state_change;\n            checkBoundingBoxes(ev->pos(), state_change);\n            if (state_change) {\n                drawLabels();\n            }\n        } else if (edit_state == EDITING_BOX) {\n            editBoxCoords(editing_bbox, ev->pos());\n            drawLabels();\n        }\n    }\n}\n\nvoid ImageLabel::editBoxCoords(BoundingBox &bbox, QPoint location)\n{\n    auto scaled_location = getScaledImageLocation(location);\n\n    // Edge cases\n    if (bbox.selected_edge == EDGE_TOP) {\n        bbox.rect.setTop(scaled_location.y());\n    } else if (bbox.selected_edge == EDGE_BOTTOM) {\n        bbox.rect.setBottom(scaled_location.y());\n    } else if (bbox.selected_edge == EDGE_LEFT) {\n        bbox.rect.setLeft(scaled_location.x());\n    } else if (bbox.selected_edge == EDGE_RIGHT) {\n        bbox.rect.setRight(scaled_location.x());\n    }\n\n    // Corner cases\n    if (bbox.selected_corner == CORNER_BOTTOMLEFT) {\n        bbox.rect.setBottomLeft(scaled_location);\n    } else if (bbox.selected_corner == CORNER_BOTTOMRIGHT) {\n        bbox.rect.setBottomRight(scaled_location);\n    } else if (bbox.selected_corner == CORNER_TOPLEFT) {\n        bbox.rect.setTopLeft(scaled_location);\n    } else if (bbox.selected_corner == CORNER_TOPRIGHT) {\n        bbox.rect.setTopRight(scaled_location);\n    }\n\n    if (bbox.is_selected) {\n        int width = bbox.rect.width();\n        int height = bbox.rect.height();\n\n        auto delta = scaled_location - getScaledImageLocation(initial_edit_centre);\n\n        bbox.rect.setTopLeft(\n            {bbox.rect.topLeft().x() + delta.x(), bbox.rect.topLeft().y() + delta.y()});\n        bbox.rect.setWidth(width);\n        bbox.rect.setHeight(height);\n\n        initial_edit_centre = location;\n    }\n\n    bbox.rect = bbox.rect.normalized();\n}\n\nbool ImageLabel::isSelected(BoundingBox bbox, QPoint location, int padding)\n{\n    auto rect = QRect(QPoint(bbox.rect.topLeft().x() + padding, bbox.rect.topLeft().y() + padding),\n                      QPoint(bbox.rect.bottomRight().x() - padding,\n                             bbox.rect.bottomRight().y() - padding));\n\n    return rect.contains(location);\n}\n\nBoundingBox ImageLabel::checkBoundingBoxes(QPoint location, bool &state_change, bool click)\n{\n    state_change = false;\n    bool something_selected = false;\n    BoundingBox active;\n\n    for (auto &bbox : bboxes) {\n        // Check for selected (centre hitbox)\n        if (click) {\n            auto prev_select = bbox.is_selected;\n            bool selected = isSelected(bbox, getScaledImageLocation(location));\n\n            if (selected && !something_selected) {\n                bbox.is_selected = true;\n                selected_bbox = bbox;\n                active = bbox;\n                something_selected = true;\n                emit setCurrentClass(selected_bbox.classname);\n            } else {\n                bbox.is_selected = false;\n            }\n\n            state_change |= (prev_select == bbox.is_selected);\n        }\n\n        // Check for selected edge (edge hitboxes)\n        auto prev_edge = bbox.selected_edge;\n        bbox.selected_edge = getSelectedEdge(bbox, getScaledImageLocation(location));\n        if (bbox.selected_edge != EDGE_NONE) {\n            active = bbox;\n        }\n        state_change |= (prev_edge == bbox.selected_edge);\n\n        // Check for selected corner (corner hitboxes)\n        auto prev_corner = bbox.selected_corner;\n        bbox.selected_corner = getSelectedCorner(bbox, getScaledImageLocation(location));\n        if (bbox.selected_corner != CORNER_NONE) {\n            active = bbox;\n        }\n        state_change |= (prev_corner == bbox.selected_corner);\n    }\n\n    return active;\n}\n\nQRect ImageLabel::clip(QRect bbox){\n\n    auto xpad = (width() - scaled_width)/2;\n\n    bbox.setLeft(std::max(xpad, bbox.left()));\n    bbox.setRight(std::min(width()-xpad, bbox.right()));\n\n    auto ypad = (height() - scaled_height)/2;\n\n    bbox.setTop(std::max(ypad, bbox.top()));\n    bbox.setBottom(std::min(height()-ypad, bbox.bottom()));\n\n    return bbox;\n}\n\nvoid ImageLabel::drawBoundingBox(BoundingBox bbox)\n{\n    auto colour_list = QColor::colorNames();\n    QColor colour = QColor(colour_list.at(std::max(0, bbox.classid) % colour_list.size()));\n    drawBoundingBox(bbox, colour);\n}\n\nQRect ImageLabel::getPaddedRectangle(QPoint location, int pad)\n{\n    return QRect(location.x() - pad, location.y() - pad, 2 * pad, 2 * pad);\n}\n\nvoid ImageLabel::drawBoundingBox(BoundingBox bbox, QColor colour)\n{\n    if (scaled_pixmap.isNull())\n        return;\n\n    QPainter painter;\n    painter.begin(&scaled_pixmap);\n    QPen pen(colour, 2);\n    painter.setPen(pen);\n\n    auto scaled_bbox = bbox.rect;\n\n    scaled_bbox.setRight(static_cast<int>(scaled_bbox.right() * scale_factor));\n    scaled_bbox.setLeft(static_cast<int>(scaled_bbox.left() * scale_factor));\n    scaled_bbox.setTop(static_cast<int>(scaled_bbox.top() * scale_factor));\n    scaled_bbox.setBottom(static_cast<int>(scaled_bbox.bottom() * scale_factor));\n\n    if(bbox.classname != \"\"){\n\n        //painter.fillRect(QRect(scaled_bbox.bottomLeft(), scaled_bbox.bottomRight()+QPoint(0,-10)).normalized(), QBrush(Qt::white));\n\n        painter.setFont(QFont(\"Helvetica\", 10));\n        painter.drawText(scaled_bbox.bottomLeft(), bbox.classname);\n    }\n\n    painter.drawRect(scaled_bbox);\n\n    if (current_mode == MODE_SELECT) {\n        QPen pen(Qt::white, 4);\n        painter.setPen(pen);\n\n        painter.drawRect(getPaddedRectangle(scaled_bbox.topLeft(), 5));\n        painter.drawRect(getPaddedRectangle(scaled_bbox.topRight(), 5));\n        painter.drawRect(getPaddedRectangle(scaled_bbox.bottomLeft(), 5));\n        painter.drawRect(getPaddedRectangle(scaled_bbox.bottomRight(), 5));\n\n        if (bbox.selected_edge != EDGE_NONE) {\n            QPen pen(Qt::red, 4);\n            painter.setPen(pen);\n\n            if (bbox.selected_edge == EDGE_LEFT) {\n                painter.drawLine(scaled_bbox.topLeft(), scaled_bbox.bottomLeft());\n            } else if (bbox.selected_edge == EDGE_RIGHT) {\n                painter.drawLine(scaled_bbox.topRight(), scaled_bbox.bottomRight());\n            } else if (bbox.selected_edge == EDGE_BOTTOM) {\n                painter.drawLine(scaled_bbox.bottomRight(), scaled_bbox.bottomLeft());\n            } else {\n                painter.drawLine(scaled_bbox.topLeft(), scaled_bbox.topRight());\n            }\n        }\n\n        if (bbox.selected_corner != CORNER_NONE) {\n            QPen pen(Qt::red, 4);\n            painter.setPen(pen);\n\n            if (bbox.selected_corner == CORNER_TOPLEFT) {\n                painter.drawRect(getPaddedRectangle(scaled_bbox.topLeft(), 5));\n            } else if (bbox.selected_corner == CORNER_TOPRIGHT) {\n                painter.drawRect(getPaddedRectangle(scaled_bbox.topRight(), 5));\n            } else if (bbox.selected_corner == CORNER_BOTTOMLEFT) {\n                painter.drawRect(getPaddedRectangle(scaled_bbox.bottomLeft(), 5));\n            } else {\n                painter.drawRect(getPaddedRectangle(scaled_bbox.bottomRight(), 5));\n            }\n        }\n    }\n\n    painter.end();\n}\n\nvoid ImageLabel::setBoundingBoxes(QList<BoundingBox> input_bboxes){\n    bboxes.clear();\n    bboxes = input_bboxes;\n    drawLabels();\n}\n\nvoid ImageLabel::setClassname(QString classname)\n{\n    current_classname = classname;\n\n    if(selected_bbox.classname != classname){\n        auto new_bbox = selected_bbox;\n        new_bbox.classname = classname;\n\n        emit updateLabel(selected_bbox, new_bbox);\n    }\n\n}\n\nvoid ImageLabel::setScaledContents(bool should_scale){\n    shouldScaleContents = should_scale;\n\n    if(!shouldScaleContents){\n        scale_factor = 1.0;\n    }\n\n}\n\nbool ImageLabel::scaleContents(void){\n    return shouldScaleContents;\n}\n\nSelectedEdge ImageLabel::getSelectedEdge(BoundingBox bbox, QPoint location)\n{\n    auto edge = EDGE_NONE;\n    auto rect = bbox.rect;\n\n    auto left_hitbox = QRect(QPoint(rect.topLeft().x() - point_threshold,\n                                    rect.topLeft().y() + point_threshold),\n                             QPoint(rect.bottomLeft().x() + point_threshold,\n                                    rect.bottomLeft().y() - point_threshold));\n\n    auto top_hitbox = QRect(QPoint(rect.topLeft().x() + point_threshold,\n                                   rect.topLeft().y() - point_threshold),\n                            QPoint(rect.topRight().x() - point_threshold,\n                                   rect.topRight().y() + point_threshold));\n\n    auto right_hitbox = QRect(QPoint(rect.topRight().x() - point_threshold,\n                                     rect.topRight().y() + point_threshold),\n                              QPoint(rect.bottomRight().x() + point_threshold,\n                                     rect.bottomRight().y() - point_threshold));\n\n    auto bottom_hitbox = QRect(QPoint(rect.bottomLeft().x() + point_threshold,\n                                      rect.bottomLeft().y() - point_threshold),\n                               QPoint(rect.bottomRight().x() - point_threshold,\n                                      rect.bottomRight().y() + point_threshold));\n\n    bool proper = true;\n    if (left_hitbox.contains(location, proper)) {\n        edge = EDGE_LEFT;\n    } else if (right_hitbox.contains(location, proper)) {\n        edge = EDGE_RIGHT;\n    } else if (top_hitbox.contains(location, proper)) {\n        edge = EDGE_TOP;\n    } else if (bottom_hitbox.contains(location, proper)) {\n        edge = EDGE_BOTTOM;\n    }\n\n    return edge;\n}\n\ndouble ImageLabel::pointDistance(QPoint p1, QPoint p2)\n{\n    auto l = QLine(p1, p2);\n    return std::sqrt(std::pow(l.dx(), 2) + std::pow(l.dy(), 2));\n}\n\nSelectedCorner ImageLabel::getSelectedCorner(BoundingBox bbox, QPoint location)\n{\n    auto corner = CORNER_NONE;\n    auto rect = bbox.rect;\n\n    auto top_left_dist = pointDistance(rect.topLeft(), location);\n    auto top_right_dist = pointDistance(rect.topRight(), location);\n    auto bottom_left_dist = pointDistance(rect.bottomLeft(), location);\n    auto bottom_right_dist = pointDistance(rect.bottomRight(), location);\n\n    if (top_left_dist < point_threshold) {\n        corner = CORNER_TOPLEFT;\n    } else if (top_right_dist < point_threshold) {\n        corner = CORNER_TOPRIGHT;\n    } else if (bottom_left_dist < point_threshold) {\n        corner = CORNER_BOTTOMLEFT;\n    } else if (bottom_right_dist < point_threshold) {\n        corner = CORNER_BOTTOMRIGHT;\n    }\n\n    return corner;\n}\n\nvoid ImageLabel::drawLabels(QPoint cursor_location)\n{\n    scaledPixmap();\n\n    if (scale_factor == 1.0) {\n        scaled_pixmap = base_pixmap;\n    }\n\n    BoundingBox bbox;\n    auto colour_list = QColor::colorNames();\n\n    foreach (bbox, bboxes) {\n        if (bbox.label_id == editing_bbox.label_id) {\n            drawBoundingBox(editing_bbox, Qt::red);\n        } else {\n            if (bbox.label_id == selected_bbox.label_id) {\n                drawBoundingBox(bbox, Qt::green);\n            } else {\n                drawBoundingBox(bbox);\n            }\n        }\n    }\n\n    QLabel::setPixmap(scaled_pixmap);\n}\n\nvoid ImageLabel::addLabel(QRect rect, QString classname){\n    BoundingBox new_bbox;\n    new_bbox.classname = classname;\n    new_bbox.rect = rect;\n\n    bboxes.append(new_bbox);\n    emit newLabel(new_bbox);\n\n    drawLabels();\n}\n\nvoid ImageLabel::keyPressEvent(QKeyEvent *event)\n{\n\n    if(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete){\n        emit removeLabel(selected_bbox);\n    } else if (event->key() == Qt::Key_Escape) {\n        rubberBand->setGeometry(QRect(bbox_origin, QSize()));\n        bbox_state = WAIT_START;\n        selected_bbox = BoundingBox();\n        bool state;\n        checkBoundingBoxes({-1, -1}, state, true);\n        drawLabels();\n    } else if (event->key() == Qt::Key_Space && bbox_state == WAIT_START) {\n        if(rubberBand->width() > 0 && rubberBand->height() > 0){\n\n            if(current_classname == \"\"){\n                qWarning() << \"No class selected!\";\n            }else{\n\n                QRect bbox_rect;\n\n                bbox_rect = QRect(bbox_origin, bbox_final);\n                bbox_rect.setTopLeft(getScaledImageLocation(bbox_rect.topLeft()));\n                bbox_rect.setBottomRight(getScaledImageLocation(bbox_rect.bottomRight()));\n\n                addLabel(bbox_rect, current_classname);\n\n                rubberBand->setGeometry(QRect(bbox_origin, QSize()));\n            }\n    }else if(event->key() == 'o'){\n        emit setOccluded(selected_bbox);\n        drawLabels();\n    }\n    } else {\n        event->ignore();\n    }\n}\n"
  },
  {
    "path": "src/imagelabel.h",
    "content": "#ifndef IMAGELABEL_H\n#define IMAGELABEL_H\n\n\n#include <QLabel>\n#include <QPixmap>\n#include <QPainter>\n#include <QDebug>\n#include <QRubberBand>\n#include <QResizeEvent>\n\n#include <opencv2/opencv.hpp>\n#include<boundingbox.h>\n\nenum drawState {\n    WAIT_START,\n    DRAWING_BBOX,\n};\n\nenum interactionState {\n    MODE_DRAW,\n    MODE_DRAW_DRAG,\n    MODE_EDIT,\n    MODE_MOVE,\n    MODE_SELECT,\n};\n\nenum editState { EDIT_WAIT, EDIT_START, EDITING_BOX, MOVING_BOX };\n\nclass ImageLabel : public QLabel\n{\n    Q_OBJECT\npublic:\n    explicit ImageLabel(QWidget *parent = nullptr);\n    virtual int heightForWidth( int width ) const;\n    virtual QSize sizeHint() const;\n    void setScaledContents(bool scale_factor);\n    QList<BoundingBox> getBoundingBoxes(){return bboxes;}\n    cv::Mat getImage(void){return image;}\n    bool scaleContents(void);\n\nsignals:\n    void newLabel(BoundingBox);\n    void removeLabel(BoundingBox);\n    void updateLabel(BoundingBox, BoundingBox);\n    void setOccluded(BoundingBox);\n    void setCurrentClass(QString);\n\npublic slots:\n    void setPixmap ( QPixmap & );\n    void setImage(cv::Mat &image){this->image = image;}\n    void setBoundingBoxes(QList<BoundingBox> input_bboxes);\n    void setClassname(QString classname);\n    void addLabel(QRect rect, QString classname);\n    void zoom(double factor);\n\n    void setDrawMode();\n    void setDrawDragMode();\n    void setSelectMode();\n\n    void resizeEvent(QResizeEvent *);\n    void mousePressEvent(QMouseEvent *event);\n    void mouseReleaseEvent(QMouseEvent *event);\n    void mouseMoveEvent(QMouseEvent *event);\n    void keyPressEvent(QKeyEvent *event);\n\nprivate:\n    cv::Mat image;\n    QPixmap pix;\n    QPixmap base_pixmap;\n    QPixmap scaled_pixmap;\n    QString current_classname;\n    bool shouldScaleContents = false;\n\n    QList<BoundingBox> bboxes;\n    BoundingBox selected_bbox;\n    BoundingBox editing_bbox;\n    void drawBoundingBox(BoundingBox bbox);\n    void drawBoundingBox(BoundingBox bbox, QColor colour);\n    void drawLabels(QPoint cursor_location = QPoint(-1, -1));\n    QPoint getScaledImageLocation(QPoint location);\n    QPixmap scaledPixmap(void);\n\n    QRect clip(QRect bbox);\n\n    QPainter* painter;\n\n    drawState bbox_state = WAIT_START;\n    editState edit_state = EDIT_WAIT;\n    interactionState current_mode = MODE_DRAW;\n\n    QPoint bbox_origin, bbox_final;\n    QRubberBand* rubberBand;\n    int bbox_width = 0;\n    int bbox_height = 0;\n\n    double scale_factor = 1.0;\n    double zoom_factor = 1.0;\n\n    int scaled_width;\n    int scaled_height;\n    int point_threshold = 5;\n\n    void drawBoundingBoxes(QPoint location);\n    SelectedEdge getSelectedEdge(BoundingBox bbox, QPoint location);\n    SelectedCorner getSelectedCorner(BoundingBox bbox, QPoint location);\n    bool isSelected(BoundingBox bbox, QPoint location, int padding = 5);\n    BoundingBox checkBoundingBoxes(QPoint location, bool &state_change, bool click = false);\n    QRect getPaddedRectangle(QPoint location, int pad);\n    double pointDistance(QPoint p1, QPoint p2);\n    void editBoxCoords(BoundingBox &bbox, QPoint location);\n    QPoint initial_edit_centre;\n};\n\n#endif // IMAGELABEL_H\n"
  },
  {
    "path": "src/importdialog.cpp",
    "content": "#include \"importdialog.h\"\n#include \"ui_importdialog.h\"\n\nImportDialog::ImportDialog(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::ImportDialog)\n{\n    ui->setupUi(this);\n\n    connect(ui->importSelectComboBox,\n            SIGNAL(currentIndexChanged(QString)),\n            this,\n            SLOT(toggleImporter()));\n    connect(ui->importLabelledCheckbox,\n            SIGNAL(clicked(bool)),\n            this,\n            SLOT(setImportUnlabelled(bool)));\n    connect(ui->relativeImageCheckbox, SIGNAL(clicked(bool)), this, SLOT(setRelativePaths(bool)));\n    connect(ui->namesFileLineEdit, SIGNAL(textEdited(QString)), SLOT(setNamesFile(QString)));\n    connect(ui->namesFilePushButton, SIGNAL(clicked()), this, SLOT(setNamesFile()));\n    connect(ui->inputListLineEdit, SIGNAL(textEdited(QString)), SLOT(setInputFile(QString)));\n    connect(ui->inputListPushButton, SIGNAL(clicked()), this, SLOT(setInputFile()));\n    connect(ui->relativeImageRootLineEdit,\n            SIGNAL(textEdited(QString)),\n            SLOT(setRelativeImagePath(QString)));\n    connect(ui->relativeImageRootPushButton, SIGNAL(clicked()), this, SLOT(setRelativeImagePath()));\n    connect(ui->annotationLineEdit, SIGNAL(textEdited(QString)), SLOT(setAnnotationFile(QString)));\n    connect(ui->annotationPushButton, SIGNAL(clicked()), this, SLOT(setAnnotationFile()));\n\n    settings = new QSettings(\"DeepLabel\", \"DeepLabel\");\n\n    setImportUnlabelled(settings->value(\"import_unlabelled\", false).toBool());\n    ui->importLabelledCheckbox->setChecked(import_unlabelled);\n\n    setRelativePaths(settings->value(\"relative_image_paths\", false).toBool());\n    ui->relativeImageCheckbox->setChecked(relative_image_paths);\n\n    if(settings->contains(\"import_format\")){\n        auto format = settings->value(\"import_format\").toString();\n        if(format != \"\"){\n            setImporter(format);\n        }\n    }else{\n        setImporter(\"Darknet\");\n    }\n\n    if(settings->contains(\"input_file\")){\n        auto path = settings->value(\"input_file\").toString();\n        if(path != \"\"){\n            setInputFile(path);\n        }\n    }\n\n    if(settings->contains(\"names_file\")){\n        auto path = settings->value(\"names_file\").toString();\n        if(path != \"\"){\n            setNamesFile(path);\n         }\n    }\n\n    if(settings->contains(\"annotation_file\")){\n        auto path = settings->value(\"annotation_file\").toString();\n        if(path != \"\"){\n            setAnnotationFile(path);\n         }\n    }\n\n    if (settings->contains(\"relative_root\")) {\n        auto path = settings->value(\"relative_root\").toString();\n        if (path != \"\") {\n            setRelativeImagePath(path);\n        }\n    }\n}\n\nImportDialog::~ImportDialog()\n{\n    delete ui;\n}\n\nbool ImportDialog::getImportUnlabelled(void){\n    return import_unlabelled;\n}\n\nvoid ImportDialog::setImportUnlabelled(bool res){\n    import_unlabelled = res;\n    settings->setValue(\"import_unlabelled\", import_unlabelled);\n}\n\nvoid ImportDialog::setRelativePaths(bool res)\n{\n    relative_image_paths = res;\n    settings->setValue(\"relative_image_paths\", relative_image_paths);\n}\n\nvoid ImportDialog::setRelativeImagePath(QString path)\n{\n    if (path == \"\") {\n        QString openDir;\n        if (relative_root == \"\") {\n            openDir = QDir::homePath();\n        } else {\n            openDir = QDir(relative_root).path();\n        }\n\n        path = QFileDialog::getExistingDirectory(this, tr(\"Select relative root folder\"), openDir);\n    }\n\n    if (path != \"\") {\n        relative_root = path;\n        ui->relativeImageRootLineEdit->setText(relative_root);\n        settings->setValue(\"relative_root\", relative_root);\n    }\n\n    checkOK();\n}\n\nvoid ImportDialog::setInputFile(QString path){\n\n    if(path == \"\"){\n        QString openDir;\n        if(input_file == \"\"){\n             openDir = QDir::homePath();\n        }else{\n             openDir = QDir(input_file).path();\n        }\n\n        if(current_importer == \"MOT\"\n           || current_importer == \"BirdsAI\"){\n            path = QFileDialog::getExistingDirectory(this, tr(\"Select sequence folder\"),\n                                                            openDir);\n        }else if(current_importer == \"Coco\"\n                 || current_importer == \"PascalVOC\"){\n                  path = QFileDialog::getExistingDirectory(this, tr(\"Select image folder\"),\n                                                                  openDir);\n        }else{\n            path = QFileDialog::getOpenFileName(this, tr(\"Select input file\"),\n                                                            openDir);\n        }\n    }\n\n    if(path != \"\"){\n        input_file = path;\n        ui->inputListLineEdit->setText(input_file);\n        settings->setValue(\"input_file\", input_file);\n    }\n\n    checkOK();\n}\n\nvoid ImportDialog::setNamesFile(QString path){\n\n    if(path == \"\"){\n        QString openDir;\n\n        if(names_file == \"\"){\n             openDir = QDir::homePath();\n        }else{\n             openDir = QFileInfo(names_file).absoluteDir().absolutePath();\n        }\n\n        path = QFileDialog::getOpenFileName(this, tr(\"Select darknet names file\"),\n                                                        openDir);\n    }\n\n    if(path != \"\"){\n        names_file = path;\n        ui->namesFileLineEdit->setText(names_file);\n        settings->setValue(\"names_file\", names_file);\n    }\n\n    checkOK();\n}\n\nvoid ImportDialog::setAnnotationFile(QString path){\n\n    if(path == \"\"){\n        QString openDir;\n\n        if(annotation_file == \"\"){\n             openDir = QDir::homePath();\n        }else{\n             openDir = QFileInfo(annotation_file).absoluteDir().absolutePath();\n        }\n\n        if(current_importer == \"BirdsAI\" || current_importer == \"PascalVOC\"){\n            path = QFileDialog::getExistingDirectory(this, tr(\"Select annotation folder\"),\n                                                            openDir);\n        }else{\n            path = QFileDialog::getOpenFileName(this, tr(\"Select annotation file\"),\n                                                            openDir);\n        }\n    }\n\n    if(path != \"\"){\n        annotation_file = path;\n        ui->annotationLineEdit->setText(annotation_file);\n        settings->setValue(\"annotation_file\", annotation_file);\n    }\n\n    checkOK();\n}\n\nbool ImportDialog::checkOK(){\n\n    ui->namesFileLineEdit->setEnabled(current_importer != \"Coco\");\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);\n\n    if(current_importer == \"Coco\"){\n        ui->namesFileLineEdit->setDisabled(true);\n        ui->namesFilePushButton->setDisabled(true);\n        ui->inputListLineEdit->setEnabled(true);\n        ui->inputListPushButton->setEnabled(true);\n        ui->relativeImageRootLineEdit->setEnabled(false);\n        ui->relativeImageCheckbox->setEnabled(false);\n        ui->relativeImageRootPushButton->setEnabled(false);\n        ui->annotationLineEdit->setEnabled(true);\n        ui->annotationPushButton->setEnabled(true);\n\n        ui->ImagesLabel->setText(\"Image folder\");\n\n        if(!QFile::exists(annotation_file)){\n            //qCritical() << \"Annotation file doesn't exist\";\n            return false;\n        }\n    }\n\n    if(current_importer == \"PascalVOC\"){\n        ui->namesFileLineEdit->setDisabled(true);\n        ui->namesFilePushButton->setDisabled(true);\n        ui->inputListLineEdit->setEnabled(true);\n        ui->inputListPushButton->setEnabled(true);\n        ui->relativeImageRootLineEdit->setEnabled(false);\n        ui->relativeImageCheckbox->setEnabled(false);\n        ui->relativeImageRootPushButton->setEnabled(false);\n        ui->annotationLineEdit->setEnabled(true);\n        ui->annotationPushButton->setEnabled(true);\n\n        ui->ImagesLabel->setText(\"Image folder\");\n\n        if(!QFile::exists(annotation_file)){\n            //qCritical() << \"Annotation file doesn't exist\";\n            return false;\n        }\n    }\n\n    if(current_importer == \"MOT\" ||\n        ui->importSelectComboBox->currentText() == \"BirdsAI\"){\n        ui->namesFileLineEdit->setEnabled(true);\n        ui->namesFilePushButton->setEnabled(true);\n        ui->inputListLineEdit->setEnabled(true);\n        ui->inputListPushButton->setEnabled(true);\n        ui->relativeImageRootLineEdit->setEnabled(false);\n        ui->relativeImageCheckbox->setEnabled(false);\n        ui->relativeImageRootPushButton->setEnabled(false);\n\n        bool is_mot = (current_importer == \"MOT\");\n        ui->annotationLineEdit->setDisabled(is_mot);\n        ui->annotationPushButton->setDisabled(is_mot);\n\n        if(!QDir(annotation_file).exists() && !is_mot){\n            //qCritical() << \"Annotation folder doesn't exist\";\n            return false;\n        }\n\n        ui->ImagesLabel->setText(\"Image folder\");\n\n        if(!checkNamesFile(names_file))\n            return false;\n\n    }\n\n    if(current_importer == \"Darknet\"){\n        ui->namesFileLineEdit->setEnabled(true);\n        ui->namesFilePushButton->setEnabled(true);\n        ui->inputListLineEdit->setEnabled(true);\n        ui->inputListPushButton->setEnabled(true);\n        ui->relativeImageRootLineEdit->setEnabled(true);\n        ui->relativeImageCheckbox->setEnabled(true);\n        ui->relativeImageRootPushButton->setEnabled(true);\n        ui->annotationLineEdit->setDisabled(true);\n        ui->annotationPushButton->setDisabled(true);\n\n        ui->ImagesLabel->setText(\"Image list\");\n\n        if(!checkNamesFile(names_file))\n            return false;\n    }\n\n    // If input file exists\n    if(!QFile(input_file).exists() || input_file == \"\"){\n        //qCritical() << \"Import file/folder doesn't exist\";\n        return false;\n    }\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n    return true;\n}\n\nbool ImportDialog::checkNamesFile(QString names_file){\n    // If we're using darknet, check the names\n    // file exists and contains something\n    if(!QFile::exists(names_file)){\n        //qCritical() << \"Names file doesn't exist\";\n        return false;\n    }\n\n    QStringList class_list;\n    QFile fh(names_file);\n\n    if (fh.open(QIODevice::ReadOnly)) {\n\n        while (!fh.atEnd()) {\n            // Darknet name file is just a newline delimited list of classes\n            QByteArray line = fh.readLine();\n            class_list.append(line);\n        }\n    }\n\n    if(class_list.size() == 0){\n        //qCritical() << \"No classes found\";\n        return false;\n    }\n\n    return true;\n}\n\nvoid ImportDialog::setImporter(QString format){\n    if(format == \"\"){\n        current_importer = ui->importSelectComboBox->currentText();\n        settings->setValue(\"import_format\", current_importer);\n    }else{\n        ui->importSelectComboBox->setCurrentText(format);\n        current_importer = format;\n    }\n\n    checkOK();\n}\n\nvoid ImportDialog::toggleImporter(QString format){\n    setImporter(format);\n}\n\n"
  },
  {
    "path": "src/importdialog.h",
    "content": "#ifndef IMPORTDIALOG_H\n#define IMPORTDIALOG_H\n\n#include <QDialog>\n#include <QPushButton>\n#include <QFile>\n#include <QDir>\n#include <QFileDialog>\n#include <QSettings>\n#include <QtDebug>\n\nnamespace Ui {\nclass ImportDialog;\n}\n\nclass ImportDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ImportDialog(QWidget *parent = nullptr);\n    QString getImporter() { return current_importer; }\n    QString getInputFile() { return input_file; }\n    QString getNamesFile() { return names_file; }\n    QString getRelativePath() { return relative_root; }\n    QString getAnnotationFile() { return annotation_file; }\n    bool getImportUnlabelled();\n    bool getUseRelativePaths() { return relative_image_paths; }\n    ~ImportDialog();\n\nprivate slots:\n    void setNamesFile(QString path = \"\");\n    void setInputFile(QString path = \"\");\n    void setAnnotationFile(QString path = \"\");\n    void toggleImporter(QString format = \"\");\n    void setImportUnlabelled(bool res);\n    void setRelativePaths(bool res);\n    void setRelativeImagePath(QString path = \"\");\n\nprivate:\n    bool checkOK();\n    QSettings *settings;\n\n    Ui::ImportDialog *ui;\n    QString input_file = \"\";\n    QString names_file = \"\";\n    QString annotation_file = \"\";\n    QString relative_root = \"\";\n    QString current_importer = \"Darknet\";\n    bool import_unlabelled;\n    bool relative_image_paths;\n    bool checkNamesFile(QString names_file);\n    void setImporter(QString format);\n};\n\n#endif // IMPORTDIALOG_H\n"
  },
  {
    "path": "src/importdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ImportDialog</class>\n <widget class=\"QDialog\" name=\"ImportDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>522</width>\n    <height>274</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"ImagesLabel\">\n       <property name=\"text\">\n        <string>Images</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"2\">\n      <widget class=\"QToolButton\" name=\"inputListPushButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Relative Root</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"2\">\n      <widget class=\"QToolButton\" name=\"annotationPushButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"inputListLineEdit\">\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"placeholderText\">\n        <string/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"annotationLineEdit\"/>\n     </item>\n     <item row=\"3\" column=\"2\">\n      <widget class=\"QToolButton\" name=\"namesFilePushButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>Names file</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Project Type:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"namesFileLineEdit\">\n       <property name=\"enabled\">\n        <bool>true</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string>Darknet format</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\" colspan=\"2\">\n      <widget class=\"QComboBox\" name=\"importSelectComboBox\">\n       <item>\n        <property name=\"text\">\n         <string>BirdsAI</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Coco</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Darknet</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>MOT</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>PascalVOC</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"AnnotationsLabel\">\n       <property name=\"text\">\n        <string>Annotations</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"relativeImageRootLineEdit\">\n       <property name=\"placeholderText\">\n        <string>Images are relative to this path</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"7\" column=\"0\" colspan=\"2\">\n      <widget class=\"QCheckBox\" name=\"importLabelledCheckbox\">\n       <property name=\"text\">\n        <string>Import unlabelled images</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"0\" colspan=\"2\">\n      <widget class=\"QCheckBox\" name=\"relativeImageCheckbox\">\n       <property name=\"text\">\n        <string>Use relative image paths</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"2\">\n      <widget class=\"QToolButton\" name=\"relativeImageRootPushButton\">\n       <property name=\"text\">\n        <string>...</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ImportDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ImportDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/importer.h",
    "content": "#ifndef IMPORTER_H\n#define IMPORTER_H\n\n#include <darknetimporter.h>\n#include <motimporter.h>\n#include <birdsaiimporter.h>\n#include <cocoimporter.h>\n#include <pascalvocimporter.h>\n#include <tfrecordimporter.h>\n\n#endif // IMPORTER_H\n"
  },
  {
    "path": "src/kittiexporter.cpp",
    "content": "#include \"kittiexporter.h\"\n\nbool KittiExporter::setOutputFolder(QString folder){\n\n    if(folder == \"\") return false;\n\n    output_folder = folder;\n\n    train_folder = QDir::cleanPath(output_folder + \"/train\");\n    val_folder = QDir::cleanPath(output_folder + \"/val\");\n\n    train_label_folder = QDir::cleanPath(train_folder + \"/labels\");\n    train_image_folder = QDir::cleanPath(train_folder + \"/images\");\n    val_label_folder = QDir::cleanPath(val_folder + \"/labels\");\n    val_image_folder = QDir::cleanPath(val_folder + \"/images\");\n\n    QDir(output_folder).mkpath(train_label_folder);\n    QDir(output_folder).mkpath(train_image_folder);\n    QDir(output_folder).mkpath(val_label_folder);\n    QDir(output_folder).mkpath(val_image_folder);\n\n    if(!QDir(train_label_folder).exists()) return false;\n    if(!QDir(train_image_folder).exists()) return false;\n    if(!QDir(val_label_folder).exists()) return false;\n    if(!QDir(val_image_folder).exists()) return false;\n\n    return true;\n\n}\n\nvoid KittiExporter::appendLabel(QString label_filename, QList<BoundingBox> labels, double scale_x, double scale_y){\n\n    QFile f(label_filename);\n\n    if (f.open(QIODevice::WriteOnly | QIODevice::Append)) {\n        BoundingBox label;\n        foreach(label, labels){\n            QString text;\n\n            text += label.classname;\n            text += \" 0.0\";\n            text += \" 0\";\n            text += \" 0.0\";\n            text += QString(\" %1\").arg(label.rect.left() * scale_x);\n            text += QString(\" %2\").arg(label.rect.top() * scale_x);\n            text += QString(\" %3\").arg(label.rect.right() * scale_y);\n            text += QString(\" %4\").arg(label.rect.bottom() * scale_y);\n            text += \" 0.0 0.0 0.0 0.0 0.0 0.0 0.0\\n\";\n\n            f.write(text.toUtf8());\n\n        }\n\n    }\n}\n\nint KittiExporter::processSet(QString folder, QList<QString> images, int n_images){\n\n    int base = 10;\n    int pad = 5;\n\n    QString image;\n    QList<BoundingBox> labels;\n    double scale_x=1, scale_y=1;\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n\n    if(disable_progress){\n        progress.hide();\n    }\n\n    int progress_count = 0;\n\n    foreach(image, images){\n\n        if(progress.wasCanceled()){\n            break;\n        }\n\n        project->getLabels(image, labels);\n\n        if(!export_unlabelled && labels.size() == 0){\n            if(!disable_progress){\n                progress.setValue(progress_count);\n                progress.setLabelText(QString(\"%1 is unlabelled\").arg(image));\n                progress.repaint();\n                QApplication::processEvents();\n            }\n            continue;\n        }\n\n        QString extension = QFileInfo(image).suffix();\n        QString filename_noext = QFileInfo(image).completeBaseName();\n\n        QString image_filename = QString(\"%1/%2.%3\").arg(folder).arg(filename_noext).arg(extension);\n\n        // Correct for duplicate file names in output\n        int dupe_file = 1;\n        while(QFile(image_filename).exists()){\n            image_filename = QString(\"%1/%2_%3.%4\").arg(folder).arg(filename_noext).arg(dupe_file++).arg(extension);\n        }\n\n        QString label_filename = QString(\"%1/labels/%2.txt\").arg(folder).arg(n_images, pad, base, QChar('0'));\n        appendLabel(label_filename, labels, scale_x, scale_y);\n\n        if(!disable_progress){\n            progress.setValue(progress_count++);\n            progress.setLabelText(image_filename);\n            QApplication::processEvents();\n        }\n\n    }\n\n    return n_images;\n}\n\nvoid KittiExporter::process(){\n\n    int n_images = 0;\n\n    n_images = processSet(train_folder, train_set, 0);\n    processSet(val_folder, validation_set, n_images);\n\n}\n\n"
  },
  {
    "path": "src/kittiexporter.h",
    "content": "#ifndef KITTIEXPORTER_H\n#define KITTIEXPORTER_H\n\n#include <baseexporter.h>\n\nclass KittiExporter : public BaseExporter\n{\n    Q_OBJECT\npublic:\n    explicit KittiExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n\npublic slots:\n    void process();\n    bool setOutputFolder(QString folder);\n\nprivate:\n    void appendLabel(QString file, QList<BoundingBox> labels, double scale_x=1, double scale_y=1);\n    int processSet(QString folder, QList<QString> images, int i);\n};\n\n#endif // KITTIEXPORTER_H\n"
  },
  {
    "path": "src/labelproject.cpp",
    "content": "#include \"labelproject.h\"\n\nLabelProject::LabelProject(QObject *parent) : QObject(parent)\n{\n    connect(this, SIGNAL(video_split_finished(QString)), this, SLOT(addImageFolder(QString)));\n}\n\nvoid LabelProject::assignThread(QThread *thread) {\n  this->moveToThread(thread);\n  connect(this, SIGNAL(finished()), thread, SLOT(quit()));\n  connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));\n  connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));\n  thread->start();\n}\n\nbool LabelProject::loadDatabase(QString fileName)\n{\n    /*!\n     * Load and check an existing database file. If the database is valid, returns true, otherwise\n     * if an error occured returns false.\n     */\n\n    connection_name = fileName;\n\n    auto db = QSqlDatabase::addDatabase(\"QSQLITE\", connection_name);\n    db.setDatabaseName(fileName);\n\n    QFileInfo check_file(fileName);\n\n    if (!db.open()) {\n        QMessageBox::critical(nullptr, QObject::tr(\"Cannot open database\"),\n            QObject::tr(\"Unable to establish a database connection.\\n\"\n                        \"This example needs SQLite support. Please read \"\n                        \"the Qt SQL driver documentation for information how \"\n                        \"to build it.\\n\\n\"\n                        \"Click Cancel to exit.\"), QMessageBox::Cancel);\n        return false;\n    }\n    else if ( check_file.exists() && check_file.isFile() ){\n        return checkDatabase();\n    }else{\n        return true;\n    }\n\n}\n\nbool LabelProject::checkDatabase(){\n\n    /*!\n     * Check that the newly loaded database contains the correct tables/fields. Returns\n     * true if the database is valid, false if not.\n     */\n\n    {\n        auto db = getDatabase();\n        QSqlRecord image_record = db.record(\"images\");\n        QSqlRecord label_record = db.record(\"labels\");\n        QSqlRecord class_record = db.record(\"classes\");\n\n        if(image_record.isEmpty()) return false;\n        if(label_record.isEmpty()) return false;\n        if(class_record.isEmpty()) return false;\n\n        if(!image_record.contains(\"image_id\")) return false;\n        if(!image_record.contains(\"path\")) return false;\n\n        if(!label_record.contains(\"image_id\")) return false;\n        if(!label_record.contains(\"label_id\")) return false;\n        if(!label_record.contains(\"class_id\")) return false;\n        if(!label_record.contains(\"x\")) return false;\n        if(!label_record.contains(\"y\")) return false;\n        if(!label_record.contains(\"width\")) return false;\n        if(!label_record.contains(\"height\")) return false;\n\n        if(!class_record.contains(\"class_id\")) return false;\n        if(!class_record.contains(\"name\")) return false;\n    }\n\n    qInfo() << \"Database structure looks good\";\n\n    return true;\n}\n\nbool LabelProject::createDatabase(QString fileName)\n{\n    /*!\n     * Create a new labelling database at the given absolute location (\\a fileName). Returns true if the database\n     * was created successfully.\n     */\n\n    bool res;\n\n    loadDatabase(fileName);\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n\n        /*\n         * The label database is a simple relational db - image locations, classes\n         * and label bounding boxes.\n         */\n\n        res = query.exec(\"CREATE table images (image_id INTEGER PRIMARY KEY ASC, \"\n                   \"path varchar(256))\");\n        res &= query.exec(\"CREATE table classes (class_id INTEGER PRIMARY KEY ASC, \"\n                   \"name varchar(32))\");\n        res &= query.exec(\"CREATE table labels (label_id INTEGER PRIMARY KEY ASC, \"\n                   \"image_id int, class_id int, x int, y int, width int, height int, automatic int)\");\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }\n    }\n\n    return checkDatabase();\n}\n\nvoid LabelProject::addFolderRecursive(QString path_filter){\n    /*!\n     * Search for (and add) all subfolders that match \\a path_filter, which\n     * can include * wildcards.\n     */\n\n    // Start at the root directory in the path.\n    path_filter = QDir(path_filter).absolutePath();\n    QRegExp rx(path_filter);\n    rx.setPatternSyntax(QRegExp::Wildcard);\n\n    // Find the first instance of a *\n    int first_wildcard = path_filter.indexOf('*');\n    auto pre_wildcard = path_filter.mid(0, first_wildcard);\n\n    // Get the top level folder\n    int last_separator = pre_wildcard.lastIndexOf(QDir::separator());\n    auto top_level_folder = pre_wildcard.mid(0, last_separator);\n\n    // Recurse into this folder and find all subdirectories\n    QDirIterator it(top_level_folder, QDir::Dirs, QDirIterator::Subdirectories);\n    QList<QString> subfolders;\n    while (it.hasNext()) {\n        auto subfolder = QDir(it.next()).canonicalPath();\n\n        if (rx.exactMatch(subfolder)){\n            subfolders.append(subfolder);\n        }\n    }\n\n    subfolders.removeDuplicates();\n    subfolders.sort();\n\n    for(auto &subfolder : subfolders){\n        addImageFolder(subfolder);\n    }\n\n}\n\nbool LabelProject::getImageList(QList<QString> &images, bool relative)\n{\n    /*!\n     * Get a list of all images in the database with absolute paths, which is cleared prior to retrieval. Returns false if the database query failed.\n     */\n    bool res = false;\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        res = query.exec(\"SELECT path FROM images\");\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n\n            images.clear();\n\n            while (query.next()) {\n                QString path = query.value(0).toString();\n                // Push absolute file path\n                if(!relative){\n                    auto abs_path = QDir::cleanPath(QDir(db.databaseName()).absolutePath() + QDir::separator() + path);\n                    images.push_back(abs_path);\n                }else{\n                    images.push_back(QDir(db.databaseName()).relativeFilePath(path));\n                }\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::getLabelledImageList(QList<QString> &images, bool relative)\n{\n    /*!\n     * Get a list of all labelled images in the database with absolute paths, which is cleared prior to retrieval. Returns false if the database query failed.\n     */\n    bool res = false;\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        res = query.exec(\"SELECT path FROM images, labels\"\n                         \" WHERE images.image_id = labels.image_id\"\n                         \" GROUP BY images.image_id\");\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n\n            images.clear();\n\n            while (query.next()) {\n                QString path = query.value(0).toString();\n                // Push absolute file path\n                if(!relative){\n                    auto abs_path = QDir::cleanPath(QDir(db.databaseName()).absolutePath() + QDir::separator() + path);\n                    images.push_back(abs_path);\n                }else{\n                    images.push_back(QDir(db.databaseName()).relativeFilePath(path));\n                }\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::getClassList(QList<QString> &classes)\n{\n    /*!\n     * Get a list of all class names in the database and puts them in (\\a classes), which is cleared prior to retrieval. Returns false if the database query failed.\n     */\n    bool res = false;\n\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        res = query.exec(\"SELECT name FROM classes\");\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n\n            classes.clear();\n\n            while (query.next()) {\n                QString name = query.value(0).toString();\n                if(name == \"\"){\n                    qWarning() << \"Warning: empty classname found in db file\";\n                }\n                classes.push_back(name);\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::classInDB(QString className){\n    /*!\n     * Returns true if \\a className is in the database\n     */\n    bool res = false;\n\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        query.prepare(\"SELECT * FROM classes WHERe (name)\"\n                      \"= (:name)\");\n\n        query.bindValue(\":name\", className);\n        res = query.exec();\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n            return false;\n        }else{\n            return query.next();\n        }\n    }\n}\n\nbool LabelProject::imageInDB(QString fileName){\n    /*!\n     * Returns true if \\a fileName is in the database\n     */\n    bool res = false;\n    //IF NOT EXISTS(SELECT * FROM images where (path) = (:PATH))\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        query.prepare(\"SELECT * FROM images WHERe (path)\"\n                      \"= (:path)\");\n\n        query.bindValue(\":path\", QDir(db.databaseName()).relativeFilePath(fileName));\n        res = query.exec();\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n            return query.next();\n        }\n    }\n\n    return false;\n}\n\nvoid LabelProject::cancelLoad(){\n    /*!\n     * Cancel loading a video or processing a folder\n     */\n    should_cancel = true;\n}\n\nvoid LabelProject::addVideo(QString fileName, QString outputFolder, int frame_skip){\n    cv::VideoCapture video(fileName.toStdString());\n\n    QString base_name = QFileInfo(fileName).completeBaseName();\n    QDir dir(outputFolder);\n\n    cv::Mat frame;\n    int frame_count = 0;\n    int n_frames = video.get(cv::CAP_PROP_FRAME_COUNT);\n\n    if(!video.isOpened()){\n        qCritical() << \"Failed to open\" << fileName;\n    }\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, n_frames, static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setWindowTitle(\"Loading video\");\n\n    while(video.read(frame)){\n\n        if(progress.wasCanceled() || frame.empty())\n            break;\n\n        if(frame_count++ % frame_skip != 0)\n            continue;\n\n        QString output_name = QString(\"%1_%2.jpg\").arg(base_name).arg(frame_count, 6, 10, QChar('0'));\n\n        output_name = dir.absoluteFilePath(output_name);\n        cv::imwrite(output_name.toStdString(), frame);\n\n        qDebug() << output_name;\n\n        progress.setValue(frame_count);\n        progress.setLabelText(output_name);\n\n    }\n\n    video.release();\n\n    emit video_split_finished(outputFolder);\n\n    return;\n}\n\nbool LabelProject::addAsset(QString fileName)\n{\n    /*!\n     * Add an asset to the database, given an absolute path to the image or video (\\a fileName). Returns false if the file doesn't exist or if the query failed.\n     */\n    bool res = false;\n\n    if(!QFile::exists(fileName)){\n        qCritical() << \"File not found on disk \" << fileName;\n        return false;\n    }\n\n    if(imageInDB(fileName)){\n        qDebug() << \"Image exists!\";\n        return true;\n    }\n    QFileInfo check_file(fileName);\n\n    if (  check_file.exists() && check_file.isFile() ){\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n            query.prepare(\"INSERT INTO images (path)\"\n                          \"VALUES (:path)\");\n\n            query.bindValue(\":path\", QDir(db.databaseName()).relativeFilePath(fileName));\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n    }\n\n    return res;\n}\n\nint LabelProject::getImageId(QString fileName){\n    /*!\n     * Get the image ID for an image at an absolute path (\\a fileName), returns -1 if not found\n     */\n    int id = -1;\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        query.prepare(\"SELECT image_id FROM images WHERE path = ?\");\n\n        //Important - we need the relative image path again\n        query.bindValue(0, QDir(db.databaseName()).relativeFilePath(fileName));\n        bool res = query.exec();\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n            if(query.next()){\n                id = query.value(0).toInt();\n            }\n        }\n    }\n\n    return id;\n}\n\nint LabelProject::getClassId(QString className){\n    /*!\n     * Get the class ID for an class name, (\\a className), returns -1 if not found\n     */\n    int id = -1;\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        query.prepare(\"SELECT class_id FROM classes WHERE name = ?\");\n        query.bindValue(0, className);\n        bool res = query.exec();\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n            if(query.next()){\n                id = query.value(0).toInt();\n            }\n        }\n    }\n\n    return id;\n}\n\nQString LabelProject::getClassName(int classID){\n    /*!\n     * Get the class Name for an class id, (\\a classID), returns \"\" if not found\n     */\n    QString className = \"\";\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        query.prepare(\"SELECT name FROM classes WHERE class_id = ?\");\n        query.bindValue(0, classID);\n        bool res = query.exec();\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }else{\n            if(query.next()){\n                className = query.value(0).toString();\n            }\n        }\n    }\n\n    return className;\n}\n\nbool LabelProject::getLabels(QString fileName, QList<BoundingBox> &bboxes){\n    bboxes.clear();\n    int image_id = getImageId(fileName);\n    return getLabels(image_id, bboxes);\n}\n\nbool LabelProject::getClassCounts(QMap<int, int> &counts){\n    /*!\n     * Get class counts for the project.\n     */\n    bool res = false;\n\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n\n        query.prepare(\"SELECT class_id, COUNT(class_id) AS \\\"count\\\" FROM labels GROUP BY class_id\");\n\n        res = query.exec();\n\n        if(!res){\n            qCritical() << \"Error: \" << query.lastError();\n        }\n\n        while(query.next()){\n            auto rec = query.record();\n            auto id = rec.value(rec.indexOf(\"class_id\")).toInt();\n            auto count = rec.value(rec.indexOf(\"count\")).toInt();\n            counts.insert(id, count);\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::getLabels(int image_id, QList<BoundingBox> &bboxes){\n    /*!\n     * Get the labels for a given image with ID \\a image_id and puts them in the provided list: \\a bboxes. Note \\a bboxes will be emptied.\n     * Returns false if the query failed.\n     */\n    bool res = false;\n\n    if(image_id > 0){\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            query.prepare(\"SELECT label_id, name, x, y, width, height FROM labels\"\n                          \" INNER JOIN classes ON labels.class_id = classes.class_id\"\n                          \" WHERE image_id = ?\");\n            query.addBindValue(image_id);\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n\n            while(query.next()){\n                BoundingBox new_bbox;\n                auto rec = query.record();\n\n                new_bbox.label_id = rec.value(rec.indexOf(\"label_id\")).toInt();\n                new_bbox.classname = rec.value(rec.indexOf(\"name\")).toString();\n                new_bbox.classid = getClassId(new_bbox.classname);\n\n                new_bbox.rect.setX(rec.value(rec.indexOf(\"x\")).toInt());\n                new_bbox.rect.setY(rec.value(rec.indexOf(\"y\")).toInt());\n                new_bbox.rect.setWidth(rec.value(rec.indexOf(\"width\")).toInt());\n                new_bbox.rect.setHeight(rec.value(rec.indexOf(\"height\")).toInt());\n\n                if (new_bbox.rect.width() * new_bbox.rect.height() <= 0) {\n                    qWarning() << \"Zero size bounding box found in database\";\n                    continue;\n                }\n\n                bboxes.append(new_bbox);\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::removeLabels(QString fileName){\n    /*!\n     * Remove all labels from an image given an absolute path to the image (\\a fileName).\n     */\n    int image_id = getImageId(fileName);\n\n    bool res = false;\n\n    if(image_id > 0){\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            query.prepare(\"DELETE FROM labels WHERE (image_id = :image_id)\");\n            query.bindValue(\":image_id\", image_id);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n\n    }\n\n    return res;\n}\n\nbool LabelProject::removeLabel(QString fileName, int label_id)\n{\n    /*!\n     * Remove a label given an absolute path  (\\a fileName) and the bounding box  (\\a bbox). Returns false\n     * if the query failed.\n     */\n    int image_id = getImageId(fileName);\n\n    bool res = false;\n\n    if (image_id > 0) {\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            query.prepare(\n                \"DELETE FROM labels WHERE (image_id = :image_id AND label_id = :label_id)\");\n            query.bindValue(\":image_id\", image_id);\n            query.bindValue(\":label_id\", label_id);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::setOccluded(QString fileName, BoundingBox bbox, int occluded){\n    /*!\n     * Set a label to be occluded. Returns false\n     * if the query failed.\n     */\n    int image_id = getImageId(fileName);\n    int class_id = getClassId(bbox.classname);\n\n    bool res = false;\n\n    if(image_id > 0 && class_id > 0){\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            query.prepare(\"UPDATE labels SET occluded = :occluded WHERE (image_id = :image_id AND class_id = :class_id\"\n                          \" AND x = :x AND y = :y AND width = :width AND height = :height)\");\n            query.bindValue(\":image_id\", image_id);\n            query.bindValue(\":class_id\", class_id);\n            query.bindValue(\":x\", bbox.rect.x());\n            query.bindValue(\":y\", bbox.rect.y());\n            query.bindValue(\":width\", bbox.rect.width());\n            query.bindValue(\":height\", bbox.rect.height());\n            query.bindValue(\":occluded\", occluded);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::removeClass(QString className){\n    /*!\n     * Remove a class given a class name (\\a className). Also removes all labels with that class. Returns false\n     * if the query failed.\n     */\n    int class_id = getClassId(className);\n\n    bool res = false;\n\n    if(class_id > 0){\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            // Delete all labels with this class\n            query.prepare(\"DELETE FROM labels WHERE (class_id = :class_id)\");\n            query.bindValue(\":class_id\", class_id);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n\n            // Delete the class itself\n            query.prepare(\"DELETE FROM classes WHERE (class_id = :class_id)\");\n            query.bindValue(\":class_id\", class_id);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n    }\n\n    return res;\n}\n\nint LabelProject::getNextUnlabelled(QString fileName){\n    /*!\n     * Returns the index of the next unlabelled image, after \\a fileName\n     */\n    QList<QString> images;\n    getImageList(images);\n\n    QList<BoundingBox> bboxes;\n\n    for(int i = images.indexOf(fileName) + 1; i < images.size(); i++){\n        bboxes.clear();\n        getLabels(images.at(i), bboxes);\n\n        if(bboxes.size() == 0) return i+1;\n    }\n\n    return -1;\n}\n\nint LabelProject::getNextInstance(QString fileName, QString className){\n    /*!\n     * Returns the index of the next unlabelled image, after \\a fileName\n     */\n    QList<QString> images;\n    getImageList(images);\n\n    int i = images.indexOf(fileName);\n    QList<BoundingBox> bboxes;\n\n    for(i += 1; i < images.size(); i++){\n        bboxes.clear();\n        getLabels(images.at(i), bboxes);\n\n        for(auto bbox : bboxes){\n            if(bbox.classname.compare(className) == 0){\n                return i;\n            }\n        }\n    }\n\n    return -1;\n}\n\nbool LabelProject::removeImage(QString fileName){\n\n    /*!\n     * Remove an image given an absolute path (\\a fileName). Also removes all labels for that image. Returns false\n     * if the query failed.\n     */\n    int image_id = getImageId(fileName);\n\n    bool res = false;\n\n    if(image_id > 0){\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            // Delete all labels for this image\n            query.prepare(\"DELETE FROM labels WHERE (image_id = :image_id)\");\n            query.bindValue(\":image_id\", image_id);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n\n            // Delete the image itself\n            query.prepare(\"DELETE FROM images WHERE (image_id = :image_id)\");\n            query.bindValue(\":image_id\", image_id);\n\n            res = query.exec();\n\n            if(!res){\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n\n    }\n\n    return res;\n}\n\nbool LabelProject::updateLabel(QString fileName, int labelID, BoundingBox new_bbox)\n{\n    /*!\n     * Update a label given an absolute path (\\a fileName), the old label ID (\\a labelID) \n     * and a new bounding box (\\a new_bbox). Returns false if the query failed.\n     */\n\n    QMutexLocker locker(&mutex);\n\n    bool res = false;\n\n    int image_id = getImageId(fileName);\n\n    {\n        auto db = getDatabase();\n        db.transaction();\n\n        if (labelID > 0 && image_id > 0 && new_bbox.label_id > 0\n            && new_bbox.rect.width() * new_bbox.rect.height() > 0) {\n            QSqlQuery query(db);\n\n            query.prepare(\"UPDATE labels SET  width = (:width),\"\n                          \" height = (:height), \"\n                          \" class_id = (:class_id), \"\n                          \" x = (:x), \"\n                          \" y = (:y) \"\n                          \" WHERE labels.label_id = (:label_id)\");\n\n            query.bindValue(\":class_id\", new_bbox.classid);\n            query.bindValue(\":x\", new_bbox.rect.x());\n            query.bindValue(\":y\", new_bbox.rect.y());\n            query.bindValue(\":width\", new_bbox.rect.width());\n            query.bindValue(\":height\", new_bbox.rect.height());\n            query.bindValue(\":label_id\", labelID);\n\n            res = query.exec();\n\n            if (!res) {\n                qCritical() << \"Error updating label: \" << query.lastError();\n            }\n        }\n\n        db.commit();\n\n        return res;\n    }\n}\n\nbool LabelProject::addLabel(QString fileName, BoundingBox bbox)\n{\n    /*!\n     * Add a label given an absolute path (\\a fileName) and the bounding box (\\a bbox). Returns false\n     * if the query failed.\n     */\n\n    QMutexLocker locker(&mutex);\n\n    bool res = false;\n\n    int image_id = getImageId(fileName);\n    int class_id = -1;\n\n    if (bbox.classname != \"\") {\n        class_id = getClassId(bbox.classname);\n    } else if (bbox.classid > 0) {\n        class_id = bbox.classid;\n    }\n\n    if (image_id > 0 && class_id > 0 && bbox.rect.width() * bbox.rect.height() > 0) {\n        {\n            auto db = getDatabase();\n            QSqlQuery query(db);\n\n            query.prepare(\"INSERT INTO labels (image_id, class_id, x, y, width, height)\"\n                          \"VALUES (:image_id, :class_id, :x, :y, :width, :height)\");\n            query.bindValue(\":image_id\", image_id);\n            query.bindValue(\":class_id\", class_id);\n            query.bindValue(\":x\", bbox.rect.x());\n            query.bindValue(\":y\", bbox.rect.y());\n            query.bindValue(\":width\", bbox.rect.width());\n            query.bindValue(\":height\", bbox.rect.height());\n            res = query.exec();\n\n            if (!res) {\n                qCritical() << \"Error: \" << query.lastError();\n            }\n        }\n    }\n\n    return res;\n}\n\nbool LabelProject::addLabel(QString fileName, QList<BoundingBox> bboxes)\n{\n    /*!\n     * Add a label given an absolute path (\\a fileName) and the bounding box (\\a bbox). Returns false\n     * if the query failed.\n     */\n\n    QMutexLocker locker(&mutex);\n\n    bool res = false;\n\n    int image_id = getImageId(fileName);\n    int class_id = -1;\n\n    {\n        auto db = getDatabase();\n        db.transaction();\n\n        for (auto bbox : bboxes) {\n            if (bbox.classname != \"\") {\n                class_id = getClassId(bbox.classname);\n            } else if (bbox.classid > 0) {\n                class_id = bbox.classid;\n            }\n\n            if (image_id > 0 && class_id > 0 && bbox.rect.width() * bbox.rect.height() > 0) {\n                QSqlQuery query(db);\n\n                query.prepare(\"INSERT INTO labels (image_id, class_id, x, y, width, height)\"\n                              \"VALUES (:image_id, :class_id, :x, :y, :width, :height)\");\n                query.bindValue(\":image_id\", image_id);\n                query.bindValue(\":class_id\", class_id);\n                query.bindValue(\":x\", bbox.rect.x());\n                query.bindValue(\":y\", bbox.rect.y());\n                query.bindValue(\":width\", bbox.rect.width());\n                query.bindValue(\":height\", bbox.rect.height());\n                res = query.exec();\n\n                if (!res) {\n                    qCritical() << \"Error: \" << query.lastError();\n                }\n            }\n        }\n\n        db.commit();\n    }\n\n    return res;\n}\n\nint LabelProject::addImageFolder(QString path)\n{\n    /*!\n     * Add all images in a folder given an absolute \\a path to the directory. Returns the number of images successfully added.\n     */\n    QDir dir(path);\n    int number_added = 0;\n\n    if (dir.exists()) {\n        QStringList filters;\n        filters << \"*.png\"\n                << \"*.jpg\"\n                << \"*.jpeg\"\n                << \"*.bmp\"\n                << \"*.tiff\";\n        auto image_list = dir.entryInfoList(filters, QDir::Files | QDir::NoDotAndDotDot);\n\n        QFileInfo image_info;\n\n        QSqlDatabase::database().transaction();\n\n        QProgressDialog progress(\"...\",\n                                 \"Abort\",\n                                 0,\n                                 image_list.size(),\n                                 static_cast<QWidget *>(parent()));\n        progress.setWindowModality(Qt::WindowModal);\n        progress.setWindowTitle(\"Loading images\");\n        int i = 0;\n\n        foreach (image_info, image_list) {\n            if (progress.wasCanceled()) {\n                break;\n            }\n\n            QString image_path = image_info.absoluteFilePath();\n\n            bool res = addAsset(image_path);\n\n            if (res) {\n                number_added++;\n                qDebug() << \"Added image: \" << image_path;\n            } else {\n                qCritical() << \"Failed to add image: \" << image_path;\n            }\n\n            progress.setValue(i++);\n            progress.setLabelText(image_path);\n            QApplication::processEvents();\n        }\n        QSqlDatabase::database().commit();\n    }\n\n    emit load_finished();\n\n    return number_added;\n}\n\nbool LabelProject::addLabelledAssets(QList<QString> images, QList<QList<BoundingBox>> bboxes)\n{\n    if (images.size() != bboxes.size())\n        return false;\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget *>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setWindowTitle(\"Loading into database\");\n\n    for (int i = 0; i < images.size(); ++i) {\n        if (progress.wasCanceled())\n            break;\n\n        auto image = images[i];\n        if (addAsset(image)) {\n            addLabel(image, bboxes[i]);\n        }\n\n        progress.setValue(i);\n        progress.setLabelText(QString(\"%1/%2: %3\").arg(i).arg(images.size()).arg(image));\n    }\n\n    return true;\n}\n\nbool LabelProject::addClass(QString className)\n{\n    /*!\n     * Add a class with name \\a className. Returns false\n     * if the query failed.\n     */\n    bool res = false;\n\n    // Make sure we strip any extraneous characters\n    className = className.simplified();\n\n    if (classInDB(className)) {\n        qWarning() << \"Class \" << className << \"exists!\";\n        return true;\n    }\n\n    {\n        auto db = getDatabase();\n        QSqlQuery query(db);\n        query.prepare(\"INSERT INTO classes (name)\"\n                      \"VALUES (:name)\");\n        query.bindValue(\":name\", className);\n        res = query.exec();\n\n        if (!res) {\n            qCritical() << \"Error: \" << query.lastError();\n        }\n    }\n\n    return res;\n}\n\nQSqlDatabase LabelProject::getDatabase()\n{\n    return QSqlDatabase::database(connection_name);\n}\n\nLabelProject::~LabelProject()\n{\n    /*!\n     * Destructor, closes the database if it's open.\n     */\n\n    QSqlDatabase::removeDatabase(connection_name);\n\n    emit finished();\n}\n"
  },
  {
    "path": "src/labelproject.h",
    "content": "#ifndef LABELPROJECT_H\n#define LABELPROJECT_H\n\n#include <QObject>\n#include <QApplication>\n#include <QDir>\n#include <QDirIterator>\n\n#include <QSqlDatabase>\n#include <QSqlQuery>\n#include <QSqlRecord>\n#include <QSqlError>\n#include <QRegExp>\n#include <QMutex>\n#include <QMutexLocker>\n#include <QThread>\n#include <QProgressDialog>\n\n#include <QDebug>\n#include <QMessageBox>\n\n#include <boundingbox.h>\n#include <opencv2/opencv.hpp>\n\nclass LabelProject : public QObject\n{\n    Q_OBJECT\npublic:\n    explicit LabelProject(QObject *parent = nullptr);\n    ~LabelProject();\n    bool loadDatabase(QString fileName);\n    bool createDatabase(QString fileName);\n    bool addClass(QString className);\n    bool getClassList(QList<QString> &classes);\n    bool getClassCounts(QMap<int, int> &counts);\n    bool removeClass(QString className);\n    bool classInDB(QString classname);\n    bool addAsset(QString fileName);\n    bool addLabelledAssets(QList<QString> images, QList<QList<BoundingBox>> bboxes);\n    void addVideo(QString fileName, QString outputFolder, int frame_skip=1);\n    bool getImageList(QList<QString> &images, bool relative=false);\n    bool getLabelledImageList(QList<QString> &images, bool relative=false);\n    bool removeImage(QString fileName);\n    bool imageInDB(QString fileName);\n\n    bool addLabel(QString fileName, BoundingBox bbox);\n    bool addLabel(QString fileName, QList<BoundingBox> bbox);\n    bool getLabels(QString fileName, QList<BoundingBox> &bboxes);\n    bool getLabels(int imageId, QList<BoundingBox> &bboxes);\n    bool removeLabel(QString fileName, int labelID);\n    bool removeLabels(QString fileName);\n    bool updateLabel(QString fileName, int labelID, BoundingBox new_bbox);\n\n    bool setOccluded(QString fileName, BoundingBox bbox, int occluded);\n\n    int getNextUnlabelled(QString fileName);\n    int getNextInstance(QString fileName, QString className);\n\n    QDir getDbFolder(){\n            auto db = getDatabase();\n            return QDir(db.databaseName());\n    }\n\n    int getImageId(QString fileName);\n    int getClassId(QString className);\n    QString getClassName(int classID);\n    void assignThread(QThread* thread);\n\nsignals:\n    void finished();\n    bool labelUpdated(BoundingBox selected_bbox, BoundingBox new_bbox);\n    void video_split_finished(QString);\n    void load_finished();\n    void load_progress(int);\n\npublic slots:\n    int addImageFolder(QString path);\n    void cancelLoad();\n    void addFolderRecursive(QString path_filter);\nprivate:\n    QMutex mutex;\n    bool checkDatabase();\n    bool should_cancel;\n    QString connection_name;\n    QSqlDatabase getDatabase();\n};\n\n#endif // LABELPROJECT_H\n"
  },
  {
    "path": "src/labelrefinedialog.cpp",
    "content": "#include \"labelrefinedialog.h\"\n#include \"ui_labelrefinedialog.h\"\n\nLabelRefineDialog::LabelRefineDialog(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::LabelRefineDialog)\n{\n    ui->setupUi(this);\n}\n\nLabelRefineDialog::~LabelRefineDialog()\n{\n    delete ui;\n}\n"
  },
  {
    "path": "src/labelrefinedialog.h",
    "content": "#ifndef LABELREFINEDIALOG_H\n#define LABELREFINEDIALOG_H\n\n#include <QDialog>\n\nnamespace Ui {\nclass LabelRefineDialog;\n}\n\nclass LabelRefineDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit LabelRefineDialog(QWidget *parent = nullptr);\n    ~LabelRefineDialog();\n\nprivate:\n    Ui::LabelRefineDialog *ui;\n};\n\n#endif // LABELREFINEDIALOG_H\n"
  },
  {
    "path": "src/labelrefinedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>LabelRefineDialog</class>\n <widget class=\"QDialog\" name=\"LabelRefineDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>884</width>\n    <height>533</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n   <item row=\"0\" column=\"0\">\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <item row=\"0\" column=\"0\" rowspan=\"4\">\n      <widget class=\"QLabel\" name=\"imageCropLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"styleSheet\">\n        <string notr=\"true\">background:#eeeeee</string>\n       </property>\n       <property name=\"text\">\n        <string>TextLabel</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\" rowspan=\"2\">\n      <widget class=\"QGroupBox\" name=\"groupBox\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"title\">\n        <string>Box Properties</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n        <item>\n         <layout class=\"QFormLayout\" name=\"formLayout\">\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"classComboBox\"/>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label\">\n            <property name=\"text\">\n             <string>Class:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"topSpinBox\">\n            <property name=\"suffix\">\n             <string> px</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"6\" column=\"1\">\n           <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"standardButtons\">\n             <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"leftSpinBox\">\n            <property name=\"suffix\">\n             <string> px</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"rightSpinBox\">\n            <property name=\"suffix\">\n             <string> px</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"bottomSpinbox\">\n            <property name=\"suffix\">\n             <string> px</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_2\">\n            <property name=\"text\">\n             <string>Left</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_3\">\n            <property name=\"text\">\n             <string>Bottom</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_4\">\n            <property name=\"text\">\n             <string>Right</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_5\">\n            <property name=\"text\">\n             <string>Top</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"spinBox\">\n            <property name=\"suffix\">\n             <string> px</string>\n            </property>\n            <property name=\"value\">\n             <number>20</number>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_6\">\n            <property name=\"text\">\n             <string>Display padding</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"7\" column=\"1\">\n           <spacer name=\"verticalSpacer\">\n            <property name=\"orientation\">\n             <enum>Qt::Vertical</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>20</width>\n              <height>40</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <spacer name=\"verticalSpacer_3\">\n          <property name=\"orientation\">\n           <enum>Qt::Vertical</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>20</width>\n            <height>40</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QGroupBox\" name=\"groupBox_2\">\n       <property name=\"title\">\n        <string>Image / Box</string>\n       </property>\n       <layout class=\"QFormLayout\" name=\"formLayout_2\">\n        <item row=\"3\" column=\"0\">\n         <widget class=\"QLabel\" name=\"label_7\">\n          <property name=\"text\">\n           <string>Image ID</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"3\" column=\"1\">\n         <widget class=\"QSpinBox\" name=\"imageIdSpinBox\">\n          <property name=\"suffix\">\n           <string> / 0</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"5\" column=\"0\">\n         <widget class=\"QLabel\" name=\"label_8\">\n          <property name=\"text\">\n           <string>Label ID</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"5\" column=\"1\">\n         <widget class=\"QSpinBox\" name=\"labelIdSpinBox\">\n          <property name=\"suffix\">\n           <string> / 0</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"6\" column=\"1\">\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n          <item>\n           <widget class=\"QPushButton\" name=\"previousLabelPushButton\">\n            <property name=\"text\">\n             <string>Previous</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"nextLabelPushButton\">\n            <property name=\"text\">\n             <string>Next</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item row=\"8\" column=\"1\">\n         <spacer name=\"verticalSpacer_2\">\n          <property name=\"orientation\">\n           <enum>Qt::Vertical</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>20</width>\n            <height>40</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item row=\"4\" column=\"1\">\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n          <item>\n           <widget class=\"QPushButton\" name=\"previousImagePushButton\">\n            <property name=\"text\">\n             <string>Previous</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"nextImagePushButton\">\n            <property name=\"text\">\n             <string>Next</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item row=\"0\" column=\"0\" colspan=\"2\">\n         <widget class=\"QProgressBar\" name=\"labelProgressBar\">\n          <property name=\"value\">\n           <number>24</number>\n          </property>\n         </widget>\n        </item>\n        <item row=\"1\" column=\"0\" colspan=\"2\">\n         <widget class=\"QLabel\" name=\"imageNameLabel\">\n          <property name=\"text\">\n           <string>Image Name</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>LabelRefineDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>LabelRefineDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/main.cpp",
    "content": "#include \"mainwindow.h\"\n#include \"cliparser.h\"\n#include <QApplication>\n\nint main(int argc, char *argv[])\n{\n    QCoreApplication::setApplicationName(\"deeplabel\");\n    QCoreApplication::setApplicationVersion(\"0.15\");\n\n    QApplication a(argc, argv);\n\n    if(argc == 1){\n        MainWindow w;\n        w.show();\n        return a.exec();\n    }else{\n        CliParser cli;\n        auto res = cli.Run();\n        if(!res){\n            return 1;\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/mainwindow.cpp",
    "content": "#include \"mainwindow.h\"\n#include \"ui_mainwindow.h\"\n\nMainWindow::MainWindow(QWidget *parent) :\n    QMainWindow(parent),\n    ui(new Ui::MainWindow)\n{\n    ui->setupUi(this);\n\n    connect(ui->actionNew_Project, SIGNAL(triggered(bool)), this, SLOT(newProject()));\n    connect(ui->actionOpen_Project, SIGNAL(triggered(bool)), this, SLOT(openProject()));\n    connect(ui->actionMerge_Project, SIGNAL(triggered(bool)), this, SLOT(mergeProject()));\n\n    connect(ui->actionAdd_video, SIGNAL(triggered(bool)), this, SLOT(addVideo()));\n    connect(ui->actionAdd_image, SIGNAL(triggered(bool)), this, SLOT(addImages()));\n    connect(ui->actionAdd_image_folder, SIGNAL(triggered(bool)), this, SLOT(addImageFolder()));\n    connect(ui->actionAdd_image_folders, SIGNAL(triggered(bool)), this, SLOT(addImageFolders()));\n\n    connect(ui->actionNextImage, SIGNAL(triggered(bool)), this, SLOT(nextImage()));\n    connect(ui->actionPreviousImage, SIGNAL(triggered(bool)), this, SLOT(previousImage()));\n    connect(ui->actionJump_forward, SIGNAL(triggered(bool)), this, SLOT(jumpForward()));\n    connect(ui->actionJump_backward, SIGNAL(triggered(bool)), this, SLOT(jumpBackward()));\n\n    connect(ui->addClassButton, SIGNAL(clicked(bool)), this, SLOT(addClass()));\n    connect(ui->newClassText, SIGNAL(editingFinished()), this, SLOT(addClass()));\n\n    connect(ui->actionInit_Tracking, SIGNAL(triggered(bool)), this, SLOT(initTrackers()));\n    connect(ui->actionPropagate_Tracking, SIGNAL(triggered(bool)), this, SLOT(updateTrackers()));\n    connect(ui->propagateCheckBox, SIGNAL(clicked(bool)), this, SLOT(toggleAutoPropagate(bool)));\n    connect(ui->refineTrackingCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleRefineTracking(bool)));\n\n    connect(ui->nextUnlabelledButton, SIGNAL(clicked(bool)), this, SLOT(nextUnlabelled()));\n    connect(ui->nextInstanceButton, SIGNAL(clicked(bool)), this, SLOT(nextInstance()));\n\n    display = new ImageDisplay;\n    ui->imageDisplayLayout->addWidget(display);\n    currentImage = display->getImageLabel();\n\n    connect(this, SIGNAL(selectedClass(QString)), currentImage, SLOT(setClassname(QString)));\n    connect(currentImage, SIGNAL(newLabel(BoundingBox)), this, SLOT(addLabel(BoundingBox)));\n    connect(currentImage, SIGNAL(removeLabel(BoundingBox)), this, SLOT(removeLabel(BoundingBox)));\n    connect(currentImage, SIGNAL(updateLabel(BoundingBox, BoundingBox)), this, SLOT(updateLabel(BoundingBox, BoundingBox)));\n    connect(currentImage, SIGNAL(setCurrentClass(QString)), this, SLOT(setCurrentClass(QString)));\n\n    connect(ui->actionDraw_Tool, SIGNAL(triggered(bool)), currentImage, SLOT(setDrawMode()));\n    connect(ui->actionSelect_Tool, SIGNAL(triggered(bool)), currentImage, SLOT(setSelectMode()));\n    connect(ui->classComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setCurrentClass(QString)));\n    connect(display, SIGNAL(image_loaded()), this, SLOT(updateImageInfo()));\n\n    connect(ui->removeClassButton, SIGNAL(clicked(bool)), this, SLOT(removeClass()));\n    connect(ui->removeImageButton, SIGNAL(clicked(bool)), this, SLOT(removeImage()));\n    connect(ui->removeImageLabelsButton, SIGNAL(clicked(bool)), this, SLOT(removeImageLabels()));\n    connect(ui->actionRemove_labels_forwards, SIGNAL(triggered(bool)), this, SLOT(removeImageLabelsForward()));\n\n    ui->actionDraw_Tool->setChecked(true);\n\n    connect(ui->changeImageButton, SIGNAL(clicked(bool)), this, SLOT(updateDisplay()));\n    connect(ui->imageNumberSpinbox, SIGNAL(editingFinished()), this, SLOT(updateDisplay()));\n\n    connect(ui->colourMapCombo, SIGNAL(currentIndexChanged(QString)), display, SLOT(setColourMap(QString)));\n    connect(ui->colourMapCheckbox, SIGNAL(clicked(bool)), display, SLOT(toggleColourMap(bool)));\n\n    connect(ui->actionWrap_images, SIGNAL(triggered(bool)), this, SLOT(enableWrap(bool)));\n    connect(ui->actionExport, SIGNAL(triggered(bool)), this, SLOT(launchExportDialog()));\n    connect(ui->actionImport_Labels, SIGNAL(triggered(bool)), this, SLOT(launchImportDialog()));\n    connect(ui->actionRefine_boxes, SIGNAL(triggered(bool)), this, SLOT(refineBoxes()));\n\n    connect(ui->actionSetup_detector, SIGNAL(triggered(bool)), this, SLOT(setupDetector()));\n    connect(ui->actionCalculate_histograms, SIGNAL(triggered(bool)), this, SLOT(computeStatistics()));\n\n    auto prev_shortcut = ui->actionPreviousImage->shortcuts();\n    prev_shortcut.append(QKeySequence(\"Left\"));\n    ui->actionPreviousImage->setShortcuts(prev_shortcut);\n\n    auto next_shortcut = ui->actionNextImage->shortcuts();\n    next_shortcut.append(QKeySequence(\"Right\"));\n    ui->actionNextImage->setShortcuts(next_shortcut);\n\n    // Override progress bar animation on Windows\n#ifdef WIN32\n    ui->imageProgressBar->setStyleSheet(\"QProgressBar::chunk {background-color: #3add36; width: 1px;}\");\n#endif\n\n    project = new LabelProject;\n\n    settings = new QSettings(\"DeepLabel\", \"DeepLabel\");\n\n    multitracker = new MultiTrackerCV();\n    reinterpret_cast<MultiTrackerCV *>(multitracker)->setTrackerType(CSRT);\n\n    QtAwesome* awesome = new QtAwesome(qApp);\n    awesome->initFontAwesome();\n\n    QVariantMap options;\n    options.insert( \"color\" , QColor(30,30,30) );\n    options.insert( \"scale-factor\", 0.7 );\n\n    ui->actionPreviousImage->setIcon(awesome->icon(fa::arrowleft, options));\n    ui->actionNextImage->setIcon(awesome->icon(fa::arrowright, options));\n    ui->actionSelect_Tool->setIcon(awesome->icon(fa::handpointero, options));\n    ui->actionDraw_Tool->setIcon(awesome->icon(fa::pencilsquareo, options));\n    ui->actionDetect_Objects->setIcon(awesome->icon(fa::magic, options));\n    ui->actionDetect_Objects->setEnabled(false);\n    connect(ui->actionDetect_Objects, SIGNAL(triggered(bool)), this, SLOT(detectCurrentImage()));\n    connect(ui->actionSet_threshold, SIGNAL(triggered(bool)), this, SLOT(setConfidenceThreshold()));\n    connect(ui->actionSet_NMS_threshold, SIGNAL(triggered(bool)), this, SLOT(setNMSThreshold()));\n    detector.setConfidenceThreshold(settings->value(\"detector_confidence\", 0.5).toDouble());\n    detector.setNMSThreshold(settings->value(\"detector_nms_threshold\", 0.4).toDouble());\n    connect(ui->actionDetect_project, SIGNAL(triggered(bool)), this, SLOT(detectProject()));\n    //ui->actionInit_Tracking->setIcon(awesome->icon(fa::objectungroup, options));\n\n    refine_range_dialog = new RefineRangeDialog(this);\n    connect(ui->actionRefine_image_range, SIGNAL(triggered(bool)), refine_range_dialog, SLOT(open()));\n    connect(refine_range_dialog, SIGNAL(accepted()), this, SLOT(handleRefineRange()));\n\n    resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5);\n\n}\n\nvoid MainWindow::mergeProject(QString filename){\n\n    if(filename == \"\"){\n        QString openDir = settings->value(\"project_folder\", QDir::homePath()).toString();\n        filename = QFileDialog::getOpenFileName(this, tr(\"Open Project\"),\n                                                        openDir,\n                                                        tr(\"Label database (*.lbldb)\"));\n    }\n\n    if(filename == \"\") return;\n\n    LabelProject new_project;\n    new_project.loadDatabase(filename);\n\n    // Add new classes\n    QList<QString> new_classes;\n    new_project.getClassList(new_classes);\n\n    qInfo() << \"Found \" << new_classes.size() << \" classes.\";\n\n    for(auto &classname : new_classes){\n        project->addClass(classname);\n    }\n\n    // Add new images\n    QList<QString> new_images;\n    new_project.getImageList(new_images);\n\n    qInfo() << \"Found \" << new_images.size() << \" images.\";\n\n    for(auto &image : new_images){\n        // Add image\n        auto res = project->addAsset(image);\n\n        if(!res){\n            qWarning() << \"Problem adding: \" << image;\n        }else{\n            qDebug() << \"Added: \" << image;\n        }\n\n        // Add labels for image\n        QList<BoundingBox> bboxes;\n        new_project.getLabels(image, bboxes);\n\n        for(auto &bbox : bboxes){\n            // Update the class ID\n            bbox.classid = project->getClassId(bbox.classname);\n            project->addLabel(image, bbox);\n        }\n    }\n\n    updateImageList();\n    updateClassList();\n    updateDisplay();\n}\n\nvoid MainWindow::setCurrentClass(QString name){\n\n    if(ui->classComboBox->currentText() != name){\n        ui->classComboBox->setCurrentText(name);\n    }\n\n    current_class = name;\n    emit selectedClass(current_class);\n}\n\nvoid MainWindow::setupDetector(void){\n\n    ui->actionDetect_Objects->setEnabled(false);\n    DetectorSetupDialog detection_dialog;\n    detection_dialog.exec();\n\n    if(detection_dialog.result() != QDialog::Accepted ) return;\n\n    auto names_file = detection_dialog.getNames().toStdString();\n    auto cfg_file = detection_dialog.getCfg().toStdString();\n    auto weight_file = detection_dialog.getWeights().toStdString();\n\n    detector.setChannels(detection_dialog.getChannels());\n    detector.setTarget(detection_dialog.getTarget());\n    detector.setFramework(detection_dialog.getFramework());\n    detector.setConvertGrayscale(detection_dialog.getConvertGrayscale());\n    detector.setConvertDepth(detection_dialog.getConvertDepth());\n    detector.setImageSize(detection_dialog.getWidth(), detection_dialog.getHeight());\n    detector.loadNetwork(names_file, cfg_file, weight_file);\n\n    ui->actionDetect_Objects->setEnabled(true);\n    ui->actionDetect_project->setEnabled(true);\n}\n\nvoid MainWindow::detectCurrentImage(){\n    auto image = display->getOriginalImage();\n    detectObjects(image, current_imagepath);\n\n    updateClassList();\n    updateLabels();\n}\n\nvoid MainWindow::detectObjects(cv::Mat &image, QString image_path){\n\n    if(image.empty()) return;\n\n    auto new_boxes = detector.infer(image);\n\n    QList<BoundingBox> existing_boxes;\n    project->getLabels(image_path, existing_boxes);\n\n    for(auto &box : new_boxes){\n        if(!project->classInDB(box.classname)){\n            project->addClass(box.classname);\n        }\n\n        // Strip out boxes which are already in the image\n        // assume detector is deterministic\n        bool exists = false;\n        for(auto &existing : existing_boxes){\n            if(existing.rect == box.rect && existing.classname == box.classname){\n                exists = true;\n            }\n        }\n\n        if(!exists){\n            qDebug() << \"Adding label\";\n            project->addLabel(image_path, box);\n        }\n\n    }\n\n}\n\nvoid MainWindow::setConfidenceThreshold(void){\n    QDialog  confidence_set_dialog(this);\n\n    auto threshold_spinbox = new QDoubleSpinBox();\n    threshold_spinbox->setMinimum(0);\n    threshold_spinbox->setMaximum(1);\n    threshold_spinbox->setValue(detector.getConfidenceThreshold());\n\n    auto threshold_label = new QLabel(\"Detection Threshold: \");\n\n    auto ok_button = new QPushButton(\"Ok\");\n\n    confidence_set_dialog.setWindowTitle(\"Confidence Threshold\");\n    confidence_set_dialog.setLayout(new QVBoxLayout());\n    confidence_set_dialog.layout()->addWidget(threshold_label);\n    confidence_set_dialog.layout()->addWidget(threshold_spinbox);\n    confidence_set_dialog.layout()->addWidget(ok_button);\n    confidence_set_dialog.layout()->setSizeConstraint(QLayout::SetFixedSize);\n\n    connect(ok_button, SIGNAL(clicked(bool)), &confidence_set_dialog, SLOT(accept()));\n\n    confidence_set_dialog.exec();\n\n    if(confidence_set_dialog.result() == QDialog::Accepted){\n        detector.setConfidenceThreshold(threshold_spinbox->value());\n        settings->setValue(\"detector_confidence\", detector.getConfidenceThreshold());\n    }\n}\n\nvoid MainWindow::setNMSThreshold(void){\n    QDialog  confidence_set_dialog(this);\n\n    auto threshold_spinbox = new QDoubleSpinBox();\n    threshold_spinbox->setMinimum(0);\n    threshold_spinbox->setMaximum(1);\n    threshold_spinbox->setValue(detector.getNMSThreshold());\n\n    auto threshold_label = new QLabel(\"NMS Threshold: \");\n\n    auto ok_button = new QPushButton(\"Ok\");\n\n    confidence_set_dialog.setWindowTitle(\"NMS Threshold\");\n    confidence_set_dialog.setLayout(new QVBoxLayout());\n    confidence_set_dialog.layout()->addWidget(threshold_label);\n    confidence_set_dialog.layout()->addWidget(threshold_spinbox);\n    confidence_set_dialog.layout()->addWidget(ok_button);\n    confidence_set_dialog.layout()->setSizeConstraint(QLayout::SetFixedSize);\n\n    connect(ok_button, SIGNAL(clicked(bool)), &confidence_set_dialog, SLOT(accept()));\n\n    confidence_set_dialog.exec();\n\n    if(confidence_set_dialog.result() == QDialog::Accepted){\n        detector.setNMSThreshold(threshold_spinbox->value());\n        settings->setValue(\"detector_nms_threshold\", detector.getNMSThreshold());\n    }\n}\n\nvoid MainWindow::detectProject(void){\n\n    QProgressDialog progress(\"Running detector\", \"Abort\", 0, images.size(), this);\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setLabelText(\"...\");\n\n    int i = 0;\n    for(auto& image_path : images){\n\n        progress.setLabelText(image_path);\n\n        if (progress.wasCanceled())\n            break;\n\n        auto image = cv::imread(image_path.toStdString(), cv::IMREAD_UNCHANGED|cv::IMREAD_ANYDEPTH);\n\n        // Assume we have an alpha image if 4 channels\n        if(image.channels() == 4){\n            cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);\n        }\n\n        detectObjects(image, image_path);\n\n        progress.setValue(i++);\n\n    }\n    updateClassList();\n    updateLabels();\n}\n\nvoid MainWindow::toggleAutoPropagate(bool state){\n    track_previous = state;\n}\n\nvoid MainWindow::toggleRefineTracking(bool state){\n    refine_on_propagate = state;\n}\n\nvoid MainWindow::enableWrap(bool enable){\n    wrap_index = enable;\n}\n\nvoid MainWindow::jumpForward(int n){\n\n    if(ui->imageNumberSpinbox->maximum() == 0) return;\n\n    current_index = std::min(ui->imageNumberSpinbox->maximum()-1, ui->imageNumberSpinbox->value()+n);\n    ui->imageNumberSpinbox->setValue(current_index);\n    updateDisplay();\n}\n\nvoid MainWindow::jumpBackward(int n){\n\n    current_index = std::max(1, ui->imageNumberSpinbox->value()-n);\n    ui->imageNumberSpinbox->setValue(current_index);\n    updateDisplay();\n}\n\nvoid MainWindow::setDrawMode(){\n    ui->actionDraw_Tool->setChecked(true);\n    ui->actionSelect_Tool->setChecked(false);\n    currentImage->setDrawMode();\n}\n\nvoid MainWindow::setSelectMode(){\n    ui->actionDraw_Tool->setChecked(false);\n    ui->actionSelect_Tool->setChecked(true);\n    currentImage->setSelectMode();\n}\n\nvoid MainWindow::openProject(QString fileName)\n{\n\n    if(fileName == \"\"){\n        QString openDir = settings->value(\"project_folder\", QDir::homePath()).toString();\n        fileName = QFileDialog::getOpenFileName(this, tr(\"Open Project\"),\n                                                        openDir,\n                                                        tr(\"Label database (*.lbldb)\"));\n    }\n\n    if(fileName != \"\"){\n        settings->setValue(\"project_folder\", QFileInfo(fileName).absoluteDir().absolutePath());\n        if(project->loadDatabase(fileName)){\n            initDisplay();\n            ui->menuImages->setEnabled(true);\n            ui->menuDetection->setEnabled(true);\n            ui->menuLabeling->setEnabled(true);\n            ui->menuNavigation->setEnabled(true);\n            ui->mainToolBar->setEnabled(true);\n            ui->actionImport_Labels->setEnabled(true);\n            ui->actionMerge_Project->setEnabled(true);\n            setWindowTitle(\"DeepLabel - \" + fileName);\n        }else{\n            QMessageBox::warning(this,tr(\"Project file error\"), tr(\"Failed to open project.\"));\n            setWindowTitle(\"DeepLabel\");\n        }\n    }\n\n    return;\n}\n\nvoid MainWindow::updateLabels(){\n    QList<BoundingBox> bboxes;\n    project->getLabels(current_imagepath, bboxes);\n\n    ui->instanceCountLabel->setNum(static_cast<int>(bboxes.size()));\n    currentImage->setBoundingBoxes(bboxes);\n}\n\nvoid MainWindow::addImageFolders(void){\n\n    QDialog  image_folder_dialog(this);\n\n    auto path_edit = new QLineEdit();\n    auto ok_button = new QPushButton(\"Ok\");\n    auto path_label = new QLabel(\"Folder path (wildcards allowed)\");\n\n    image_folder_dialog.setWindowTitle(\"Add folders\");\n    image_folder_dialog.setLayout(new QVBoxLayout());\n    image_folder_dialog.layout()->addWidget(path_label);\n    image_folder_dialog.layout()->addWidget(path_edit);\n    image_folder_dialog.layout()->addWidget(ok_button);\n\n    connect(ok_button, SIGNAL(clicked(bool)), &image_folder_dialog, SLOT(accept()));\n\n    image_folder_dialog.exec();\n\n    if(image_folder_dialog.result() == QDialog::Accepted){\n        project->addFolderRecursive(path_edit->text());\n    }\n}\n\nvoid MainWindow::updateImageList(){\n    project->getImageList(images);\n    number_images = images.size();\n\n    if(number_images == 0){\n        ui->imageGroupBox->setDisabled(true);\n        ui->labelGroupBox->setDisabled(true);\n        ui->navigationGroupBox->setDisabled(true);\n        ui->actionExport->setDisabled(true);\n        ui->imageIndexLabel->setText(QString(\"-\"));\n    }else{\n        ui->imageGroupBox->setEnabled(true);\n        ui->labelGroupBox->setEnabled(true);\n        ui->navigationGroupBox->setEnabled(true);\n        ui->actionExport->setEnabled(true);\n    }\n\n    ui->imageProgressBar->setValue(0);\n    ui->imageProgressBar->setMaximum(number_images);\n\n    ui->imageNumberSpinbox->setMaximum(number_images);\n    ui->imageNumberSpinbox->setValue(1);\n\n    refine_range_dialog->setMaxImage(number_images);\n}\n\nvoid MainWindow::updateClassList(){\n    project->getClassList(classes);\n\n    ui->classComboBox->clear();\n\n    QString classname;\n    foreach(classname, classes){\n        if(classname != \"\")\n            ui->classComboBox->addItem(classname);\n    }\n\n    if(classes.size() > 0){\n        ui->classComboBox->setEnabled(true);\n        ui->removeClassButton->setEnabled(true);\n        ui->classComboBox->setCurrentIndex(0);\n    }else{\n        ui->classComboBox->setDisabled(true);\n        ui->removeClassButton->setDisabled(true);\n    }\n}\n\nvoid MainWindow::addClass(){\n    QString new_class = ui->newClassText->text();\n\n    if(new_class.simplified() != \"\" && !classes.contains(new_class)){\n        project->addClass(new_class.simplified());\n        ui->newClassText->clear();\n        updateClassList();\n        setCurrentClass(new_class.simplified());\n    }\n}\n\nvoid MainWindow::addLabel(BoundingBox bbox){\n    project->addLabel(current_imagepath, bbox);\n    updateLabels();\n}\n\nvoid MainWindow::removeLabel(BoundingBox bbox){\n    project->removeLabel(current_imagepath, bbox.label_id);\n    updateLabels();\n}\n\nvoid MainWindow::removeImageLabels(){\n    if (QMessageBox::Yes == QMessageBox::question(this,\n                                                  tr(\"Remove Labels\"),\n                                                  QString(\"Really delete all labels for this image?\"))){;\n        project->removeLabels(current_imagepath);\n        updateLabels();\n        updateDisplay();\n    }\n}\n\nvoid MainWindow::removeImageLabelsForward(){\n    if (QMessageBox::Yes == QMessageBox::question(this,\n                                                  tr(\"Remove Labels\"),\n                                                  QString(\"Really delete all labels forwards?\"))){;\n\n        while(true){\n\n            QList<BoundingBox> bboxes;\n            project->getLabels(current_imagepath, bboxes);\n\n            if(bboxes.size() == 0){\n                return;\n            }\n\n            if(current_index == (number_images-1)){\n                    return;\n            }else{\n                current_index++;\n            }\n\n            ui->imageNumberSpinbox->setValue(current_index+1);\n            project->removeLabels(current_imagepath);\n\n            // Show the new image\n            updateLabels();\n            updateDisplay();\n        }\n\n    }\n}\n\nvoid MainWindow::updateLabel(BoundingBox old_bbox, BoundingBox new_bbox)\n{\n    project->updateLabel(current_imagepath, old_bbox.label_id, new_bbox);\n    updateLabels();\n}\n\nvoid MainWindow::removeImage(){\n    if (QMessageBox::Yes == QMessageBox::question(this,\n                                                  tr(\"Remove Image\"),\n                                                  tr(\"Really delete image and associated labels?\"))){\n        auto previousProgressValue = ui->imageProgressBar->value();\n        auto previousSpinboxValue = ui->imageNumberSpinbox->value();\n        project->removeImage(current_imagepath);\n        updateImageList();\n        ui->imageProgressBar->setValue(MIN(previousProgressValue, ui->imageProgressBar->maximum()));\n        ui->imageNumberSpinbox->setValue(MIN(previousSpinboxValue, ui->imageNumberSpinbox->maximum()));\n        updateDisplay();\n    }\n}\n\nvoid MainWindow::removeClass(){\n\n    if (QMessageBox::Yes == QMessageBox::question(this,\n                                                  tr(\"Remove Class\"),\n                                                  QString(\"Really delete all \\\"%1\\\" labels from your entire dataset?\")\n                                                    .arg(current_class))){;\n        project->removeClass(current_class);\n        updateClassList();\n        updateDisplay();\n    }\n}\n\nvoid MainWindow::initDisplay(){\n\n    display->clearPixmap();\n\n    updateImageList();\n    updateClassList();\n\n    current_index = 0;\n\n    updateDisplay();\n}\n\nvoid MainWindow::nextUnlabelled(){\n    int n = project->getNextUnlabelled(current_imagepath);\n\n    if(n != -1){\n        ui->imageNumberSpinbox->setValue(n);\n        updateDisplay();\n    }\n}\n\nvoid MainWindow::nextInstance(void){\n    int n = project->getNextInstance(current_imagepath, current_class);\n\n    if(n != -1){\n        ui->imageNumberSpinbox->setValue(n+1);\n        updateDisplay();\n    }\n}\n\nQRect MainWindow::refineBoundingBoxSimple(cv::Mat image, QRect bbox, int margin, bool debug_save){\n    QMargins margins(margin, margin, margin, margin);\n    bbox += margins;\n\n    // Clamp to within image - note zero-indexed so boundary is width-1 etc.\n\n    bbox.setTop(std::max(bbox.top(), 0));\n    bbox.setBottom(std::min(bbox.bottom(),image.rows-1));\n    bbox.setLeft(std::max(bbox.left(), 0));\n    bbox.setRight(std::min(bbox.right(), image.cols-1));\n\n    auto roi = image(qrect2cv(bbox));\n\n    // First we need to do foreground/background segmentation\n\n    // Threshold input, 1 == good\n    cv::Mat roi_thresh;\n\n    // Convert colour images to grayscale for thresholding\n    if(roi.channels() == 4){\n        cv::cvtColor(roi, roi, cv::COLOR_BGRA2GRAY);\n    }\n    else if(roi.channels() == 3){\n        cv::cvtColor(roi, roi, cv::COLOR_BGR2GRAY);\n    }\n\n    cv::threshold(roi, roi_thresh, 0, 255, cv::THRESH_OTSU|cv::THRESH_BINARY);\n\n    if(debug_save) cv::imwrite(\"roi.png\", roi);\n    if(debug_save) cv::imwrite(\"roi_thresh.png\", roi_thresh);\n\n    std::vector<std::vector<cv::Point>> contours;\n    cv::Mat hierarchy;\n    cv::findContours(roi_thresh, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);\n\n    for(auto &contour : contours){\n\n        if(static_cast<int>(cv::contourArea(contour)) == roi.rows*roi.cols){\n            qDebug() << \"Contour encloses all!\";\n        }\n\n        //cv::drawContours(markers, {contour}, 0, {0,0,255});\n        auto contour_bound = cv::boundingRect(contour);\n        cv::rectangle(roi, contour_bound, {0,0,255});\n    }\n\n    if(debug_save) cv::imwrite(\"roi_contours.png\", roi);\n\n    QRect new_box;\n\n    if(contours.size() > 0){\n        auto contour_bound = cv::boundingRect(contours.at(0));\n        new_box.setX(bbox.x()+contour_bound.x);\n        new_box.setY(bbox.y()+contour_bound.y);\n        new_box.setWidth(contour_bound.width);\n        new_box.setHeight(contour_bound.height);\n    }\n\n    return new_box;\n}\n\nQRect MainWindow::refineBoundingBox(cv::Mat image, QRect bbox, int margin, bool debug_save){\n    // Simple connected components refinement - debug only for now.\n\n    QMargins margins(margin, margin, margin, margin);\n    bbox += margins;\n\n    bbox.setTop(std::max(0, std::min(image.rows, bbox.top())));\n    bbox.setBottom(std::max(0, std::min(image.rows, bbox.top())));\n    bbox.setLeft(std::max(0, std::min(image.cols, bbox.left())));\n    bbox.setRight(std::max(0, std::min(image.cols, bbox.right())));\n\n    auto roi = image(qrect2cv(bbox));\n\n    // First we need to do foreground/background segmentation\n\n    // Threshold input, 1 == good\n    cv::Mat roi_thresh;\n    cv::threshold(roi, roi_thresh, 0, 255, cv::THRESH_OTSU|cv::THRESH_BINARY);\n\n    if(debug_save) cv::imwrite(\"roi.png\", roi);\n    if(debug_save) cv::imwrite(\"roi_thresh.png\", roi_thresh);\n\n    auto kernel_size = cv::Size(3,3);\n    int iterations = 2;\n    auto anchor = cv::Point(-1,-1);\n    auto structure = cv::getStructuringElement(cv::MORPH_RECT, kernel_size);\n\n    cv::Mat opening;\n    cv::morphologyEx(roi_thresh, opening, cv::MORPH_OPEN, structure, anchor, iterations);\n\n    // Background area\n    iterations = 3;\n    cv::Mat background;\n    cv::dilate(opening, background, structure, anchor, iterations);\n\n    if(debug_save) cv::imwrite(\"background.png\", background);\n\n    // Foreground area\n    cv::Mat dist_transform;\n    cv::Mat dist_labels;\n    int mask_size = 5;\n    cv::distanceTransform(opening, dist_transform, dist_labels, cv::DIST_L2, mask_size);\n\n    if(debug_save) cv::imwrite(\"distance.png\", dist_transform);\n\n    cv::Mat foreground;\n    double min_val, max_val;\n    cv::minMaxIdx(dist_transform, &min_val, &max_val);\n    int thresh = static_cast<int>(0.7*max_val);\n    cv::threshold(dist_transform, foreground, thresh, 255,\n                          cv::THRESH_BINARY);\n\n    foreground.convertTo(foreground, CV_8UC1);\n    if(debug_save) cv::imwrite(\"foreground.png\", foreground);\n\n\n    // Unknown region\n    cv::Mat unknown;\n    cv::subtract(background, foreground, unknown, cv::noArray(), CV_8UC1);\n\n    if(debug_save) cv::imwrite(\"unknown.png\", unknown);\n\n    cv::Mat markers;\n    cv::connectedComponents(foreground, markers, 8, CV_32SC1);\n    markers += 1;\n\n    int region_id = markers.at<int>(cv::Point(markers.cols/2, markers.rows/2));\n    qDebug() << region_id;\n\n    for(int i=0; i < static_cast<int>(markers.total()); i++){\n        if(unknown.at<uchar>(i) == 255){\n            markers.at<int>(i) = 0;\n        }\n    }\n\n    if(debug_save) cv::imwrite(\"markers.png\", markers);\n\n    if(roi.channels() == 1) cv::cvtColor(roi, roi, cv::COLOR_GRAY2BGR);\n    cv::watershed(roi, markers);\n\n    markers.convertTo(markers, CV_8UC1);\n    cv::threshold(markers, markers, 0, 255, cv::THRESH_OTSU);\n    if(debug_save) cv::imwrite(\"watershed.png\", markers);\n\n    std::vector<std::vector<cv::Point>> contours;\n    cv::Mat hierarchy;\n    cv::findContours(markers, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);\n\n    cv::cvtColor(markers, markers, cv::COLOR_GRAY2BGR);\n\n    cv::Rect contour_bound;\n    for(auto &contour : contours){\n\n        if(static_cast<int>(cv::contourArea(contour)) == roi.rows*roi.cols){\n            qDebug() << \"Contour encloses all!\";\n        }\n\n        //cv::drawContours(markers, {contour}, 0, {0,0,255});\n        contour_bound = cv::boundingRect(contour);\n        cv::rectangle(markers, contour_bound, {0,0,255});\n    }\n\n    if(debug_save) cv::imwrite(\"contours.png\", markers);\n    QRect new_box;\n\n    if(contours.size() == 1){\n        new_box.setX(bbox.x()+contour_bound.x);\n        new_box.setY(bbox.y()+contour_bound.y);\n        new_box.setWidth(contour_bound.width);\n        new_box.setHeight(contour_bound.height);\n    }\n\n    return new_box;\n}\n\nvoid MainWindow::refineBoxes(double min_new_area, double max_new_area){\n\n    auto bboxes = currentImage->getBoundingBoxes();\n    const auto image = currentImage->getImage();\n\n    for(auto &bbox : bboxes){\n        auto previous_area = bbox.rect.width()*bbox.rect.height();\n        auto updated = refineBoundingBoxSimple(image, bbox.rect, 5, false);\n\n        auto new_bbox = bbox;\n        new_bbox.rect = updated;\n        auto new_area = new_bbox.rect.width()*new_bbox.rect.height();\n\n        // Make sure that new bbox isn't changed\n        // too much\n        if(!updated.size().isEmpty()\n                && new_area >= min_new_area*previous_area\n                && new_area <= max_new_area*previous_area){\n            updateLabel(bbox, new_bbox);\n        }\n    }\n\n    updateLabels();\n\n}\n\nvoid MainWindow::initTrackers(void){\n    multitracker->init(currentImage->getImage(), currentImage->getBoundingBoxes());\n}\n\nvoid MainWindow::updateTrackers(void){\n\n    // If there are no labels, and we're tracking the previous frame\n    // propagate the bounding boxes. Otherwise we assume that the\n    // current labels are the correct ones and should override.\n\n    auto image = currentImage->getImage();\n    if(image.empty()) return;\n\n    multitracker->update(image);\n\n    auto new_bboxes = multitracker->getBoxes();\n\n    for(auto &new_bbox : new_bboxes){\n        project->addLabel(current_imagepath, new_bbox);\n    }\n\n    updateLabels();\n}\n\nvoid MainWindow::nextImage(){\n\n    if(images.empty()) return;\n\n    if(current_index == (number_images-1)){\n        if(wrap_index){\n            current_index = 0;\n        }else{\n            return;\n        }\n    }else{\n        current_index++;\n    }\n\n    ui->imageNumberSpinbox->setValue(current_index+1);\n\n    // Show the new image\n    updateDisplay();\n\n    // Only auto-propagtae if we've enabled it and there are no boxes in the image already.\n    if(track_previous && currentImage->getBoundingBoxes().size() == 0){\n        updateTrackers();\n\n        if(refine_on_propagate){\n            refineBoxes();\n        }\n    }\n\n    updateLabels();\n}\n\nvoid MainWindow::previousImage(){\n\n    if(images.empty()) return;\n\n    if(current_index == 0){\n        if(wrap_index){\n            current_index = number_images - 1;\n        }else{\n            return;\n        }\n    }else{\n      current_index--;\n    }\n\n    ui->imageNumberSpinbox->setValue(current_index+1);\n    updateDisplay();\n}\n\nvoid MainWindow::updateCurrentIndex(int index){\n    current_index = index;\n    ui->imageNumberSpinbox->setValue(current_index);\n    updateDisplay();\n}\n\nvoid MainWindow::updateDisplay(){\n\n    if(images.size() == 0){\n        return;\n    }else{\n        current_index = ui->imageNumberSpinbox->value()-1;\n        current_imagepath = images.at(current_index);\n        display->setImagePath(current_imagepath);\n\n        ui->statusBar->showMessage(current_imagepath);\n\n        updateLabels();\n\n        ui->imageProgressBar->setValue(current_index+1);\n        ui->imageIndexLabel->setText(QString(\"%1/%2\").arg(current_index+1).arg(number_images));\n    }\n}\n\nvoid MainWindow::updateImageInfo(void){\n    auto image_info = QFileInfo(current_imagepath);\n    ui->imageBitDepthLabel->setText(QString(\"%1 bit\").arg(display->getBitDepth()));\n    ui->filenameLabel->setText(image_info.fileName());\n    ui->filenameLabel->setToolTip(image_info.fileName());\n    ui->filetypeLabel->setText(image_info.completeSuffix());\n    ui->sizeLabel->setText(QString(\"%1 kB\").arg(image_info.size() / 1000));\n    ui->dimensionsLabel->setText(QString(\"(%1, %2) px\").arg(currentImage->getImage().cols).arg(currentImage->getImage().rows));\n}\n\nvoid MainWindow::newProject()\n{\n    QString openDir = settings->value(\"project_folder\", QDir::homePath()).toString();\n\n    QFileDialog dialog(this);\n    dialog.setDefaultSuffix(\".lbldb\");\n\n    QString fileName = dialog.getSaveFileName(this, tr(\"New Project\"),\n                                                    openDir,\n                                                    tr(\"Label database (*.lbldb)\"));\n\n    if(fileName != \"\"){\n        free(project);\n        project = new LabelProject;\n        project->createDatabase(fileName);\n        openProject(fileName);\n    }\n\n    return;\n}\n\nvoid MainWindow::addVideo(void){\n    QString openDir = QDir::homePath();\n    QString video_filename = QFileDialog::getOpenFileName(this, tr(\"Select video\"),\n                                                    openDir);\n\n    QString output_folder = QFileDialog::getExistingDirectory(this, \"Output folder\", openDir);\n\n    if(video_filename != \"\"){\n\n        auto dialog_box = new QDialog(this);\n\n        auto frame_skip_spin = new QSpinBox;\n        auto frame_skip_label = new QLabel;\n        auto layout = new QVBoxLayout;\n        auto accept_button = new QPushButton;\n\n        accept_button->setDefault(true);\n        accept_button->setText(\"OK\");\n        connect(accept_button, SIGNAL(clicked()), dialog_box, SLOT(accept()));\n\n        frame_skip_spin->setValue(1);\n        frame_skip_spin->setMinimum(1);\n        frame_skip_spin->setMaximum(1000);\n\n        frame_skip_label->setText(\"Skip frames (1 to use all): \");\n\n        dialog_box->setWindowModality(Qt::WindowModal);\n        dialog_box->setLayout(layout);\n        dialog_box->layout()->addWidget(frame_skip_label);\n        dialog_box->layout()->addWidget(frame_skip_spin);\n        dialog_box->layout()->addWidget(accept_button);\n\n        if(dialog_box->exec()){\n            qInfo() << \"Frame skip: \" << frame_skip_spin->value();\n            project->addVideo(video_filename, output_folder, frame_skip_spin->value());\n        }\n    }\n\n    updateImageList();\n    initDisplay();\n\n}\n\nvoid MainWindow::addImages(void){\n    QString openDir = settings->value(\"data_folder\", QDir::homePath()).toString();\n    QStringList image_filenames = QFileDialog::getOpenFileNames(this, tr(\"Select image(s)\"),\n                                                    openDir,\n                                                    tr(\"JPEG (*.jpg *.jpeg *.JPG *.JPEG);;PNG (*.png *.PNG);;BMP (*.bmp *.BMP);;TIFF (*.tif *.tiff *.TIF *.TIFF);;All images (*.jpg *.jpeg *.png *.bmp *.tiff)\"));\n\n    if(image_filenames.size() != 0){\n        QString path;\n\n        QProgressDialog progress(\"Loading images\", \"Abort\", 0, image_filenames.size(), this);\n        progress.setWindowModality(Qt::WindowModal);\n        int i=0;\n\n        foreach(path, image_filenames){\n            if(progress.wasCanceled()){\n                break;\n            }\n\n            project->addAsset(path);\n            progress.setValue(i++);\n        }\n\n        settings->setValue(\"data_folder\", QDir(image_filenames.at(0)).dirName());\n    }\n\n    updateImageList();\n    initDisplay();\n\n    return;\n}\n\nvoid MainWindow::addImageFolder(void){\n    QString openDir = settings->value(\"data_folder\", QDir::homePath()).toString();\n    QString path = QFileDialog::getExistingDirectory(this, tr(\"Select image folder\"),\n                                                    openDir);\n\n    if(path != \"\"){\n        int number_added = project->addImageFolder(path);\n        settings->setValue(\"data_folder\", path);\n        qInfo() << \"Added: \" << number_added << \" images\";\n    }\n\n    updateImageList();\n    initDisplay();\n\n    return;\n}\n\nvoid MainWindow::handleExportDialog(){\n\n    // If we hit OK and not cancel\n    if(export_dialog->result() != QDialog::Accepted ) return;\n\n    QThread* export_thread = new QThread;\n    BaseExporter* exporter = nullptr;\n\n    if(export_dialog->getExporter() == \"Kitti\"){\n        exporter = new KittiExporter(project);\n    }else if(export_dialog->getExporter() == \"Darknet\"){\n        exporter = new DarknetExporter(project);\n        static_cast<DarknetExporter*>(exporter)->generateLabelIds(export_dialog->getNamesFile());\n    }else if(export_dialog->getExporter() == \"Pascal VOC\"){\n        exporter = new PascalVocExporter(project);\n        static_cast<PascalVocExporter*>(exporter)->setExportMap(export_dialog->getCreateLabelMap());\n        exporter->process();\n    }else if(export_dialog->getExporter().startsWith(\"COCO\")){\n        exporter = new CocoExporter(project);\n    }else if(export_dialog->getExporter().startsWith(\"GCP\")){\n        exporter = new GCPExporter(project);\n        static_cast<GCPExporter*>(exporter)->setBucket(export_dialog->getBucket());\n    }else{\n        qCritical() << \"Invalid exporter type\";\n        return;\n    }\n\n    if(exporter != nullptr){\n        exporter->moveToThread(export_thread);\n        exporter->setExportUnlabelled(export_dialog->getExportUnlablled());\n        exporter->setFilenamePrefix(export_dialog->getFilePrefix());\n        exporter->setAppendLabels(export_dialog->getAppendLabels());\n\n        if(export_dialog->getValidationSplitEnabled()){\n            exporter->setValidationSplit(true);\n            exporter->splitData(export_dialog->getValidationSplit(), export_dialog->getShuffle());\n        }else{\n            exporter->setValidationSplit(false);\n            exporter->splitData(0, export_dialog->getShuffle());\n        }\n\n\n        exporter->setOutputFolder(export_dialog->getOutputFolder());\n        exporter->process();\n    }else{\n        qCritical() << \"Failed to instantiate exporter\";\n    }\n\n}\n\nvoid MainWindow::launchExportDialog(){\n\n    export_dialog = new ExportDialog(this);\n\n    export_dialog->setModal(true);\n    connect(export_dialog, SIGNAL(accepted()), this, SLOT(handleExportDialog()));\n\n    export_dialog->open();\n}\n\nvoid MainWindow::handleRefineRange(){\n\n    if(refine_range_dialog->result() != QDialog::Accepted ){\n        return;\n    }\n\n    refineRange(refine_range_dialog->getStart(), refine_range_dialog->getEnd());\n}\n\nvoid MainWindow::refineRange(int start, int end){\n\n    if(start == -1 || end == -1) return;\n\n    QList<QString> images;\n    project->getImageList(images);\n\n    QProgressDialog progress(\"Refining images\", \"Cancel\", 0, end-start, this);\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setLabelText(\"...\");\n    QApplication::processEvents(); // Otherwise stuff can happen a wee bit fast\n\n    for(int i = start; i < end; ++i){\n        if(progress.wasCanceled())\n            break;\n\n        progress.setLabelText(images[i]);\n\n        updateCurrentIndex(i);\n        refineBoxes();\n        nextImage();\n\n        progress.setValue(i);\n        QApplication::processEvents();\n\n    }\n}\n\nvoid MainWindow::computeStatistics(void){\n    QList<QString> images;\n    project->getImageList(images);\n    std::vector<cv::Mat> histograms;\n    histograms.resize(4);\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), this->parentWidget());\n    progress.setWindowModality(Qt::WindowModal);\n    progress.setWindowTitle(\"Calculating stats\");\n    progress.show();\n    int i = 0;\n\n    for(auto &image_path : images){\n        if(progress.wasCanceled())\n            break;\n\n        auto image = cv::imread(image_path.toStdString(), cv::IMREAD_UNCHANGED);\n        std::vector<cv::Mat> image_channels;\n        cv::split( image, image_channels );\n        int histSize = 65536;\n        float range[] = { 0, 65536 }; //the upper boundary is exclusive\n        const float* histRange = { range };\n        bool uniform = true, accumulate = true;\n\n        for(int c=0; c < static_cast<int>(image_channels.size()); ++c){\n            cv::calcHist( &image_channels[c], 1, 0, cv::Mat(), histograms[c], 1, &histSize, &histRange, uniform, accumulate );\n        }\n\n        progress.setValue(++i);\n        progress.setLabelText(image_path);\n\n        QApplication::processEvents();\n    }\n\n    for(auto &histogram : histograms){\n\n        double s = 0;\n        double total_hist = 0;\n\n        for(long long i=0; i < static_cast<long long>(histogram.total()); ++i){\n            s += histogram.at<float>(i) * (i + 0.5); // bin centre\n            total_hist += histogram.at<float>(i);\n        }\n\n        double mean = s / total_hist;\n\n        double t = 0;\n        for(long long i=0; i < static_cast<long long>(histogram.total()); ++i){\n          double x = (i - mean);\n          t += histogram.at<float>(i)*x*x;\n        }\n        double stdev = std::sqrt(t / total_hist);\n\n        qInfo() << \"Mean: \" << mean;\n        qInfo() << \"Std: \" << stdev;\n    }\n}\n\nvoid MainWindow::handleImportDialog(){\n\n    // If we hit OK and not cancel\n    if (import_dialog->result() != QDialog::Accepted)\n        return;\n\n    QThread* import_thread = new QThread;\n\n    if (import_dialog->getImporter() == \"Darknet\") {\n        DarknetImporter importer(project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_dialog->getImportUnlabelled());\n\n        if (import_dialog->getUseRelativePaths()) {\n            importer.import(import_dialog->getInputFile(),\n                            import_dialog->getNamesFile(),\n                            import_dialog->getRelativePath());\n        } else {\n            importer.import(import_dialog->getInputFile(), import_dialog->getNamesFile());\n        }\n\n    } else if (import_dialog->getImporter() == \"Coco\") {\n        CocoImporter importer(project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_dialog->getImportUnlabelled());\n        importer.import(import_dialog->getAnnotationFile(), import_dialog->getInputFile());\n    } else if (import_dialog->getImporter() == \"MOT\") {\n        MOTImporter importer(project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_dialog->getImportUnlabelled());\n        importer.loadClasses(import_dialog->getNamesFile());\n        importer.import(import_dialog->getInputFile());\n    } else if (import_dialog->getImporter() == \"BirdsAI\") {\n        BirdsAIImporter importer(project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_dialog->getImportUnlabelled());\n        importer.loadClasses(import_dialog->getNamesFile());\n        importer.import(import_dialog->getInputFile(), import_dialog->getAnnotationFile());\n    } else if (import_dialog->getImporter() == \"PascalVOC\") {\n        PascalVOCImporter importer(project);\n        importer.moveToThread(import_thread);\n        importer.setImportUnlabelled(import_dialog->getImportUnlabelled());\n        importer.import(import_dialog->getInputFile(), import_dialog->getAnnotationFile());\n    }\n\n    initDisplay();\n}\n\nvoid MainWindow::launchImportDialog(){\n\n    import_dialog = new ImportDialog(this);\n\n    import_dialog->setModal(true);\n    connect(import_dialog, SIGNAL(accepted()), this, SLOT(handleImportDialog()));\n\n    import_dialog->open();\n}\n\nMainWindow::~MainWindow()\n{\n    delete ui;\n}\n"
  },
  {
    "path": "src/mainwindow.h",
    "content": "#ifndef MAINWINDOW_H\n#define MAINWINDOW_H\n\n#include <QMainWindow>\n#include <QThread>\n#include <QFileDialog>\n#include <QDialog>\n#include <QDialogButtonBox>\n#include <QSettings>\n#include <QTemporaryDir>\n#include <QScrollArea>\n#include <QScreen>\n#include <QtConcurrent>\n#include <QProgressDialog>\n\n#include <opencv2/opencv.hpp>\n#include <imagelabel.h>\n#include <labelproject.h>\n#include <exporter.h>\n#include <importer.h>\n\n#include <detection/detectoropencv.h>\n#include <detection/detectorsetupdialog.h>\n\n#include <algorithm>\n#include <exportdialog.h>\n#include <importdialog.h>\n#include <refinerangedialog.h>\n#include <multitracker.h>\n#include <imagedisplay.h>\n\n#include <QtAwesome.h>\n\nnamespace Ui {\nclass MainWindow;\n}\n\nclass MainWindow : public QMainWindow\n{\n    Q_OBJECT\n\npublic:\n    explicit MainWindow(QWidget *parent = nullptr);\n    ~MainWindow();\n\nprivate:\n    Ui::MainWindow *ui;\n    LabelProject *project;\n    ImageLabel *currentImage;\n    ExportDialog *export_dialog;\n    ImportDialog *import_dialog;\n    RefineRangeDialog *refine_range_dialog;\n    MultiTracker *multitracker;\n    QScrollArea *imageScrollArea;\n    ImageDisplay *display;\n    DetectorOpenCV detector;\n\n    // Enable tracking boxes in previous frames\n    bool track_previous = false;\n    bool refine_on_propagate = false;\n    bool wrap_index;\n    int current_index;\n\n    void initDisplay();\n\n    QList<QString> images;\n    QList<QString> classes;\n    QString current_imagepath;\n    QString current_class;\n\n    int number_images;\n    void updateImageList();\n    void updateClassList();\n    void updateLabels();\n\n    QSettings* settings;\n\nprivate slots:\n\n    void updateDisplay(void);\n\n    void openProject(QString filename = \"\");\n    void mergeProject(QString filename = \"\");\n    void newProject(void);\n\n    void addClass(void);\n    void setCurrentClass(QString);\n    void removeClass(void);\n\n    void addVideo(void);\n    void addImages(void);\n    void addImageFolder(void);\n    void addImageFolders();\n    void nextImage(void);\n    void previousImage(void);\n    void removeImage(void);\n\n    void addLabel(BoundingBox bbox);\n    void removeLabel(BoundingBox bbox);\n    void updateLabel(BoundingBox old_bbox, BoundingBox new_bbox);\n    void removeImageLabels(void);\n    void removeImageLabelsForward();\n\n    void setDrawMode(void);\n    void setSelectMode(void);\n\n    void enableWrap(bool enable);\n    void launchExportDialog();\n    void handleExportDialog();\n    void launchImportDialog();\n    void handleImportDialog();\n\n    // Tracking\n    void initTrackers();\n    void updateTrackers();\n    void toggleAutoPropagate(bool state);\n    void toggleRefineTracking(bool state);\n    void nextUnlabelled();\n    void nextInstance();\n\n    QRect refineBoundingBox(cv::Mat image, QRect bbox, int margin=5, bool debug_save=false);\n    QRect refineBoundingBoxSimple(cv::Mat image, QRect bbox, int margin=5, bool debug_save=false);\n    void refineBoxes(double min_new_area = 0.5, double max_new_area = 1.5);\n\n    void updateImageInfo();\n    void jumpForward(int n = 10);\n    void jumpBackward(int n = 10);\n\n    void detectObjects(cv::Mat &image, QString image_path);\n    void detectCurrentImage();\n    void detectProject();\n    void setupDetector();\n    void setConfidenceThreshold();\n    void setNMSThreshold();\n    void computeStatistics();\n\n    void updateCurrentIndex(int index);\n    void refineRange(int start, int end);\n    void handleRefineRange();\nsignals:\n    void selectedClass(QString);\n\n};\n\n#endif // MAINWINDOW_H\n"
  },
  {
    "path": "src/mainwindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>931</width>\n    <height>698</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>DeepLabel</string>\n  </property>\n  <widget class=\"QWidget\" name=\"centralWidget\">\n   <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n    <item>\n     <layout class=\"QVBoxLayout\" name=\"imageDisplayLayout\">\n      <property name=\"sizeConstraint\">\n       <enum>QLayout::SetDefaultConstraint</enum>\n      </property>\n     </layout>\n    </item>\n    <item>\n     <layout class=\"QGridLayout\" name=\"imageInfoLayout\">\n      <property name=\"sizeConstraint\">\n       <enum>QLayout::SetDefaultConstraint</enum>\n      </property>\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QGroupBox\" name=\"navigationGroupBox\">\n        <property name=\"enabled\">\n         <bool>false</bool>\n        </property>\n        <property name=\"maximumSize\">\n         <size>\n          <width>350</width>\n          <height>16777215</height>\n         </size>\n        </property>\n        <property name=\"title\">\n         <string>Navigation</string>\n        </property>\n        <layout class=\"QGridLayout\" name=\"gridLayout_4\">\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"imageIndexLabel\">\n           <property name=\"text\">\n            <string>Image: 0/0</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"0\" colspan=\"2\">\n          <widget class=\"QCheckBox\" name=\"propagateCheckBox\">\n           <property name=\"text\">\n            <string>Track labels</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\" colspan=\"2\">\n          <widget class=\"QPushButton\" name=\"changeImageButton\">\n           <property name=\"text\">\n            <string>Jump to image</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"0\">\n          <widget class=\"QCheckBox\" name=\"refineTrackingCheckbox\">\n           <property name=\"text\">\n            <string>Refine after tracking</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QSpinBox\" name=\"imageNumberSpinbox\">\n           <property name=\"maximumSize\">\n            <size>\n             <width>300</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"minimum\">\n            <number>1</number>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\" colspan=\"2\">\n          <widget class=\"QProgressBar\" name=\"imageProgressBar\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>0</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>300</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"styleSheet\">\n            <string notr=\"true\"/>\n           </property>\n           <property name=\"value\">\n            <number>0</number>\n           </property>\n           <property name=\"textVisible\">\n            <bool>false</bool>\n           </property>\n           <property name=\"invertedAppearance\">\n            <bool>false</bool>\n           </property>\n           <property name=\"format\">\n            <string>Image %v/%m </string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"1\" colspan=\"2\">\n          <widget class=\"QPushButton\" name=\"nextUnlabelledButton\">\n           <property name=\"text\">\n            <string>Next Unlabelled</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"QGroupBox\" name=\"labelGroupBox\">\n        <property name=\"enabled\">\n         <bool>false</bool>\n        </property>\n        <property name=\"minimumSize\">\n         <size>\n          <width>300</width>\n          <height>0</height>\n         </size>\n        </property>\n        <property name=\"maximumSize\">\n         <size>\n          <width>300</width>\n          <height>16777215</height>\n         </size>\n        </property>\n        <property name=\"title\">\n         <string>Labelling</string>\n        </property>\n        <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n         <item row=\"4\" column=\"2\">\n          <widget class=\"QPushButton\" name=\"nextInstanceButton\">\n           <property name=\"text\">\n            <string>Next instance</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\" colspan=\"3\">\n          <widget class=\"QLabel\" name=\"label_7\">\n           <property name=\"text\">\n            <string>Current label class</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"2\">\n          <widget class=\"QPushButton\" name=\"removeClassButton\">\n           <property name=\"text\">\n            <string>Remove class</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\" colspan=\"2\">\n          <widget class=\"QComboBox\" name=\"classComboBox\"/>\n         </item>\n         <item row=\"6\" column=\"0\">\n          <widget class=\"QLineEdit\" name=\"newClassText\">\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"placeholderText\">\n            <string>New class name</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"2\">\n          <widget class=\"QPushButton\" name=\"addClassButton\">\n           <property name=\"text\">\n            <string>Add class</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"2\">\n          <widget class=\"QPushButton\" name=\"removeImageLabelsButton\">\n           <property name=\"text\">\n            <string>Remove image labels</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"0\">\n       <widget class=\"QGroupBox\" name=\"imageGroupBox\">\n        <property name=\"enabled\">\n         <bool>false</bool>\n        </property>\n        <property name=\"minimumSize\">\n         <size>\n          <width>300</width>\n          <height>0</height>\n         </size>\n        </property>\n        <property name=\"maximumSize\">\n         <size>\n          <width>300</width>\n          <height>16777215</height>\n         </size>\n        </property>\n        <property name=\"title\">\n         <string>Image Attributes</string>\n        </property>\n        <layout class=\"QGridLayout\" name=\"gridLayout_3\">\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_3\">\n           <property name=\"text\">\n            <string>Filetype: </string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_5\">\n           <property name=\"text\">\n            <string>Filename:</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"1\">\n          <widget class=\"QLabel\" name=\"sizeLabel\">\n           <property name=\"text\">\n            <string>-</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QLabel\" name=\"filetypeLabel\">\n           <property name=\"text\">\n            <string>-</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_2\">\n           <property name=\"text\">\n            <string>Dimensions:</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QLabel\" name=\"dimensionsLabel\">\n           <property name=\"text\">\n            <string>-</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_6\">\n           <property name=\"text\">\n            <string>Instances labelled:</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QLabel\" name=\"filenameLabel\">\n           <property name=\"text\">\n            <string>-</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_4\">\n           <property name=\"text\">\n            <string>Size on disk: </string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label\">\n           <property name=\"text\">\n            <string>Bit Depth</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"1\">\n          <widget class=\"QLabel\" name=\"imageBitDepthLabel\">\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"1\">\n          <widget class=\"QLabel\" name=\"instanceCountLabel\">\n           <property name=\"text\">\n            <string>0</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"8\" column=\"0\">\n          <widget class=\"QPushButton\" name=\"removeImageButton\">\n           <property name=\"text\">\n            <string>Remove image</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"1\">\n          <widget class=\"QComboBox\" name=\"colourMapCombo\">\n           <item>\n            <property name=\"text\">\n             <string>Inferno</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Bone</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Viridis</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Plasma</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Magma</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Hot</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Rainbow</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Ocean</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Cividis</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_8\">\n           <property name=\"text\">\n            <string>Colour Map</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"0\" colspan=\"2\">\n          <widget class=\"QCheckBox\" name=\"colourMapCheckbox\">\n           <property name=\"text\">\n            <string>Apply colour map</string>\n           </property>\n           <property name=\"checked\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"0\">\n       <spacer name=\"verticalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Vertical</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>20</width>\n          <height>40</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menuBar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>931</width>\n     <height>21</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"menuFile\">\n    <property name=\"title\">\n     <string>File</string>\n    </property>\n    <addaction name=\"actionNew_Project\"/>\n    <addaction name=\"actionOpen_Project\"/>\n    <addaction name=\"actionMerge_Project\"/>\n    <addaction name=\"actionImport_Labels\"/>\n    <addaction name=\"actionExport\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuNavigation\">\n    <property name=\"enabled\">\n     <bool>false</bool>\n    </property>\n    <property name=\"title\">\n     <string>Navigation</string>\n    </property>\n    <addaction name=\"actionWrap_images\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionJump_forward\"/>\n    <addaction name=\"actionJump_backward\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuDetection\">\n    <property name=\"enabled\">\n     <bool>false</bool>\n    </property>\n    <property name=\"title\">\n     <string>Detection</string>\n    </property>\n    <addaction name=\"actionSetup_detector\"/>\n    <addaction name=\"actionSet_threshold\"/>\n    <addaction name=\"actionSet_NMS_threshold\"/>\n    <addaction name=\"actionDetect_project\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuImages\">\n    <property name=\"enabled\">\n     <bool>false</bool>\n    </property>\n    <property name=\"title\">\n     <string>Images</string>\n    </property>\n    <addaction name=\"actionAdd_image\"/>\n    <addaction name=\"actionAdd_image_folder\"/>\n    <addaction name=\"actionAdd_image_folders\"/>\n    <addaction name=\"actionAdd_video\"/>\n    <addaction name=\"actionCalculate_histograms\"/>\n    <addaction name=\"actionRefine_image_range\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuLabeling\">\n    <property name=\"enabled\">\n     <bool>false</bool>\n    </property>\n    <property name=\"title\">\n     <string>Labeling</string>\n    </property>\n    <addaction name=\"actionRemove_image_labels\"/>\n    <addaction name=\"actionRemove_labels_forwards\"/>\n   </widget>\n   <addaction name=\"menuFile\"/>\n   <addaction name=\"menuImages\"/>\n   <addaction name=\"menuNavigation\"/>\n   <addaction name=\"menuDetection\"/>\n   <addaction name=\"menuLabeling\"/>\n  </widget>\n  <widget class=\"QToolBar\" name=\"mainToolBar\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"movable\">\n    <bool>false</bool>\n   </property>\n   <property name=\"floatable\">\n    <bool>false</bool>\n   </property>\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n   <addaction name=\"actionPreviousImage\"/>\n   <addaction name=\"actionNextImage\"/>\n   <addaction name=\"actionDraw_Tool\"/>\n   <addaction name=\"actionSelect_Tool\"/>\n   <addaction name=\"actionInit_Tracking\"/>\n   <addaction name=\"actionPropagate_Tracking\"/>\n   <addaction name=\"actionRefine_boxes\"/>\n   <addaction name=\"actionDetect_Objects\"/>\n  </widget>\n  <widget class=\"QStatusBar\" name=\"statusBar\"/>\n  <action name=\"actionPreviousImage\">\n   <property name=\"checkable\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Prev</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Previous Image</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>A</string>\n   </property>\n  </action>\n  <action name=\"actionNextImage\">\n   <property name=\"text\">\n    <string>Next</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>D</string>\n   </property>\n  </action>\n  <action name=\"actionNew_Project\">\n   <property name=\"text\">\n    <string>New Project</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+N</string>\n   </property>\n  </action>\n  <action name=\"actionOpen_Project\">\n   <property name=\"text\">\n    <string>Open Project</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+O</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_image\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Add image(s)</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_image_folder\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Add image folder</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_class\">\n   <property name=\"text\">\n    <string>Add class</string>\n   </property>\n  </action>\n  <action name=\"actionDraw_Tool\">\n   <property name=\"text\">\n    <string>Draw</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Create rectangular labels</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Shift+D</string>\n   </property>\n  </action>\n  <action name=\"actionSelect_Tool\">\n   <property name=\"text\">\n    <string>Select</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Select labels for viewing/deletion</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Shift+S</string>\n   </property>\n  </action>\n  <action name=\"actionTwo_click\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Two-click</string>\n   </property>\n  </action>\n  <action name=\"actionDrawg\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Drag</string>\n   </property>\n  </action>\n  <action name=\"actionWrap_images\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Wrap images</string>\n   </property>\n  </action>\n  <action name=\"actionInit_Tracking\">\n   <property name=\"text\">\n    <string>Init Tracking</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Initialise object tracker with this image</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+I</string>\n   </property>\n  </action>\n  <action name=\"actionPropagate_Tracking\">\n   <property name=\"text\">\n    <string>Propagate Tracking</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+P</string>\n   </property>\n  </action>\n  <action name=\"actionExport\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Export labels</string>\n   </property>\n  </action>\n  <action name=\"actionRefine_boxes\">\n   <property name=\"text\">\n    <string>Refine boxes</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+R</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_video\">\n   <property name=\"text\">\n    <string>Add video(s)</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_image_folders\">\n   <property name=\"text\">\n    <string>Add image folders</string>\n   </property>\n  </action>\n  <action name=\"actionJump_forward\">\n   <property name=\"text\">\n    <string>Jump forward</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Right</string>\n   </property>\n  </action>\n  <action name=\"actionJump_backward\">\n   <property name=\"text\">\n    <string>Jump backward</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Left</string>\n   </property>\n  </action>\n  <action name=\"actionNext_Image\">\n   <property name=\"text\">\n    <string>Next Image</string>\n   </property>\n  </action>\n  <action name=\"actionPrevious_Image\">\n   <property name=\"text\">\n    <string>Previous Image</string>\n   </property>\n  </action>\n  <action name=\"actionSetup_detector\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Setup detector</string>\n   </property>\n  </action>\n  <action name=\"actionDetect_Objects\">\n   <property name=\"text\">\n    <string>Detect Objects</string>\n   </property>\n  </action>\n  <action name=\"actionSet_threshold\">\n   <property name=\"text\">\n    <string>Set confidence threshold</string>\n   </property>\n  </action>\n  <action name=\"actionDetect_project\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Run on project</string>\n   </property>\n  </action>\n  <action name=\"actionSet_NMS_threshold\">\n   <property name=\"text\">\n    <string>Set NMS threshold</string>\n   </property>\n  </action>\n  <action name=\"actionMerge_Project\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Merge Project</string>\n   </property>\n  </action>\n  <action name=\"actionImport_Labels\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Import Labels</string>\n   </property>\n  </action>\n  <action name=\"actioncalculateHistograms\">\n   <property name=\"text\">\n    <string>calculateHistograms</string>\n   </property>\n  </action>\n  <action name=\"actionCalculate_histograms\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Calculate histograms</string>\n   </property>\n  </action>\n  <action name=\"actionRefine_image_range\">\n   <property name=\"text\">\n    <string>Refine image range</string>\n   </property>\n  </action>\n  <action name=\"actionRemove_labels_forwards\">\n   <property name=\"text\">\n    <string>Remove labels forwards</string>\n   </property>\n  </action>\n  <action name=\"actionRemove_image_labels\">\n   <property name=\"text\">\n    <string>Remove image labels</string>\n   </property>\n  </action>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>classComboBox</tabstop>\n </tabstops>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/motimporter.cpp",
    "content": "#include \"motimporter.h\"\n\nvoid MOTImporter::importSequence(QString folder){\n\n    auto ini_filename = QDir(folder).absoluteFilePath(\"seqinfo.ini\");\n    auto sequence_name = QFileInfo(folder).baseName();\n\n    // Load image folder path from ini file\n    QSettings seq_ini(ini_filename, QSettings::IniFormat);\n    seq_ini.setIniCodec(QTextCodec::codecForName(\"UTF-8\"));\n    seq_ini.beginGroup(\"Sequence\");\n\n    if(!seq_ini.contains(\"imDir\")){\n        qCritical() << ini_filename << \" doesn't contain image directory\";\n        return;\n    }\n\n    auto image_foldername = seq_ini.value(\"imDir\").toString();\n\n    if(image_foldername == \"\"){\n        qCritical() << \"Image folder value in INI file is empty\";\n        return;\n    }\n\n    qDebug() << \"Image folder:\" << image_foldername;\n    auto image_folder = QDir(folder).absoluteFilePath(image_foldername);\n    qDebug() << \"Adding images from \" << image_folder;\n\n    QList<QList<BoundingBox>> label_list;\n    QList<QString> image_list;\n\n    // Find and load gt.txt file:\n    auto annotation_dir = QDir(QDir(folder).absoluteFilePath(\"gt\"));\n\n    QString annotation_file = annotation_dir\n                                .absoluteFilePath(QString(\"gt.txt\"));\n    qDebug() << \"Looking for: \" << annotation_file;\n\n    // Find images:\n    qInfo() << \"Loading annotations\";\n    auto labels = getLabels(annotation_file);\n\n    qInfo() << \"Checking images\";\n\n    auto pbar = cliProgressBar();\n    double progress = 0;\n    int i = 0;\n\n    auto images = QDir(image_folder).entryList(QDir::Files);\n    for(auto & image : images){\n        qDebug() << \"Adding labels for\" << image;\n\n        // Extract image ID, ignoring leading zeros\n        auto split_file = QFileInfo(image).baseName().split(\"_\");\n        int image_id = split_file.back().toInt();\n\n        // Get boxes for this ID and add to DB\n        auto boxes = findBoxes(labels, image_id);\n\n        progress = 100*static_cast<double>(i++)/images.size();\n        pbar.update(progress);\n        pbar.print();\n\n        QString abs_image_path = QDir(image_folder).absoluteFilePath(image);\n        if(boxes.empty() && !import_unlabelled){\n            continue;\n        }\n\n        label_list.append(boxes);\n        image_list.append(abs_image_path);\n\n    }\n    qInfo() << \"\\nInserting into database\";\n    project->addLabelledAssets(image_list, label_list);\n\n}\n\nvoid MOTImporter::import(QString sequence_folder){\n    QDir seq_dir(sequence_folder);\n\n    // Find all sequence folders\n    QDirIterator it(sequence_folder, QDir::Dirs | QDir::NoDotAndDotDot);\n    QList<QString> subfolders;\n\n    while (it.hasNext()) {\n        auto subfolder = QDir(it.next()).canonicalPath();\n        subfolders.append(subfolder);\n    }\n\n    if(subfolders.size() == 0){\n        qWarning() << \"Couldn't find any sequences in \" << sequence_folder;\n        return;\n    }\n\n    subfolders.removeDuplicates();\n    subfolders.sort();\n\n    for(auto &subfolder : subfolders){\n\n        qInfo() << \"Processing: \" << subfolder;\n        importSequence(subfolder);\n\n    }\n}\n\nQVector<QStringList> MOTImporter::getLabels(QString annotation_file){\n    auto lines = readLines(annotation_file);\n    QVector<QStringList> labels;\n\n    auto pbar = cliProgressBar();\n    double progress = 0;\n    int i = 0;\n\n    for(auto &line : lines){\n        auto label = line.simplified().split(\",\");\n\n        // Note - MOT docs say 10, but MOTDet only contains 9 elements\n        if(label.size() < 8){\n            qWarning() << \"Found :\" <<  label.size() << \" elements - need at least 9.\";\n            continue;\n        }else{\n            labels.push_back(label);\n        }\n\n        progress = 100*static_cast<double>(i++)/lines.size();\n        pbar.update(progress);\n        pbar.print();\n    }\n\n    qInfo() << \"\";\n\n    return labels;\n}\n\nQList<BoundingBox> MOTImporter::findBoxes(QVector<QStringList> labels, int id){\n\n    QList<BoundingBox> boxes = {};\n\n    // <frame>, <id>, <bb_left>, <bb_top>, <bb_width>, <bb_height>, <conf>, <x>, <y>, <z>\n    for(auto &label : labels){\n\n        if(label.at(0).toInt() != id){\n            continue;\n        }\n\n        BoundingBox bbox;\n\n        bbox.classid = label.at(7).toInt();\n        bbox.classname = project->getClassName(bbox.classid);\n\n        if(bbox.classname == \"\"){\n            qWarning() << \"Class\" << bbox.classid << \" not found in names file.\";\n        }\n\n        auto top_left = QPoint(label.at(2).toDouble(), label.at(3).toDouble());\n        auto bottom_right = top_left + QPoint(label.at(4).toDouble(), label.at(5).toDouble());\n\n        bbox.rect = QRect(top_left, bottom_right);\n        boxes.append(bbox);\n    }\n\n    return boxes;\n}\n\nvoid MOTImporter::loadClasses(QString names_file){\n    QFile fh(names_file);\n    QList<QString> class_list;\n    project->getClassList(class_list);\n\n    if (fh.open(QIODevice::ReadOnly)) {\n\n        while (!fh.atEnd()) {\n            // Darknet name file is just a newline delimited list of classes\n            QByteArray line = fh.readLine();\n\n            if(QString(line) == \"\") continue;\n\n            auto new_class = line.simplified();\n\n            if(!class_list.contains(new_class)){\n                project->addClass(new_class);\n            }else{\n                qDebug() << \"Added: \" << new_class;\n            }\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/motimporter.h",
    "content": "#ifndef MOTIMPORTER_H\n#define MOTIMPORTER_H\n\n#include \"baseimporter.h\"\n#include <QSettings>\n#include <QTextCodec>\n\nclass MOTImporter : public BaseImporter\n{\npublic:\n    using BaseImporter::import;\n\n    explicit MOTImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){\n        this->project = project;\n    }\n    void import(QString sequence_folder);\n    void importSequence(QString folder);\n    void loadClasses(QString names_file);\n\nprotected:\n    QVector<QStringList> getLabels(QString annotation_file);\n    virtual QList<BoundingBox> findBoxes(QVector<QStringList> labels, int id);\n    QString findImage(QList<QString> *images, QString sequence_name, int image_id);\n\n\n};\n\n#endif // MOTIMPORTER_H\n"
  },
  {
    "path": "src/multitracker.cpp",
    "content": "#include \"multitracker.h\"\n\ncv::Rect2d qrect2cv(QRect rect){\n    return cv::Rect2d(rect.x(), rect.y(), rect.width(),rect.height());\n}\n\n\n/* ---- OpenCV Trackers ---- */\n\ncv::Ptr<cv::Tracker> MultiTrackerCV::createTrackerByName(OpenCVTrackerType type){\n    using namespace cv;\n\n    Ptr<Tracker> tracker;\n\n    if (type == MIL)\n      tracker = TrackerMIL::create();\n    else if (type == KCF)\n      tracker = TrackerKCF::create();\n    else if (type == GOTURN)\n      tracker = TrackerGOTURN::create();\n    else if (type == CSRT)\n      tracker = TrackerCSRT::create();\n    else {\n      qCritical() << \"Incorrect tracker specified\";\n    }\n\n    tracker = TrackerCSRT::create();\n\n    return tracker;\n}\n\nvoid MultiTrackerCV::init(const cv::Mat &image, QList<BoundingBox> bboxes){\n\n    trackers.clear();\n    QMutex mutex;\n\n    // If we are tracking and we have some labelled boxes already\n    QtConcurrent::blockingMap(bboxes.begin(), bboxes.end(), [&](BoundingBox &bbox)\n    {\n        if(bbox.rect.width()*bbox.rect.height() <= 0) return;\n\n        auto tracker = createTrackerByName(type_);\n        tracker->init(image, qrect2cv(bbox.rect));\n\n        mutex.lock();\n        trackers.push_back({tracker, bbox.classname});\n        mutex.unlock();\n    });\n}\n\nvoid MultiTrackerCV::update(const cv::Mat &image){\n    QMutex mutex;\n    bboxes.clear();\n\n    QtConcurrent::blockingMap(trackers.begin(), trackers.end(), [&](auto&& tracker)\n    {\n\n        cv::Rect bbox;\n        if( tracker.first->update(image, bbox)){\n\n            QRect new_roi;\n            new_roi.setX(static_cast<int>(bbox.x));\n            new_roi.setY(static_cast<int>(bbox.y));\n            new_roi.setWidth(static_cast<int>(bbox.width));\n            new_roi.setHeight(static_cast<int>(bbox.height));\n\n            BoundingBox new_bbox;\n            new_bbox.rect = new_roi;\n            new_bbox.classname = tracker.second;\n\n            mutex.lock();\n            bboxes.append(new_bbox);\n            mutex.unlock();\n        }\n    });\n}\n\n/* ---- CamShift Tracker ---- */\n\nvoid MultiTrackerCamshift::init(const cv::Mat &image, QList<BoundingBox> bboxes){\n\n    trackers.clear();\n    QMutex mutex;\n\n    QtConcurrent::blockingMap(bboxes.begin(), bboxes.end(), [&](BoundingBox &bbox)\n    {\n        auto roi_hist = getRoiHist(image, bbox.rect);\n        mutex.lock();\n        trackers.push_back({roi_hist, bbox});\n        mutex.unlock();\n    });\n}\n\n\nvoid MultiTrackerCamshift::update(const cv::Mat &image){\n\n    QMutex mutex;\n    bboxes.clear();\n\n    QtConcurrent::blockingMap(trackers.begin(), trackers.end(), [&](auto&& tracker)\n    {\n        // image, roi_hist, bbox\n        cv::Rect2i bbox = this->updateCamshift(image, tracker.first, tracker.second.rect);\n\n        QRect new_roi;\n        new_roi.setX(static_cast<int>(bbox.x));\n        new_roi.setY(static_cast<int>(bbox.y));\n        new_roi.setWidth(static_cast<int>(bbox.width));\n        new_roi.setHeight(static_cast<int>(bbox.height));\n\n        BoundingBox new_bbox;\n        new_bbox.rect = new_roi;\n        new_bbox.classname = tracker.second.classname;\n\n        mutex.lock();\n        bboxes.append(new_bbox);\n        mutex.unlock();\n\n\n    });\n\n}\n\n\ncv::Mat MultiTrackerCamshift::getRoiHist(const cv::Mat &image, QRect bbox){\n    auto roi = image(qrect2cv(bbox));\n    cv::Mat mask;\n\n    if(roi.channels() == 1){\n        //cv::cvtColor(roi, roi, cv::COLOR_GRAY2BGR);\n        //cv::cvtColor(roi, roi, cv::COLOR_BGR2HSV);\n        auto lowScalar = cv::Scalar(30);\n        auto highScalar = cv::Scalar(180);\n        cv::inRange(roi, lowScalar, highScalar, mask);\n    }else if(roi.channels() == 3){\n        cv::cvtColor(roi, roi, cv::COLOR_BGR2HSV);\n        auto lowScalar = cv::Scalar(30,30,30);\n        auto highScalar = cv::Scalar(180,180,180);\n        cv::inRange(roi, lowScalar, highScalar, mask);\n    }else{\n        // What the hell kind of 2 channel image is this?\n        return cv::Mat();\n    }\n\n    // Generate histogram\n    cv::Mat roiHist;\n\n    int histSize = 256;\n    float range[] = { 0, 180 }; //the upper boundary is exclusive\n    const float* histRange = { range };\n\n    bool uniform = true;\n    bool accumulate = true;\n\n    int n_images = 1;\n    int *use_channels = nullptr;\n    int n_dims = 1;\n\n    cv::calcHist( &roi, n_images, use_channels, mask, roiHist, n_dims, &histSize, &histRange, uniform, accumulate);\n    cv::normalize(roiHist, roiHist, 0, 255, cv::NORM_MINMAX);\n\n    return roiHist;\n\n}\n\ncv::Rect2i MultiTrackerCamshift::updateCamshift(const cv::Mat &image, cv::Mat roiHist, QRect bbox){\n    auto rect = qrect2cv(bbox);\n    auto roi = image(rect);\n\n    if(roi.channels() == 1){\n        //cv::cvtColor(roi, roi, cv::COLOR_GRAY2BGR);\n        cv::cvtColor(roi, roi, cv::COLOR_BGR2HSV);\n    }else if(roi.channels() == 3){\n        cv::cvtColor(roi, roi, cv::COLOR_BGR2HSV);\n    }else{\n        // What the hell kind of 2 channel image is this?\n        return cv::Rect();\n    }\n\n    auto rectInt = cv::Rect2i(rect);\n\n    cv::Mat backProjection;\n    float range[] = { 0, 180 }; //the upper boundary is exclusive\n    const float* histRange = { range };\n    cv::calcBackProject(&image, 1, nullptr, roiHist, backProjection, &histRange);\n\n    auto termcrit = cv::TermCriteria(cv::TermCriteria::EPS|cv::TermCriteria::COUNT, 10, 1);\n    auto rotated_rect = cv::CamShift(backProjection, rectInt, termcrit);\n\n    return rotated_rect.boundingRect();\n}\n\nvoid MultiTrackerCamshift::histogram(const cv::Mat &image, cv::Mat &hist){\n    int histSize = 256;\n    float range[] = { 0, 256 }; //the upper boundary is exclusive\n    const float* histRange = { range };\n\n    bool uniform = true;\n    bool accumulate = true;\n\n    calcHist( &image, 1, nullptr, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);\n}\n\n"
  },
  {
    "path": "src/multitracker.h",
    "content": "#ifndef TRACKER_H\n#define TRACKER_H\n\n#include <QObject>\n#include <QtConcurrent/qtconcurrentmap.h>\n#include <boundingbox.h>\n\n#include <opencv2/opencv.hpp>\n#include <opencv2/tracking/tracking.hpp>\n\nenum OpenCVTrackerType {BOOSTING, MIL, KCF, TLD, MEDIANFLOW, GOTURN, MOSSE, CSRT};\nenum MultiTrackerType {OPENCV, CAMSHIFT};\n\ncv::Rect2d qrect2cv(QRect rect);\n\nclass MultiTracker : public QObject\n{\n    Q_OBJECT\npublic:\n    explicit MultiTracker(QObject *parent = nullptr) : QObject(parent){}\n\nsignals:\n\npublic slots:\n    QList<BoundingBox> getBoxes(){ return bboxes;}\n\n    virtual void init(const cv::Mat &image, QList<BoundingBox> bboxes) = 0;\n    virtual void update(const cv::Mat &image) = 0;\n\nprotected:\n    QList<BoundingBox> bboxes;\n};\n\nclass MultiTrackerCV : public MultiTracker\n{\n    Q_OBJECT\n\npublic:\n    explicit MultiTrackerCV(QObject *parent = nullptr) : MultiTracker(parent){}\n\npublic slots:\n    void init(const cv::Mat &image, QList<BoundingBox> bboxes);\n    void update(const cv::Mat &image);\n    void setTrackerType(OpenCVTrackerType type){ type_ = type; }\n\nprivate:\n    std::vector<std::pair<cv::Ptr<cv::Tracker>, QString>> trackers;\n    cv::Ptr<cv::Tracker> createTrackerByName(OpenCVTrackerType MultiTracker);\n    OpenCVTrackerType type_;\n\n};\n\nclass MultiTrackerCamshift : public MultiTracker\n{\n    Q_OBJECT\npublic:\n    explicit MultiTrackerCamshift(QObject *parent = nullptr)  : MultiTracker(parent){}\n    void init(const cv::Mat &image, QList<BoundingBox> bboxes);\n    void update(const cv::Mat &image);\n\n\nprivate:\n    std::vector<std::pair<cv::Mat, BoundingBox>> trackers;\n    cv::Rect2i updateCamshift(const cv::Mat &image, cv::Mat roiHist, QRect bbox);\n    cv::Mat getRoiHist(const cv::Mat &image, QRect bbox);\n    void histogram(const cv::Mat &image, cv::Mat &hist);\n\n};\n\n#endif // TRACKER_H\n"
  },
  {
    "path": "src/pascalvocexporter.cpp",
    "content": "#include \"pascalvocexporter.h\"\n\nvoid PascalVocExporter::splitData(float split, bool shuffle, int seed){\n\n    if(split < 0 || split > 1){\n        qCritical() << \"Invalid split fraction, should be [0,1]\";\n    }\n\n    if(shuffle){\n        std::random_device rd;\n        std::mt19937 generator(rd());\n        generator.seed(static_cast<unsigned int>(seed));\n\n        std::shuffle(images.begin(), images.end(), generator);\n    }\n\n    int pivot = static_cast<int>(images.size() * split);\n    train_set = images.mid(0, pivot);\n    validation_set = images.mid(pivot);\n\n    qInfo() << train_set.size() << \" images selected for train set.\";\n    qInfo() << validation_set.size() << \" images selected for validation set.\";\n\n}\n\nbool PascalVocExporter::setOutputFolder(const QString folder){\n\n    if(folder == \"\") return false;\n\n    output_folder = folder;\n\n    //Make output folder if it doesn't exist\n    if (!QDir(output_folder).exists()){\n        qInfo() << \"Making output folder\" << output_folder;\n        QDir().mkpath(output_folder);\n    }\n\n    //Make the training and validation folders\n    train_folder = QDir::cleanPath(output_folder+\"/train\");\n    if (!QDir(train_folder).exists()){\n        qInfo() << \"Making training folder\" << train_folder;\n        QDir().mkpath(train_folder);\n    }\n\n    val_folder = QDir::cleanPath(output_folder+\"/val\");\n    if (!QDir(val_folder).exists()){\n        qInfo() << \"Making validation folder\" << val_folder;\n        QDir().mkpath(val_folder);\n    }\n\n    train_label_folder = QDir::cleanPath(train_folder);\n    train_image_folder = QDir::cleanPath(train_folder);\n    val_label_folder = QDir::cleanPath(val_folder);\n    val_image_folder = QDir::cleanPath(val_folder);\n\n    return true;\n\n}\n\nvoid PascalVocExporter::writeLabels(const cv::Mat &image, const QString image_filename, const QString label_filename, const QList<BoundingBox> labels){\n\n    // Still make a label file even if there are no detections. This is important\n    // for background class detection.\n\n    QFile f(label_filename);\n\n    // Delete existing files for simplicity.\n    if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {\n        QXmlStreamWriter stream(&f);\n        stream.setAutoFormatting(true);\n        stream.writeStartDocument();\n\n        stream.writeStartElement(\"annotation\");\n\n        auto finfo = QFileInfo(image_filename);\n\n        stream.writeTextElement(\"folder\", finfo.dir().dirName());\n        stream.writeTextElement(\"filename\", finfo.fileName());\n        stream.writeTextElement(\"path\", finfo.filePath());\n\n        QString label_database = \"\";\n        stream.writeStartElement(\"source\");\n            stream.writeTextElement(\"database\", label_database);\n        stream.writeEndElement(); // source\n\n        // Image properties\n        stream.writeStartElement(\"size\");\n            stream.writeTextElement(\"width\", QString::number(image.cols));\n            stream.writeTextElement(\"height\", QString::number(image.rows));\n            stream.writeTextElement(\"depth\", QString::number(image.channels()));\n        stream.writeEndElement(); // size\n\n        // Not a segmentation mask\n        stream.writeTextElement(\"segmented\", \"0\");\n\n        // Labels\n        for(auto &label : labels){\n            stream.writeStartElement(\"object\");\n                stream.writeTextElement(\"name\", label.classname);\n                stream.writeTextElement(\"pose\", \"Unspecified\");\n                stream.writeTextElement(\"truncated\", \"0\");\n                stream.writeTextElement(\"difficult\", \"0\");\n\n                stream.writeStartElement(\"bndbox\");\n                    stream.writeTextElement(\"xmin\", QString::number(label.rect.left()));\n                    stream.writeTextElement(\"xmax\", QString::number(label.rect.right()));\n                    stream.writeTextElement(\"ymin\", QString::number(label.rect.top()));\n                    stream.writeTextElement(\"ymax\", QString::number(label.rect.bottom()));\n                stream.writeEndElement(); //bndbox\n\n            stream.writeEndElement(); //object\n        }\n\n        stream.writeEndElement(); // annotation\n        stream.writeEndDocument();\n\n        f.close();\n    }\n}\n\nbool PascalVocExporter::saveImage(cv::Mat &image, const QString output, const double scale_x, const double scale_y){\n\n    if(image.rows == 0 || image.cols == 0){\n        qCritical() << \"Empty image \";\n        return false;\n    }\n\n    if(scale_x > 0 && scale_y > 0)\n        cv::resize(image, image, cv::Size(), scale_x, scale_y);\n\n    std::vector<int> compression_params;\n\n    // Png compression - maximum is super slow\n    // TODO: add support to adjust this\n    if(output.split(\".\").last().toLower() == \"png\"){\n        compression_params.push_back(cv::IMWRITE_PNG_COMPRESSION);\n        compression_params.push_back(6);\n    }\n\n    return cv::imwrite(output.toStdString(), image, compression_params);\n}\n\nbool PascalVocExporter::processImages(const QString folder, const QList<QString> images){\n\n    QString image_path;\n    QList<BoundingBox> labels;\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n\n    if(disable_progress){\n        progress.hide();\n    }\n\n    int i = 0;\n\n    foreach(image_path, images){\n\n        if(progress.wasCanceled()){\n            break;\n        }\n\n        project->getLabels(image_path, labels);\n\n        if(!export_unlabelled && labels.size() == 0){\n            if(!disable_progress){\n                progress.setValue(i);\n                progress.setLabelText(QString(\"%1 is unlabelled\").arg(image_path));\n                progress.repaint();\n                QApplication::processEvents();\n            }\n            continue;\n        }\n\n        QString abs_image_path = project->getDbFolder().absoluteFilePath(image_path);\n        QString extension = QFileInfo(image_path).suffix();\n        QString filename_noext = QFileInfo(image_path).completeBaseName();\n        QString image_filename = QString(\"%1/%2%3.%4\").arg(folder).arg(filename_prefix).arg(filename_noext).arg(extension);\n\n        // Correct for duplicate file names in output\n        int dupe_file = 1;\n        while(QFile(image_filename).exists()){\n            image_filename = QString(\"%1/%2%3_%4.%5\").arg(folder).arg(filename_prefix).arg(filename_noext).arg(dupe_file++).arg(extension);\n        }\n\n        cv::Mat image = cv::imread(abs_image_path.toStdString());\n\n        // Copy the image to the new folder\n        //saveImage(image, image_filename);\n        QFile::copy(abs_image_path, image_filename);\n\n        QString label_filename = QString(\"%1/%2.xml\").arg(folder).arg(filename_noext);\n        writeLabels(image, image_filename, label_filename, labels);\n\n        if(!disable_progress){\n            progress.setValue(i++);\n            progress.setLabelText(image_filename);\n            QApplication::processEvents();\n        }\n    }\n\n    return true;\n}\n\nvoid PascalVocExporter::saveLabelMap(QString filename){\n\n    QFile f(filename);\n\n    // Delete existing files for simplicity.\n    if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {\n\n        QList<QString> classes;\n        project->getClassList(classes);\n\n        for(auto classname : classes){\n            f.write(\"item {\\n\");\n            f.write(QString(\"  name: %1\\n\").arg(classname).toStdString().c_str());\n            f.write(QString(\"  id: %1\\n\").arg(project->getClassId(classname)).toStdString().c_str());\n            f.write(QString(\"  displayname: %1\\n\").arg(classname).toStdString().c_str());\n            f.write(\"}\\n\");\n        }\n\n        f.close();\n    }\n\n}\n\nvoid PascalVocExporter::process(){\n    processImages(train_folder, train_set);\n    processImages(val_folder, validation_set);\n    if(export_map){\n        saveLabelMap(QString(\"%1/%2\").arg(output_folder).arg(\"label_map.pbtxt\"));\n    }\n}\n\n"
  },
  {
    "path": "src/pascalvocexporter.h",
    "content": "#ifndef PASCALVOCEXPORTER_H\n#define PASCALVOCEXPORTER_H\n\n#include <baseexporter.h>\n#include <QObject>\n#include <QImageReader>\n#include <QFileDialog>\n#include <QXmlStreamWriter>\n#include <boundingbox.h>\n#include <opencv2/opencv.hpp>\n#include <labelproject.h>\n#include <random>\n\nclass PascalVocExporter : public BaseExporter\n{\n    Q_OBJECT\npublic:\n    explicit PascalVocExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n\nsignals:\n    void export_progress(int);\n\npublic slots:\n    void splitData(float split=1, bool shuffle=false, int seed=42);\n    bool setOutputFolder(QString folder);\n    void setExportUnlabelled(bool res){export_unlabelled = res;}\n    void setExportMap(bool map){export_map = map;}\n    void process();\n\nprivate:\n    LabelProject *project;\n    QList<QString> train_set;\n    QList<QString> validation_set;\n    QList<QString> images;\n\n    QString train_folder;\n    QString train_label_folder;\n    QString train_image_folder;\n\n    QString val_folder;\n    QString val_label_folder;\n    QString val_image_folder;\n\n    QString output_folder;\n    bool export_unlabelled = false;\n    bool export_map;\n\n\n    std::map<QString, int> id_map;\n\n    void writeLabels(const cv::Mat &image, const QString image_filename, const QString label_filename, const QList<BoundingBox> labels);\n    bool processImages(const QString folder, const QList<QString> images);\n    void saveLabelMap(QString filename);\n    bool saveImage(cv::Mat &image, const QString output, const double scale_x = -1.0, const double scale_y = -1.0);\n};\n\n#endif // PASCALVOCEXPORTER_H\n"
  },
  {
    "path": "src/pascalvocimporter.cpp",
    "content": "#include \"pascalvocimporter.h\"\n\nvoid PascalVOCImporter::import(QString image_folder, QString annotation_folder){\n\n    QDir image_dir(image_folder);\n    QDir annotation_dir(annotation_folder);\n\n    QList<QList<BoundingBox>> label_list;\n    QList<QString> image_list;\n\n    auto annotations = QDir(annotation_folder).entryList(QDir::Files|QDir::NoDotAndDotDot);\n\n    auto pbar = cliProgressBar();\n    double progress = 0;\n    int i = 0;\n\n    for(auto & annotation : annotations){\n\n        qDebug() << annotation;\n\n        auto abs_annotation_path = annotation_dir.absoluteFilePath(annotation);\n        QString image_filename;\n        auto labels = getLabels(abs_annotation_path, image_filename);\n        auto abs_image_path = image_dir.absoluteFilePath(image_filename);\n\n        progress = 100*static_cast<double>(i++)/annotations.size();\n        pbar.update(progress);\n        pbar.print();\n\n        if(!QFileInfo(abs_image_path).exists()){\n            qWarning() << \"Couldn't find image: \" << abs_image_path;\n            continue;\n        }\n\n        if(labels.empty() && !import_unlabelled){\n            continue;\n        }\n\n        image_list.append(abs_image_path);\n        label_list.append(labels);\n    }\n\n    qInfo() << \"Importing images and labels\";\n    project->addLabelledAssets(image_list, label_list);\n\n}\n\nQList<BoundingBox> PascalVOCImporter::getLabels(QString annotation_file, QString &image_filename){\n    QFile f(annotation_file);\n    QDomDocument doc;\n    QList<BoundingBox> labels;\n    QList<QString> classes;\n    project->getClassList(classes);\n\n    if (!f.open(QIODevice::ReadOnly))\n        return labels;\n    if (!doc.setContent(&f)) {\n        f.close();\n        return labels;\n    }\n\n    auto objects = doc.elementsByTagName(\"object\");\n\n    image_filename = doc.elementsByTagName(\"filename\").at(0).toElement().text();\n\n    for(int i=0; i < objects.size(); i++){\n\n        BoundingBox new_box;\n        auto object = objects.at(i).toElement();\n        auto name_node = object.elementsByTagName(\"name\").at(0).toElement().text();\n\n        new_box.classname = object.elementsByTagName(\"name\").at(0).toElement().text();\n\n        if(!classes.contains(new_box.classname)){\n            project->addClass(new_box.classname);\n            classes.clear();\n            project->getClassList(classes);\n        }\n\n        new_box.classid = project->getClassId(new_box.classname);\n\n        auto xmin = object.elementsByTagName(\"xmin\").at(0).toElement().text().toInt();\n        auto ymin = object.elementsByTagName(\"ymin\").at(0).toElement().text().toInt();\n        auto xmax = object.elementsByTagName(\"xmax\").at(0).toElement().text().toInt();\n        auto ymax = object.elementsByTagName(\"ymax\").at(0).toElement().text().toInt();\n\n        new_box.rect = QRect(QPoint(xmin, ymin), QPoint(xmax, ymax));\n\n        labels.append(new_box);\n    }\n\n    return labels;\n}\n\n"
  },
  {
    "path": "src/pascalvocimporter.h",
    "content": "#ifndef PASCALVOCIMPORTER_H\n#define PASCALVOCIMPORTER_H\n\n#include \"baseimporter.h\"\n#include <QDomDocument>\n\nclass PascalVOCImporter : public BaseImporter\n{\npublic:\n    using BaseImporter::import;\n\n    explicit PascalVOCImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){\n        this->project = project;\n    }\n    void import(QString image_folder, QString annotation_folder);\n\nprivate:\n    QList<BoundingBox> getLabels(QString annotation_file, QString &image_file);\n};\n\n#endif // PASCALVOCIMPORTER_H\n"
  },
  {
    "path": "src/proto/example.pb.cc",
    "content": "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: example.proto\n\n#include \"example.pb.h\"\n\n#include <algorithm>\n\n#include <google/protobuf/io/coded_stream.h>\n#include <google/protobuf/extension_set.h>\n#include <google/protobuf/wire_format_lite.h>\n#include <google/protobuf/descriptor.h>\n#include <google/protobuf/generated_message_reflection.h>\n#include <google/protobuf/reflection_ops.h>\n#include <google/protobuf/wire_format.h>\n// @@protoc_insertion_point(includes)\n#include <google/protobuf/port_def.inc>\n\nPROTOBUF_PRAGMA_INIT_SEG\nnamespace tensorflow {\nconstexpr Example::Example(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : features_(nullptr){}\nstruct ExampleDefaultTypeInternal {\n  constexpr ExampleDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~ExampleDefaultTypeInternal() {}\n  union {\n    Example _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT ExampleDefaultTypeInternal _Example_default_instance_;\nconstexpr SequenceExample::SequenceExample(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : context_(nullptr)\n  , feature_lists_(nullptr){}\nstruct SequenceExampleDefaultTypeInternal {\n  constexpr SequenceExampleDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~SequenceExampleDefaultTypeInternal() {}\n  union {\n    SequenceExample _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT SequenceExampleDefaultTypeInternal _SequenceExample_default_instance_;\n}  // namespace tensorflow\nstatic ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_example_2eproto[2];\nstatic constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_example_2eproto = nullptr;\nstatic constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_example_2eproto = nullptr;\n\nconst ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_example_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Example, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Example, features_),\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::SequenceExample, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::SequenceExample, context_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::SequenceExample, feature_lists_),\n};\nstatic const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {\n  { 0, -1, sizeof(::tensorflow::Example)},\n  { 6, -1, sizeof(::tensorflow::SequenceExample)},\n};\n\nstatic ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = {\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_Example_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_SequenceExample_default_instance_),\n};\n\nconst char descriptor_table_protodef_example_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =\n  \"\\n\\rexample.proto\\022\\ntensorflow\\032\\rfeature.pro\"\n  \"to\\\"1\\n\\007Example\\022&\\n\\010features\\030\\001 \\001(\\0132\\024.tensor\"\n  \"flow.Features\\\"i\\n\\017SequenceExample\\022%\\n\\007cont\"\n  \"ext\\030\\001 \\001(\\0132\\024.tensorflow.Features\\022/\\n\\rfeatu\"\n  \"re_lists\\030\\002 \\001(\\0132\\030.tensorflow.FeatureLists\"\n  \"B\\201\\001\\n\\026org.tensorflow.exampleB\\rExampleProt\"\n  \"osP\\001ZSgithub.com/tensorflow/tensorflow/t\"\n  \"ensorflow/go/core/example/example_protos\"\n  \"_go_proto\\370\\001\\001b\\006proto3\"\n  ;\nstatic const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_example_2eproto_deps[1] = {\n  &::descriptor_table_feature_2eproto,\n};\nstatic ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_example_2eproto_once;\nconst ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_example_2eproto = {\n  false, false, 340, descriptor_table_protodef_example_2eproto, \"example.proto\", \n  &descriptor_table_example_2eproto_once, descriptor_table_example_2eproto_deps, 1, 2,\n  schemas, file_default_instances, TableStruct_example_2eproto::offsets,\n  file_level_metadata_example_2eproto, file_level_enum_descriptors_example_2eproto, file_level_service_descriptors_example_2eproto,\n};\nPROTOBUF_ATTRIBUTE_WEAK ::PROTOBUF_NAMESPACE_ID::Metadata\ndescriptor_table_example_2eproto_metadata_getter(int index) {\n  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_example_2eproto);\n  return descriptor_table_example_2eproto.file_level_metadata[index];\n}\n\n// Force running AddDescriptors() at dynamic initialization time.\nPROTOBUF_ATTRIBUTE_INIT_PRIORITY static ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptorsRunner dynamic_init_dummy_example_2eproto(&descriptor_table_example_2eproto);\nnamespace tensorflow {\n\n// ===================================================================\n\nclass Example::_Internal {\n public:\n  static const ::tensorflow::Features& features(const Example* msg);\n};\n\nconst ::tensorflow::Features&\nExample::_Internal::features(const Example* msg) {\n  return *msg->features_;\n}\nvoid Example::clear_features() {\n  if (GetArena() == nullptr && features_ != nullptr) {\n    delete features_;\n  }\n  features_ = nullptr;\n}\nExample::Example(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.Example)\n}\nExample::Example(const Example& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message() {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  if (from._internal_has_features()) {\n    features_ = new ::tensorflow::Features(*from.features_);\n  } else {\n    features_ = nullptr;\n  }\n  // @@protoc_insertion_point(copy_constructor:tensorflow.Example)\n}\n\nvoid Example::SharedCtor() {\nfeatures_ = nullptr;\n}\n\nExample::~Example() {\n  // @@protoc_insertion_point(destructor:tensorflow.Example)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid Example::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n  if (this != internal_default_instance()) delete features_;\n}\n\nvoid Example::ArenaDtor(void* object) {\n  Example* _this = reinterpret_cast< Example* >(object);\n  (void)_this;\n}\nvoid Example::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid Example::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid Example::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.Example)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  if (GetArena() == nullptr && features_ != nullptr) {\n    delete features_;\n  }\n  features_ = nullptr;\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* Example::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // .tensorflow.Features features = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr = ctx->ParseMessage(_internal_mutable_features(), ptr);\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* Example::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.Example)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // .tensorflow.Features features = 1;\n  if (this->has_features()) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(\n        1, _Internal::features(this), target, stream);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.Example)\n  return target;\n}\n\nsize_t Example::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.Example)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // .tensorflow.Features features = 1;\n  if (this->has_features()) {\n    total_size += 1 +\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(\n        *features_);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid Example::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.Example)\n  GOOGLE_DCHECK_NE(&from, this);\n  const Example* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Example>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.Example)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.Example)\n    MergeFrom(*source);\n  }\n}\n\nvoid Example::MergeFrom(const Example& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.Example)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  if (from.has_features()) {\n    _internal_mutable_features()->::tensorflow::Features::MergeFrom(from._internal_features());\n  }\n}\n\nvoid Example::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.Example)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid Example::CopyFrom(const Example& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.Example)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool Example::IsInitialized() const {\n  return true;\n}\n\nvoid Example::InternalSwap(Example* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  swap(features_, other->features_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata Example::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nclass SequenceExample::_Internal {\n public:\n  static const ::tensorflow::Features& context(const SequenceExample* msg);\n  static const ::tensorflow::FeatureLists& feature_lists(const SequenceExample* msg);\n};\n\nconst ::tensorflow::Features&\nSequenceExample::_Internal::context(const SequenceExample* msg) {\n  return *msg->context_;\n}\nconst ::tensorflow::FeatureLists&\nSequenceExample::_Internal::feature_lists(const SequenceExample* msg) {\n  return *msg->feature_lists_;\n}\nvoid SequenceExample::clear_context() {\n  if (GetArena() == nullptr && context_ != nullptr) {\n    delete context_;\n  }\n  context_ = nullptr;\n}\nvoid SequenceExample::clear_feature_lists() {\n  if (GetArena() == nullptr && feature_lists_ != nullptr) {\n    delete feature_lists_;\n  }\n  feature_lists_ = nullptr;\n}\nSequenceExample::SequenceExample(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.SequenceExample)\n}\nSequenceExample::SequenceExample(const SequenceExample& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message() {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  if (from._internal_has_context()) {\n    context_ = new ::tensorflow::Features(*from.context_);\n  } else {\n    context_ = nullptr;\n  }\n  if (from._internal_has_feature_lists()) {\n    feature_lists_ = new ::tensorflow::FeatureLists(*from.feature_lists_);\n  } else {\n    feature_lists_ = nullptr;\n  }\n  // @@protoc_insertion_point(copy_constructor:tensorflow.SequenceExample)\n}\n\nvoid SequenceExample::SharedCtor() {\n::memset(reinterpret_cast<char*>(this) + static_cast<size_t>(\n    reinterpret_cast<char*>(&context_) - reinterpret_cast<char*>(this)),\n    0, static_cast<size_t>(reinterpret_cast<char*>(&feature_lists_) -\n    reinterpret_cast<char*>(&context_)) + sizeof(feature_lists_));\n}\n\nSequenceExample::~SequenceExample() {\n  // @@protoc_insertion_point(destructor:tensorflow.SequenceExample)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid SequenceExample::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n  if (this != internal_default_instance()) delete context_;\n  if (this != internal_default_instance()) delete feature_lists_;\n}\n\nvoid SequenceExample::ArenaDtor(void* object) {\n  SequenceExample* _this = reinterpret_cast< SequenceExample* >(object);\n  (void)_this;\n}\nvoid SequenceExample::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid SequenceExample::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid SequenceExample::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.SequenceExample)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  if (GetArena() == nullptr && context_ != nullptr) {\n    delete context_;\n  }\n  context_ = nullptr;\n  if (GetArena() == nullptr && feature_lists_ != nullptr) {\n    delete feature_lists_;\n  }\n  feature_lists_ = nullptr;\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* SequenceExample::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // .tensorflow.Features context = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr = ctx->ParseMessage(_internal_mutable_context(), ptr);\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      // .tensorflow.FeatureLists feature_lists = 2;\n      case 2:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {\n          ptr = ctx->ParseMessage(_internal_mutable_feature_lists(), ptr);\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* SequenceExample::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.SequenceExample)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // .tensorflow.Features context = 1;\n  if (this->has_context()) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(\n        1, _Internal::context(this), target, stream);\n  }\n\n  // .tensorflow.FeatureLists feature_lists = 2;\n  if (this->has_feature_lists()) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(\n        2, _Internal::feature_lists(this), target, stream);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.SequenceExample)\n  return target;\n}\n\nsize_t SequenceExample::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.SequenceExample)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // .tensorflow.Features context = 1;\n  if (this->has_context()) {\n    total_size += 1 +\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(\n        *context_);\n  }\n\n  // .tensorflow.FeatureLists feature_lists = 2;\n  if (this->has_feature_lists()) {\n    total_size += 1 +\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(\n        *feature_lists_);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid SequenceExample::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.SequenceExample)\n  GOOGLE_DCHECK_NE(&from, this);\n  const SequenceExample* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<SequenceExample>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.SequenceExample)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.SequenceExample)\n    MergeFrom(*source);\n  }\n}\n\nvoid SequenceExample::MergeFrom(const SequenceExample& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.SequenceExample)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  if (from.has_context()) {\n    _internal_mutable_context()->::tensorflow::Features::MergeFrom(from._internal_context());\n  }\n  if (from.has_feature_lists()) {\n    _internal_mutable_feature_lists()->::tensorflow::FeatureLists::MergeFrom(from._internal_feature_lists());\n  }\n}\n\nvoid SequenceExample::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.SequenceExample)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid SequenceExample::CopyFrom(const SequenceExample& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.SequenceExample)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool SequenceExample::IsInitialized() const {\n  return true;\n}\n\nvoid SequenceExample::InternalSwap(SequenceExample* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::internal::memswap<\n      PROTOBUF_FIELD_OFFSET(SequenceExample, feature_lists_)\n      + sizeof(SequenceExample::feature_lists_)\n      - PROTOBUF_FIELD_OFFSET(SequenceExample, context_)>(\n          reinterpret_cast<char*>(&context_),\n          reinterpret_cast<char*>(&other->context_));\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata SequenceExample::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// @@protoc_insertion_point(namespace_scope)\n}  // namespace tensorflow\nPROTOBUF_NAMESPACE_OPEN\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::Example* Arena::CreateMaybeMessage< ::tensorflow::Example >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::Example >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::SequenceExample* Arena::CreateMaybeMessage< ::tensorflow::SequenceExample >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::SequenceExample >(arena);\n}\nPROTOBUF_NAMESPACE_CLOSE\n\n// @@protoc_insertion_point(global_scope)\n#include <google/protobuf/port_undef.inc>\n"
  },
  {
    "path": "src/proto/example.pb.h",
    "content": "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: example.proto\n\n#ifndef GOOGLE_PROTOBUF_INCLUDED_example_2eproto\n#define GOOGLE_PROTOBUF_INCLUDED_example_2eproto\n\n#include <limits>\n#include <string>\n\n#include <google/protobuf/port_def.inc>\n#if PROTOBUF_VERSION < 3015000\n#error This file was generated by a newer version of protoc which is\n#error incompatible with your Protocol Buffer headers. Please update\n#error your headers.\n#endif\n#if 3015000 < PROTOBUF_MIN_PROTOC_VERSION\n#error This file was generated by an older version of protoc which is\n#error incompatible with your Protocol Buffer headers. Please\n#error regenerate this file with a newer version of protoc.\n#endif\n\n#include <google/protobuf/port_undef.inc>\n#include <google/protobuf/io/coded_stream.h>\n#include <google/protobuf/arena.h>\n#include <google/protobuf/arenastring.h>\n#include <google/protobuf/generated_message_table_driven.h>\n#include <google/protobuf/generated_message_util.h>\n#include <google/protobuf/metadata_lite.h>\n#include <google/protobuf/generated_message_reflection.h>\n#include <google/protobuf/message.h>\n#include <google/protobuf/repeated_field.h>  // IWYU pragma: export\n#include <google/protobuf/extension_set.h>  // IWYU pragma: export\n#include <google/protobuf/unknown_field_set.h>\n#include \"feature.pb.h\"\n// @@protoc_insertion_point(includes)\n#include <google/protobuf/port_def.inc>\n#define PROTOBUF_INTERNAL_EXPORT_example_2eproto\nPROTOBUF_NAMESPACE_OPEN\nnamespace internal {\nclass AnyMetadata;\n}  // namespace internal\nPROTOBUF_NAMESPACE_CLOSE\n\n// Internal implementation detail -- do not use these members.\nstruct TableStruct_example_2eproto {\n  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]\n    PROTOBUF_SECTION_VARIABLE(protodesc_cold);\n  static const ::PROTOBUF_NAMESPACE_ID::internal::AuxiliaryParseTableField aux[]\n    PROTOBUF_SECTION_VARIABLE(protodesc_cold);\n  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[2]\n    PROTOBUF_SECTION_VARIABLE(protodesc_cold);\n  static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];\n  static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];\n  static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];\n};\nextern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_example_2eproto;\n::PROTOBUF_NAMESPACE_ID::Metadata descriptor_table_example_2eproto_metadata_getter(int index);\nnamespace tensorflow {\nclass Example;\nstruct ExampleDefaultTypeInternal;\nextern ExampleDefaultTypeInternal _Example_default_instance_;\nclass SequenceExample;\nstruct SequenceExampleDefaultTypeInternal;\nextern SequenceExampleDefaultTypeInternal _SequenceExample_default_instance_;\n}  // namespace tensorflow\nPROTOBUF_NAMESPACE_OPEN\ntemplate<> ::tensorflow::Example* Arena::CreateMaybeMessage<::tensorflow::Example>(Arena*);\ntemplate<> ::tensorflow::SequenceExample* Arena::CreateMaybeMessage<::tensorflow::SequenceExample>(Arena*);\nPROTOBUF_NAMESPACE_CLOSE\nnamespace tensorflow {\n\n// ===================================================================\n\nclass Example PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.Example) */ {\n public:\n  inline Example() : Example(nullptr) {}\n  virtual ~Example();\n  explicit constexpr Example(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  Example(const Example& from);\n  Example(Example&& from) noexcept\n    : Example() {\n    *this = ::std::move(from);\n  }\n\n  inline Example& operator=(const Example& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline Example& operator=(Example&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const Example& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const Example* internal_default_instance() {\n    return reinterpret_cast<const Example*>(\n               &_Example_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    0;\n\n  friend void swap(Example& a, Example& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(Example* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(Example* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline Example* New() const final {\n    return CreateMaybeMessage<Example>(nullptr);\n  }\n\n  Example* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<Example>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const Example& from);\n  void MergeFrom(const Example& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(Example* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.Example\";\n  }\n  protected:\n  explicit Example(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_example_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kFeaturesFieldNumber = 1,\n  };\n  // .tensorflow.Features features = 1;\n  bool has_features() const;\n  private:\n  bool _internal_has_features() const;\n  public:\n  void clear_features();\n  const ::tensorflow::Features& features() const;\n  ::tensorflow::Features* release_features();\n  ::tensorflow::Features* mutable_features();\n  void set_allocated_features(::tensorflow::Features* features);\n  private:\n  const ::tensorflow::Features& _internal_features() const;\n  ::tensorflow::Features* _internal_mutable_features();\n  public:\n  void unsafe_arena_set_allocated_features(\n      ::tensorflow::Features* features);\n  ::tensorflow::Features* unsafe_arena_release_features();\n\n  // @@protoc_insertion_point(class_scope:tensorflow.Example)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::tensorflow::Features* features_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_example_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass SequenceExample PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.SequenceExample) */ {\n public:\n  inline SequenceExample() : SequenceExample(nullptr) {}\n  virtual ~SequenceExample();\n  explicit constexpr SequenceExample(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  SequenceExample(const SequenceExample& from);\n  SequenceExample(SequenceExample&& from) noexcept\n    : SequenceExample() {\n    *this = ::std::move(from);\n  }\n\n  inline SequenceExample& operator=(const SequenceExample& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline SequenceExample& operator=(SequenceExample&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const SequenceExample& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const SequenceExample* internal_default_instance() {\n    return reinterpret_cast<const SequenceExample*>(\n               &_SequenceExample_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    1;\n\n  friend void swap(SequenceExample& a, SequenceExample& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(SequenceExample* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(SequenceExample* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline SequenceExample* New() const final {\n    return CreateMaybeMessage<SequenceExample>(nullptr);\n  }\n\n  SequenceExample* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<SequenceExample>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const SequenceExample& from);\n  void MergeFrom(const SequenceExample& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(SequenceExample* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.SequenceExample\";\n  }\n  protected:\n  explicit SequenceExample(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_example_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kContextFieldNumber = 1,\n    kFeatureListsFieldNumber = 2,\n  };\n  // .tensorflow.Features context = 1;\n  bool has_context() const;\n  private:\n  bool _internal_has_context() const;\n  public:\n  void clear_context();\n  const ::tensorflow::Features& context() const;\n  ::tensorflow::Features* release_context();\n  ::tensorflow::Features* mutable_context();\n  void set_allocated_context(::tensorflow::Features* context);\n  private:\n  const ::tensorflow::Features& _internal_context() const;\n  ::tensorflow::Features* _internal_mutable_context();\n  public:\n  void unsafe_arena_set_allocated_context(\n      ::tensorflow::Features* context);\n  ::tensorflow::Features* unsafe_arena_release_context();\n\n  // .tensorflow.FeatureLists feature_lists = 2;\n  bool has_feature_lists() const;\n  private:\n  bool _internal_has_feature_lists() const;\n  public:\n  void clear_feature_lists();\n  const ::tensorflow::FeatureLists& feature_lists() const;\n  ::tensorflow::FeatureLists* release_feature_lists();\n  ::tensorflow::FeatureLists* mutable_feature_lists();\n  void set_allocated_feature_lists(::tensorflow::FeatureLists* feature_lists);\n  private:\n  const ::tensorflow::FeatureLists& _internal_feature_lists() const;\n  ::tensorflow::FeatureLists* _internal_mutable_feature_lists();\n  public:\n  void unsafe_arena_set_allocated_feature_lists(\n      ::tensorflow::FeatureLists* feature_lists);\n  ::tensorflow::FeatureLists* unsafe_arena_release_feature_lists();\n\n  // @@protoc_insertion_point(class_scope:tensorflow.SequenceExample)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::tensorflow::Features* context_;\n  ::tensorflow::FeatureLists* feature_lists_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_example_2eproto;\n};\n// ===================================================================\n\n\n// ===================================================================\n\n#ifdef __GNUC__\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n#endif  // __GNUC__\n// Example\n\n// .tensorflow.Features features = 1;\ninline bool Example::_internal_has_features() const {\n  return this != internal_default_instance() && features_ != nullptr;\n}\ninline bool Example::has_features() const {\n  return _internal_has_features();\n}\ninline const ::tensorflow::Features& Example::_internal_features() const {\n  const ::tensorflow::Features* p = features_;\n  return p != nullptr ? *p : reinterpret_cast<const ::tensorflow::Features&>(\n      ::tensorflow::_Features_default_instance_);\n}\ninline const ::tensorflow::Features& Example::features() const {\n  // @@protoc_insertion_point(field_get:tensorflow.Example.features)\n  return _internal_features();\n}\ninline void Example::unsafe_arena_set_allocated_features(\n    ::tensorflow::Features* features) {\n  if (GetArena() == nullptr) {\n    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(features_);\n  }\n  features_ = features;\n  if (features) {\n    \n  } else {\n    \n  }\n  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:tensorflow.Example.features)\n}\ninline ::tensorflow::Features* Example::release_features() {\n  \n  ::tensorflow::Features* temp = features_;\n  features_ = nullptr;\n  if (GetArena() != nullptr) {\n    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);\n  }\n  return temp;\n}\ninline ::tensorflow::Features* Example::unsafe_arena_release_features() {\n  // @@protoc_insertion_point(field_release:tensorflow.Example.features)\n  \n  ::tensorflow::Features* temp = features_;\n  features_ = nullptr;\n  return temp;\n}\ninline ::tensorflow::Features* Example::_internal_mutable_features() {\n  \n  if (features_ == nullptr) {\n    auto* p = CreateMaybeMessage<::tensorflow::Features>(GetArena());\n    features_ = p;\n  }\n  return features_;\n}\ninline ::tensorflow::Features* Example::mutable_features() {\n  // @@protoc_insertion_point(field_mutable:tensorflow.Example.features)\n  return _internal_mutable_features();\n}\ninline void Example::set_allocated_features(::tensorflow::Features* features) {\n  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena();\n  if (message_arena == nullptr) {\n    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(features_);\n  }\n  if (features) {\n    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =\n      reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(features)->GetArena();\n    if (message_arena != submessage_arena) {\n      features = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(\n          message_arena, features, submessage_arena);\n    }\n    \n  } else {\n    \n  }\n  features_ = features;\n  // @@protoc_insertion_point(field_set_allocated:tensorflow.Example.features)\n}\n\n// -------------------------------------------------------------------\n\n// SequenceExample\n\n// .tensorflow.Features context = 1;\ninline bool SequenceExample::_internal_has_context() const {\n  return this != internal_default_instance() && context_ != nullptr;\n}\ninline bool SequenceExample::has_context() const {\n  return _internal_has_context();\n}\ninline const ::tensorflow::Features& SequenceExample::_internal_context() const {\n  const ::tensorflow::Features* p = context_;\n  return p != nullptr ? *p : reinterpret_cast<const ::tensorflow::Features&>(\n      ::tensorflow::_Features_default_instance_);\n}\ninline const ::tensorflow::Features& SequenceExample::context() const {\n  // @@protoc_insertion_point(field_get:tensorflow.SequenceExample.context)\n  return _internal_context();\n}\ninline void SequenceExample::unsafe_arena_set_allocated_context(\n    ::tensorflow::Features* context) {\n  if (GetArena() == nullptr) {\n    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(context_);\n  }\n  context_ = context;\n  if (context) {\n    \n  } else {\n    \n  }\n  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:tensorflow.SequenceExample.context)\n}\ninline ::tensorflow::Features* SequenceExample::release_context() {\n  \n  ::tensorflow::Features* temp = context_;\n  context_ = nullptr;\n  if (GetArena() != nullptr) {\n    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);\n  }\n  return temp;\n}\ninline ::tensorflow::Features* SequenceExample::unsafe_arena_release_context() {\n  // @@protoc_insertion_point(field_release:tensorflow.SequenceExample.context)\n  \n  ::tensorflow::Features* temp = context_;\n  context_ = nullptr;\n  return temp;\n}\ninline ::tensorflow::Features* SequenceExample::_internal_mutable_context() {\n  \n  if (context_ == nullptr) {\n    auto* p = CreateMaybeMessage<::tensorflow::Features>(GetArena());\n    context_ = p;\n  }\n  return context_;\n}\ninline ::tensorflow::Features* SequenceExample::mutable_context() {\n  // @@protoc_insertion_point(field_mutable:tensorflow.SequenceExample.context)\n  return _internal_mutable_context();\n}\ninline void SequenceExample::set_allocated_context(::tensorflow::Features* context) {\n  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena();\n  if (message_arena == nullptr) {\n    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(context_);\n  }\n  if (context) {\n    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =\n      reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(context)->GetArena();\n    if (message_arena != submessage_arena) {\n      context = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(\n          message_arena, context, submessage_arena);\n    }\n    \n  } else {\n    \n  }\n  context_ = context;\n  // @@protoc_insertion_point(field_set_allocated:tensorflow.SequenceExample.context)\n}\n\n// .tensorflow.FeatureLists feature_lists = 2;\ninline bool SequenceExample::_internal_has_feature_lists() const {\n  return this != internal_default_instance() && feature_lists_ != nullptr;\n}\ninline bool SequenceExample::has_feature_lists() const {\n  return _internal_has_feature_lists();\n}\ninline const ::tensorflow::FeatureLists& SequenceExample::_internal_feature_lists() const {\n  const ::tensorflow::FeatureLists* p = feature_lists_;\n  return p != nullptr ? *p : reinterpret_cast<const ::tensorflow::FeatureLists&>(\n      ::tensorflow::_FeatureLists_default_instance_);\n}\ninline const ::tensorflow::FeatureLists& SequenceExample::feature_lists() const {\n  // @@protoc_insertion_point(field_get:tensorflow.SequenceExample.feature_lists)\n  return _internal_feature_lists();\n}\ninline void SequenceExample::unsafe_arena_set_allocated_feature_lists(\n    ::tensorflow::FeatureLists* feature_lists) {\n  if (GetArena() == nullptr) {\n    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(feature_lists_);\n  }\n  feature_lists_ = feature_lists;\n  if (feature_lists) {\n    \n  } else {\n    \n  }\n  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:tensorflow.SequenceExample.feature_lists)\n}\ninline ::tensorflow::FeatureLists* SequenceExample::release_feature_lists() {\n  \n  ::tensorflow::FeatureLists* temp = feature_lists_;\n  feature_lists_ = nullptr;\n  if (GetArena() != nullptr) {\n    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);\n  }\n  return temp;\n}\ninline ::tensorflow::FeatureLists* SequenceExample::unsafe_arena_release_feature_lists() {\n  // @@protoc_insertion_point(field_release:tensorflow.SequenceExample.feature_lists)\n  \n  ::tensorflow::FeatureLists* temp = feature_lists_;\n  feature_lists_ = nullptr;\n  return temp;\n}\ninline ::tensorflow::FeatureLists* SequenceExample::_internal_mutable_feature_lists() {\n  \n  if (feature_lists_ == nullptr) {\n    auto* p = CreateMaybeMessage<::tensorflow::FeatureLists>(GetArena());\n    feature_lists_ = p;\n  }\n  return feature_lists_;\n}\ninline ::tensorflow::FeatureLists* SequenceExample::mutable_feature_lists() {\n  // @@protoc_insertion_point(field_mutable:tensorflow.SequenceExample.feature_lists)\n  return _internal_mutable_feature_lists();\n}\ninline void SequenceExample::set_allocated_feature_lists(::tensorflow::FeatureLists* feature_lists) {\n  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena();\n  if (message_arena == nullptr) {\n    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(feature_lists_);\n  }\n  if (feature_lists) {\n    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =\n      reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(feature_lists)->GetArena();\n    if (message_arena != submessage_arena) {\n      feature_lists = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(\n          message_arena, feature_lists, submessage_arena);\n    }\n    \n  } else {\n    \n  }\n  feature_lists_ = feature_lists;\n  // @@protoc_insertion_point(field_set_allocated:tensorflow.SequenceExample.feature_lists)\n}\n\n#ifdef __GNUC__\n  #pragma GCC diagnostic pop\n#endif  // __GNUC__\n// -------------------------------------------------------------------\n\n\n// @@protoc_insertion_point(namespace_scope)\n\n}  // namespace tensorflow\n\n// @@protoc_insertion_point(global_scope)\n\n#include <google/protobuf/port_undef.inc>\n#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_example_2eproto\n"
  },
  {
    "path": "src/proto/example.proto",
    "content": "// Protocol messages for describing input data Examples for machine learning\n// model training or inference.\nsyntax = \"proto3\";\n\npackage tensorflow;\n\nimport \"feature.proto\";\n\noption cc_enable_arenas = true;\noption java_outer_classname = \"ExampleProtos\";\noption java_multiple_files = true;\noption java_package = \"org.tensorflow.example\";\noption go_package = \"github.com/tensorflow/tensorflow/tensorflow/go/core/example/example_protos_go_proto\";\n\n// LINT.IfChange\n// An Example is a mostly-normalized data format for storing data for\n// training and inference.  It contains a key-value store (features); where\n// each key (string) maps to a Feature message (which is oneof packed BytesList,\n// FloatList, or Int64List).  This flexible and compact format allows the\n// storage of large amounts of typed data, but requires that the data shape\n// and use be determined by the configuration files and parsers that are used to\n// read and write this format.  That is, the Example is mostly *not* a\n// self-describing format.  In TensorFlow, Examples are read in row-major\n// format, so any configuration that describes data with rank-2 or above\n// should keep this in mind.  For example, to store an M x N matrix of Bytes,\n// the BytesList must contain M*N bytes, with M rows of N contiguous values\n// each.  That is, the BytesList value must store the matrix as:\n//     .... row 0 .... .... row 1 .... // ...........  // ... row M-1 ....\n//\n// An Example for a movie recommendation application:\n//   features {\n//     feature {\n//       key: \"age\"\n//       value { float_list {\n//         value: 29.0\n//       }}\n//     }\n//     feature {\n//       key: \"movie\"\n//       value { bytes_list {\n//         value: \"The Shawshank Redemption\"\n//         value: \"Fight Club\"\n//       }}\n//     }\n//     feature {\n//       key: \"movie_ratings\"\n//       value { float_list {\n//         value: 9.0\n//         value: 9.7\n//       }}\n//     }\n//     feature {\n//       key: \"suggestion\"\n//       value { bytes_list {\n//         value: \"Inception\"\n//       }}\n//     }\n//     # Note that this feature exists to be used as a label in training.\n//     # E.g., if training a logistic regression model to predict purchase\n//     # probability in our learning tool we would set the label feature to\n//     # \"suggestion_purchased\".\n//     feature {\n//       key: \"suggestion_purchased\"\n//       value { float_list {\n//         value: 1.0\n//       }}\n//     }\n//     # Similar to \"suggestion_purchased\" above this feature exists to be used\n//     # as a label in training.\n//     # E.g., if training a linear regression model to predict purchase\n//     # price in our learning tool we would set the label feature to\n//     # \"purchase_price\".\n//     feature {\n//       key: \"purchase_price\"\n//       value { float_list {\n//         value: 9.99\n//       }}\n//     }\n//  }\n//\n// A conformant Example data set obeys the following conventions:\n//   - If a Feature K exists in one example with data type T, it must be of\n//       type T in all other examples when present. It may be omitted.\n//   - The number of instances of Feature K list data may vary across examples,\n//       depending on the requirements of the model.\n//   - If a Feature K doesn't exist in an example, a K-specific default will be\n//       used, if configured.\n//   - If a Feature K exists in an example but contains no items, the intent\n//       is considered to be an empty tensor and no default will be used.\n\nmessage Example {\n  Features features = 1;\n}\n\n// A SequenceExample is an Example representing one or more sequences, and\n// some context.  The context contains features which apply to the entire\n// example. The feature_lists contain a key, value map where each key is\n// associated with a repeated set of Features (a FeatureList).\n// A FeatureList thus represents the values of a feature identified by its key\n// over time / frames.\n//\n// Below is a SequenceExample for a movie recommendation application recording a\n// sequence of ratings by a user. The time-independent features (\"locale\",\n// \"age\", \"favorites\") describing the user are part of the context. The sequence\n// of movies the user rated are part of the feature_lists. For each movie in the\n// sequence we have information on its name and actors and the user's rating.\n// This information is recorded in three separate feature_list(s).\n// In the example below there are only two movies. All three feature_list(s),\n// namely \"movie_ratings\", \"movie_names\", and \"actors\" have a feature value for\n// both movies. Note, that \"actors\" is itself a bytes_list with multiple\n// strings per movie.\n//\n// context: {\n//   feature: {\n//     key  : \"locale\"\n//     value: {\n//       bytes_list: {\n//         value: [ \"pt_BR\" ]\n//       }\n//     }\n//   }\n//   feature: {\n//     key  : \"age\"\n//     value: {\n//       float_list: {\n//         value: [ 19.0 ]\n//       }\n//     }\n//   }\n//   feature: {\n//     key  : \"favorites\"\n//     value: {\n//       bytes_list: {\n//         value: [ \"Majesty Rose\", \"Savannah Outen\", \"One Direction\" ]\n//       }\n//     }\n//   }\n// }\n// feature_lists: {\n//   feature_list: {\n//     key  : \"movie_ratings\"\n//     value: {\n//       feature: {\n//         float_list: {\n//           value: [ 4.5 ]\n//         }\n//       }\n//       feature: {\n//         float_list: {\n//           value: [ 5.0 ]\n//         }\n//       }\n//     }\n//   }\n//   feature_list: {\n//     key  : \"movie_names\"\n//     value: {\n//       feature: {\n//         bytes_list: {\n//           value: [ \"The Shawshank Redemption\" ]\n//         }\n//       }\n//       feature: {\n//         bytes_list: {\n//           value: [ \"Fight Club\" ]\n//         }\n//       }\n//     }\n//   }\n//   feature_list: {\n//     key  : \"actors\"\n//     value: {\n//       feature: {\n//         bytes_list: {\n//           value: [ \"Tim Robbins\", \"Morgan Freeman\" ]\n//         }\n//       }\n//       feature: {\n//         bytes_list: {\n//           value: [ \"Brad Pitt\", \"Edward Norton\", \"Helena Bonham Carter\" ]\n//         }\n//       }\n//     }\n//   }\n// }\n//\n// A conformant SequenceExample data set obeys the following conventions:\n//\n// Context:\n//   - All conformant context features K must obey the same conventions as\n//     a conformant Example's features (see above).\n// Feature lists:\n//   - A FeatureList L may be missing in an example; it is up to the\n//     parser configuration to determine if this is allowed or considered\n//     an empty list (zero length).\n//   - If a FeatureList L exists, it may be empty (zero length).\n//   - If a FeatureList L is non-empty, all features within the FeatureList\n//     must have the same data type T. Even across SequenceExamples, the type T\n//     of the FeatureList identified by the same key must be the same. An entry\n//     without any values may serve as an empty feature.\n//   - If a FeatureList L is non-empty, it is up to the parser configuration\n//     to determine if all features within the FeatureList must\n//     have the same size.  The same holds for this FeatureList across multiple\n//     examples.\n//   - For sequence modeling, e.g.:\n//        http://colah.github.io/posts/2015-08-Understanding-LSTMs/\n//        https://github.com/tensorflow/nmt\n//     the feature lists represent a sequence of frames.\n//     In this scenario, all FeatureLists in a SequenceExample have the same\n//     number of Feature messages, so that the ith element in each FeatureList\n//     is part of the ith frame (or time step).\n// Examples of conformant and non-conformant examples' FeatureLists:\n//\n// Conformant FeatureLists:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } } }\n//    } }\n//\n// Non-conformant FeatureLists (mismatched types):\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { int64_list: { value: [ 5 ] } } }\n//    } }\n//\n// Conditionally conformant FeatureLists, the parser configuration determines\n// if the feature sizes must match:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0, 6.0 ] } } }\n//    } }\n//\n// Conformant pair of SequenceExample\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } } }\n//    } }\n// and:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } }\n//               feature: { float_list: { value: [ 2.0 ] } } }\n//    } }\n//\n// Conformant pair of SequenceExample\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } } }\n//    } }\n// and:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { }\n//    } }\n//\n// Conditionally conformant pair of SequenceExample, the parser configuration\n// determines if the second feature_lists is consistent (zero-length) or\n// invalid (missing \"movie_ratings\"):\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } } }\n//    } }\n// and:\n//    feature_lists: { }\n//\n// Non-conformant pair of SequenceExample (mismatched types)\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } } }\n//    } }\n// and:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { int64_list: { value: [ 4 ] } }\n//               feature: { int64_list: { value: [ 5 ] } }\n//               feature: { int64_list: { value: [ 2 ] } } }\n//    } }\n//\n// Conditionally conformant pair of SequenceExample; the parser configuration\n// determines if the feature sizes must match:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.5 ] } }\n//               feature: { float_list: { value: [ 5.0 ] } } }\n//    } }\n// and:\n//    feature_lists: { feature_list: {\n//      key: \"movie_ratings\"\n//      value: { feature: { float_list: { value: [ 4.0 ] } }\n//               feature: { float_list: { value: [ 5.0, 3.0 ] } }\n//    } }\n\nmessage SequenceExample {\n  Features context = 1;\n  FeatureLists feature_lists = 2;\n}\n// LINT.ThenChange(\n//     https://www.tensorflow.org/code/tensorflow/python/training/training.py)\n"
  },
  {
    "path": "src/proto/feature.pb.cc",
    "content": "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: feature.proto\n\n#include \"feature.pb.h\"\n\n#include <algorithm>\n\n#include <google/protobuf/io/coded_stream.h>\n#include <google/protobuf/extension_set.h>\n#include <google/protobuf/wire_format_lite.h>\n#include <google/protobuf/descriptor.h>\n#include <google/protobuf/generated_message_reflection.h>\n#include <google/protobuf/reflection_ops.h>\n#include <google/protobuf/wire_format.h>\n// @@protoc_insertion_point(includes)\n#include <google/protobuf/port_def.inc>\n\nPROTOBUF_PRAGMA_INIT_SEG\nnamespace tensorflow {\nconstexpr BytesList::BytesList(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : value_(){}\nstruct BytesListDefaultTypeInternal {\n  constexpr BytesListDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~BytesListDefaultTypeInternal() {}\n  union {\n    BytesList _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT BytesListDefaultTypeInternal _BytesList_default_instance_;\nconstexpr FloatList::FloatList(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : value_()\n  , _value_cached_byte_size_(){}\nstruct FloatListDefaultTypeInternal {\n  constexpr FloatListDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~FloatListDefaultTypeInternal() {}\n  union {\n    FloatList _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT FloatListDefaultTypeInternal _FloatList_default_instance_;\nconstexpr Int64List::Int64List(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : value_()\n  , _value_cached_byte_size_(){}\nstruct Int64ListDefaultTypeInternal {\n  constexpr Int64ListDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~Int64ListDefaultTypeInternal() {}\n  union {\n    Int64List _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT Int64ListDefaultTypeInternal _Int64List_default_instance_;\nconstexpr Feature::Feature(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : _oneof_case_{}{}\nstruct FeatureDefaultTypeInternal {\n  constexpr FeatureDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~FeatureDefaultTypeInternal() {}\n  union {\n    Feature _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT FeatureDefaultTypeInternal _Feature_default_instance_;\nconstexpr Features_FeatureEntry_DoNotUse::Features_FeatureEntry_DoNotUse(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized){}\nstruct Features_FeatureEntry_DoNotUseDefaultTypeInternal {\n  constexpr Features_FeatureEntry_DoNotUseDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~Features_FeatureEntry_DoNotUseDefaultTypeInternal() {}\n  union {\n    Features_FeatureEntry_DoNotUse _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT Features_FeatureEntry_DoNotUseDefaultTypeInternal _Features_FeatureEntry_DoNotUse_default_instance_;\nconstexpr Features::Features(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : feature_(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}){}\nstruct FeaturesDefaultTypeInternal {\n  constexpr FeaturesDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~FeaturesDefaultTypeInternal() {}\n  union {\n    Features _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT FeaturesDefaultTypeInternal _Features_default_instance_;\nconstexpr FeatureList::FeatureList(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : feature_(){}\nstruct FeatureListDefaultTypeInternal {\n  constexpr FeatureListDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~FeatureListDefaultTypeInternal() {}\n  union {\n    FeatureList _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT FeatureListDefaultTypeInternal _FeatureList_default_instance_;\nconstexpr FeatureLists_FeatureListEntry_DoNotUse::FeatureLists_FeatureListEntry_DoNotUse(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized){}\nstruct FeatureLists_FeatureListEntry_DoNotUseDefaultTypeInternal {\n  constexpr FeatureLists_FeatureListEntry_DoNotUseDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~FeatureLists_FeatureListEntry_DoNotUseDefaultTypeInternal() {}\n  union {\n    FeatureLists_FeatureListEntry_DoNotUse _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT FeatureLists_FeatureListEntry_DoNotUseDefaultTypeInternal _FeatureLists_FeatureListEntry_DoNotUse_default_instance_;\nconstexpr FeatureLists::FeatureLists(\n  ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized)\n  : feature_list_(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}){}\nstruct FeatureListsDefaultTypeInternal {\n  constexpr FeatureListsDefaultTypeInternal()\n    : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {}\n  ~FeatureListsDefaultTypeInternal() {}\n  union {\n    FeatureLists _instance;\n  };\n};\nPROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT FeatureListsDefaultTypeInternal _FeatureLists_default_instance_;\n}  // namespace tensorflow\nstatic ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_feature_2eproto[9];\nstatic constexpr ::PROTOBUF_NAMESPACE_ID::EnumDescriptor const** file_level_enum_descriptors_feature_2eproto = nullptr;\nstatic constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_feature_2eproto = nullptr;\n\nconst ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_feature_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::BytesList, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::BytesList, value_),\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FloatList, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FloatList, value_),\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Int64List, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Int64List, value_),\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Feature, _internal_metadata_),\n  ~0u,  // no _extensions_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Feature, _oneof_case_[0]),\n  ~0u,  // no _weak_field_map_\n  ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag,\n  ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag,\n  ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag,\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Feature, kind_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Features_FeatureEntry_DoNotUse, _has_bits_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Features_FeatureEntry_DoNotUse, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Features_FeatureEntry_DoNotUse, key_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Features_FeatureEntry_DoNotUse, value_),\n  0,\n  1,\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Features, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::Features, feature_),\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureList, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureList, feature_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureLists_FeatureListEntry_DoNotUse, _has_bits_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureLists_FeatureListEntry_DoNotUse, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureLists_FeatureListEntry_DoNotUse, key_),\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureLists_FeatureListEntry_DoNotUse, value_),\n  0,\n  1,\n  ~0u,  // no _has_bits_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureLists, _internal_metadata_),\n  ~0u,  // no _extensions_\n  ~0u,  // no _oneof_case_\n  ~0u,  // no _weak_field_map_\n  PROTOBUF_FIELD_OFFSET(::tensorflow::FeatureLists, feature_list_),\n};\nstatic const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {\n  { 0, -1, sizeof(::tensorflow::BytesList)},\n  { 6, -1, sizeof(::tensorflow::FloatList)},\n  { 12, -1, sizeof(::tensorflow::Int64List)},\n  { 18, -1, sizeof(::tensorflow::Feature)},\n  { 27, 34, sizeof(::tensorflow::Features_FeatureEntry_DoNotUse)},\n  { 36, -1, sizeof(::tensorflow::Features)},\n  { 42, -1, sizeof(::tensorflow::FeatureList)},\n  { 48, 55, sizeof(::tensorflow::FeatureLists_FeatureListEntry_DoNotUse)},\n  { 57, -1, sizeof(::tensorflow::FeatureLists)},\n};\n\nstatic ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = {\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_BytesList_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_FloatList_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_Int64List_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_Feature_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_Features_FeatureEntry_DoNotUse_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_Features_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_FeatureList_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_FeatureLists_FeatureListEntry_DoNotUse_default_instance_),\n  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tensorflow::_FeatureLists_default_instance_),\n};\n\nconst char descriptor_table_protodef_feature_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =\n  \"\\n\\rfeature.proto\\022\\ntensorflow\\\"\\032\\n\\tBytesList\"\n  \"\\022\\r\\n\\005value\\030\\001 \\003(\\014\\\"\\036\\n\\tFloatList\\022\\021\\n\\005value\\030\\001 \"\n  \"\\003(\\002B\\002\\020\\001\\\"\\036\\n\\tInt64List\\022\\021\\n\\005value\\030\\001 \\003(\\003B\\002\\020\\001\\\"\"\n  \"\\230\\001\\n\\007Feature\\022+\\n\\nbytes_list\\030\\001 \\001(\\0132\\025.tensor\"\n  \"flow.BytesListH\\000\\022+\\n\\nfloat_list\\030\\002 \\001(\\0132\\025.t\"\n  \"ensorflow.FloatListH\\000\\022+\\n\\nint64_list\\030\\003 \\001(\"\n  \"\\0132\\025.tensorflow.Int64ListH\\000B\\006\\n\\004kind\\\"\\203\\001\\n\\010F\"\n  \"eatures\\0222\\n\\007feature\\030\\001 \\003(\\0132!.tensorflow.Fe\"\n  \"atures.FeatureEntry\\032C\\n\\014FeatureEntry\\022\\013\\n\\003k\"\n  \"ey\\030\\001 \\001(\\t\\022\\\"\\n\\005value\\030\\002 \\001(\\0132\\023.tensorflow.Fea\"\n  \"ture:\\0028\\001\\\"3\\n\\013FeatureList\\022$\\n\\007feature\\030\\001 \\003(\\013\"\n  \"2\\023.tensorflow.Feature\\\"\\234\\001\\n\\014FeatureLists\\022\\?\"\n  \"\\n\\014feature_list\\030\\001 \\003(\\0132).tensorflow.Featur\"\n  \"eLists.FeatureListEntry\\032K\\n\\020FeatureListEn\"\n  \"try\\022\\013\\n\\003key\\030\\001 \\001(\\t\\022&\\n\\005value\\030\\002 \\001(\\0132\\027.tensor\"\n  \"flow.FeatureList:\\0028\\001B\\201\\001\\n\\026org.tensorflow.\"\n  \"exampleB\\rFeatureProtosP\\001ZSgithub.com/ten\"\n  \"sorflow/tensorflow/tensorflow/go/core/ex\"\n  \"ample/example_protos_go_proto\\370\\001\\001b\\006proto3\"\n  ;\nstatic ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_feature_2eproto_once;\nconst ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_feature_2eproto = {\n  false, false, 760, descriptor_table_protodef_feature_2eproto, \"feature.proto\", \n  &descriptor_table_feature_2eproto_once, nullptr, 0, 9,\n  schemas, file_default_instances, TableStruct_feature_2eproto::offsets,\n  file_level_metadata_feature_2eproto, file_level_enum_descriptors_feature_2eproto, file_level_service_descriptors_feature_2eproto,\n};\nPROTOBUF_ATTRIBUTE_WEAK ::PROTOBUF_NAMESPACE_ID::Metadata\ndescriptor_table_feature_2eproto_metadata_getter(int index) {\n  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_feature_2eproto);\n  return descriptor_table_feature_2eproto.file_level_metadata[index];\n}\n\n// Force running AddDescriptors() at dynamic initialization time.\nPROTOBUF_ATTRIBUTE_INIT_PRIORITY static ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptorsRunner dynamic_init_dummy_feature_2eproto(&descriptor_table_feature_2eproto);\nnamespace tensorflow {\n\n// ===================================================================\n\nclass BytesList::_Internal {\n public:\n};\n\nBytesList::BytesList(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena),\n  value_(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.BytesList)\n}\nBytesList::BytesList(const BytesList& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message(),\n      value_(from.value_) {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  // @@protoc_insertion_point(copy_constructor:tensorflow.BytesList)\n}\n\nvoid BytesList::SharedCtor() {\n}\n\nBytesList::~BytesList() {\n  // @@protoc_insertion_point(destructor:tensorflow.BytesList)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid BytesList::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n}\n\nvoid BytesList::ArenaDtor(void* object) {\n  BytesList* _this = reinterpret_cast< BytesList* >(object);\n  (void)_this;\n}\nvoid BytesList::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid BytesList::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid BytesList::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.BytesList)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  value_.Clear();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* BytesList::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // repeated bytes value = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr -= 1;\n          do {\n            ptr += 1;\n            auto str = _internal_add_value();\n            ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);\n            CHK_(ptr);\n            if (!ctx->DataAvailable(ptr)) break;\n          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* BytesList::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.BytesList)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // repeated bytes value = 1;\n  for (int i = 0, n = this->_internal_value_size(); i < n; i++) {\n    const auto& s = this->_internal_value(i);\n    target = stream->WriteBytes(1, s, target);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.BytesList)\n  return target;\n}\n\nsize_t BytesList::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.BytesList)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // repeated bytes value = 1;\n  total_size += 1 *\n      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(value_.size());\n  for (int i = 0, n = value_.size(); i < n; i++) {\n    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(\n      value_.Get(i));\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid BytesList::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.BytesList)\n  GOOGLE_DCHECK_NE(&from, this);\n  const BytesList* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<BytesList>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.BytesList)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.BytesList)\n    MergeFrom(*source);\n  }\n}\n\nvoid BytesList::MergeFrom(const BytesList& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.BytesList)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  value_.MergeFrom(from.value_);\n}\n\nvoid BytesList::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.BytesList)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid BytesList::CopyFrom(const BytesList& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.BytesList)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool BytesList::IsInitialized() const {\n  return true;\n}\n\nvoid BytesList::InternalSwap(BytesList* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  value_.InternalSwap(&other->value_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata BytesList::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nclass FloatList::_Internal {\n public:\n};\n\nFloatList::FloatList(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena),\n  value_(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.FloatList)\n}\nFloatList::FloatList(const FloatList& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message(),\n      value_(from.value_) {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  // @@protoc_insertion_point(copy_constructor:tensorflow.FloatList)\n}\n\nvoid FloatList::SharedCtor() {\n}\n\nFloatList::~FloatList() {\n  // @@protoc_insertion_point(destructor:tensorflow.FloatList)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid FloatList::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n}\n\nvoid FloatList::ArenaDtor(void* object) {\n  FloatList* _this = reinterpret_cast< FloatList* >(object);\n  (void)_this;\n}\nvoid FloatList::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid FloatList::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid FloatList::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.FloatList)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  value_.Clear();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* FloatList::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // repeated float value = 1 [packed = true];\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedFloatParser(_internal_mutable_value(), ptr, ctx);\n          CHK_(ptr);\n        } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 13) {\n          _internal_add_value(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr));\n          ptr += sizeof(float);\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* FloatList::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.FloatList)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // repeated float value = 1 [packed = true];\n  if (this->_internal_value_size() > 0) {\n    target = stream->WriteFixedPacked(1, _internal_value(), target);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.FloatList)\n  return target;\n}\n\nsize_t FloatList::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.FloatList)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // repeated float value = 1 [packed = true];\n  {\n    unsigned int count = static_cast<unsigned int>(this->_internal_value_size());\n    size_t data_size = 4UL * count;\n    if (data_size > 0) {\n      total_size += 1 +\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(\n            static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size));\n    }\n    int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size);\n    _value_cached_byte_size_.store(cached_size,\n                                    std::memory_order_relaxed);\n    total_size += data_size;\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid FloatList::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.FloatList)\n  GOOGLE_DCHECK_NE(&from, this);\n  const FloatList* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<FloatList>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.FloatList)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.FloatList)\n    MergeFrom(*source);\n  }\n}\n\nvoid FloatList::MergeFrom(const FloatList& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.FloatList)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  value_.MergeFrom(from.value_);\n}\n\nvoid FloatList::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.FloatList)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid FloatList::CopyFrom(const FloatList& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.FloatList)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool FloatList::IsInitialized() const {\n  return true;\n}\n\nvoid FloatList::InternalSwap(FloatList* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  value_.InternalSwap(&other->value_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata FloatList::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nclass Int64List::_Internal {\n public:\n};\n\nInt64List::Int64List(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena),\n  value_(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.Int64List)\n}\nInt64List::Int64List(const Int64List& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message(),\n      value_(from.value_) {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  // @@protoc_insertion_point(copy_constructor:tensorflow.Int64List)\n}\n\nvoid Int64List::SharedCtor() {\n}\n\nInt64List::~Int64List() {\n  // @@protoc_insertion_point(destructor:tensorflow.Int64List)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid Int64List::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n}\n\nvoid Int64List::ArenaDtor(void* object) {\n  Int64List* _this = reinterpret_cast< Int64List* >(object);\n  (void)_this;\n}\nvoid Int64List::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid Int64List::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid Int64List::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.Int64List)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  value_.Clear();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* Int64List::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // repeated int64 value = 1 [packed = true];\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(_internal_mutable_value(), ptr, ctx);\n          CHK_(ptr);\n        } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8) {\n          _internal_add_value(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr));\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* Int64List::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.Int64List)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // repeated int64 value = 1 [packed = true];\n  {\n    int byte_size = _value_cached_byte_size_.load(std::memory_order_relaxed);\n    if (byte_size > 0) {\n      target = stream->WriteInt64Packed(\n          1, _internal_value(), byte_size, target);\n    }\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.Int64List)\n  return target;\n}\n\nsize_t Int64List::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.Int64List)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // repeated int64 value = 1 [packed = true];\n  {\n    size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      Int64Size(this->value_);\n    if (data_size > 0) {\n      total_size += 1 +\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(\n            static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size));\n    }\n    int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size);\n    _value_cached_byte_size_.store(cached_size,\n                                    std::memory_order_relaxed);\n    total_size += data_size;\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid Int64List::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.Int64List)\n  GOOGLE_DCHECK_NE(&from, this);\n  const Int64List* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Int64List>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.Int64List)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.Int64List)\n    MergeFrom(*source);\n  }\n}\n\nvoid Int64List::MergeFrom(const Int64List& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.Int64List)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  value_.MergeFrom(from.value_);\n}\n\nvoid Int64List::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.Int64List)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid Int64List::CopyFrom(const Int64List& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.Int64List)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool Int64List::IsInitialized() const {\n  return true;\n}\n\nvoid Int64List::InternalSwap(Int64List* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  value_.InternalSwap(&other->value_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata Int64List::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nclass Feature::_Internal {\n public:\n  static const ::tensorflow::BytesList& bytes_list(const Feature* msg);\n  static const ::tensorflow::FloatList& float_list(const Feature* msg);\n  static const ::tensorflow::Int64List& int64_list(const Feature* msg);\n};\n\nconst ::tensorflow::BytesList&\nFeature::_Internal::bytes_list(const Feature* msg) {\n  return *msg->kind_.bytes_list_;\n}\nconst ::tensorflow::FloatList&\nFeature::_Internal::float_list(const Feature* msg) {\n  return *msg->kind_.float_list_;\n}\nconst ::tensorflow::Int64List&\nFeature::_Internal::int64_list(const Feature* msg) {\n  return *msg->kind_.int64_list_;\n}\nvoid Feature::set_allocated_bytes_list(::tensorflow::BytesList* bytes_list) {\n  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena();\n  clear_kind();\n  if (bytes_list) {\n    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =\n      ::PROTOBUF_NAMESPACE_ID::Arena::GetArena(bytes_list);\n    if (message_arena != submessage_arena) {\n      bytes_list = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(\n          message_arena, bytes_list, submessage_arena);\n    }\n    set_has_bytes_list();\n    kind_.bytes_list_ = bytes_list;\n  }\n  // @@protoc_insertion_point(field_set_allocated:tensorflow.Feature.bytes_list)\n}\nvoid Feature::set_allocated_float_list(::tensorflow::FloatList* float_list) {\n  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena();\n  clear_kind();\n  if (float_list) {\n    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =\n      ::PROTOBUF_NAMESPACE_ID::Arena::GetArena(float_list);\n    if (message_arena != submessage_arena) {\n      float_list = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(\n          message_arena, float_list, submessage_arena);\n    }\n    set_has_float_list();\n    kind_.float_list_ = float_list;\n  }\n  // @@protoc_insertion_point(field_set_allocated:tensorflow.Feature.float_list)\n}\nvoid Feature::set_allocated_int64_list(::tensorflow::Int64List* int64_list) {\n  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArena();\n  clear_kind();\n  if (int64_list) {\n    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =\n      ::PROTOBUF_NAMESPACE_ID::Arena::GetArena(int64_list);\n    if (message_arena != submessage_arena) {\n      int64_list = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(\n          message_arena, int64_list, submessage_arena);\n    }\n    set_has_int64_list();\n    kind_.int64_list_ = int64_list;\n  }\n  // @@protoc_insertion_point(field_set_allocated:tensorflow.Feature.int64_list)\n}\nFeature::Feature(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.Feature)\n}\nFeature::Feature(const Feature& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message() {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  clear_has_kind();\n  switch (from.kind_case()) {\n    case kBytesList: {\n      _internal_mutable_bytes_list()->::tensorflow::BytesList::MergeFrom(from._internal_bytes_list());\n      break;\n    }\n    case kFloatList: {\n      _internal_mutable_float_list()->::tensorflow::FloatList::MergeFrom(from._internal_float_list());\n      break;\n    }\n    case kInt64List: {\n      _internal_mutable_int64_list()->::tensorflow::Int64List::MergeFrom(from._internal_int64_list());\n      break;\n    }\n    case KIND_NOT_SET: {\n      break;\n    }\n  }\n  // @@protoc_insertion_point(copy_constructor:tensorflow.Feature)\n}\n\nvoid Feature::SharedCtor() {\nclear_has_kind();\n}\n\nFeature::~Feature() {\n  // @@protoc_insertion_point(destructor:tensorflow.Feature)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid Feature::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n  if (has_kind()) {\n    clear_kind();\n  }\n}\n\nvoid Feature::ArenaDtor(void* object) {\n  Feature* _this = reinterpret_cast< Feature* >(object);\n  (void)_this;\n}\nvoid Feature::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid Feature::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid Feature::clear_kind() {\n// @@protoc_insertion_point(one_of_clear_start:tensorflow.Feature)\n  switch (kind_case()) {\n    case kBytesList: {\n      if (GetArena() == nullptr) {\n        delete kind_.bytes_list_;\n      }\n      break;\n    }\n    case kFloatList: {\n      if (GetArena() == nullptr) {\n        delete kind_.float_list_;\n      }\n      break;\n    }\n    case kInt64List: {\n      if (GetArena() == nullptr) {\n        delete kind_.int64_list_;\n      }\n      break;\n    }\n    case KIND_NOT_SET: {\n      break;\n    }\n  }\n  _oneof_case_[0] = KIND_NOT_SET;\n}\n\n\nvoid Feature::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.Feature)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  clear_kind();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* Feature::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // .tensorflow.BytesList bytes_list = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr = ctx->ParseMessage(_internal_mutable_bytes_list(), ptr);\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      // .tensorflow.FloatList float_list = 2;\n      case 2:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {\n          ptr = ctx->ParseMessage(_internal_mutable_float_list(), ptr);\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      // .tensorflow.Int64List int64_list = 3;\n      case 3:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) {\n          ptr = ctx->ParseMessage(_internal_mutable_int64_list(), ptr);\n          CHK_(ptr);\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* Feature::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.Feature)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // .tensorflow.BytesList bytes_list = 1;\n  if (_internal_has_bytes_list()) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(\n        1, _Internal::bytes_list(this), target, stream);\n  }\n\n  // .tensorflow.FloatList float_list = 2;\n  if (_internal_has_float_list()) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(\n        2, _Internal::float_list(this), target, stream);\n  }\n\n  // .tensorflow.Int64List int64_list = 3;\n  if (_internal_has_int64_list()) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(\n        3, _Internal::int64_list(this), target, stream);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.Feature)\n  return target;\n}\n\nsize_t Feature::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.Feature)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  switch (kind_case()) {\n    // .tensorflow.BytesList bytes_list = 1;\n    case kBytesList: {\n      total_size += 1 +\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(\n          *kind_.bytes_list_);\n      break;\n    }\n    // .tensorflow.FloatList float_list = 2;\n    case kFloatList: {\n      total_size += 1 +\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(\n          *kind_.float_list_);\n      break;\n    }\n    // .tensorflow.Int64List int64_list = 3;\n    case kInt64List: {\n      total_size += 1 +\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(\n          *kind_.int64_list_);\n      break;\n    }\n    case KIND_NOT_SET: {\n      break;\n    }\n  }\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid Feature::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.Feature)\n  GOOGLE_DCHECK_NE(&from, this);\n  const Feature* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Feature>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.Feature)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.Feature)\n    MergeFrom(*source);\n  }\n}\n\nvoid Feature::MergeFrom(const Feature& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.Feature)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  switch (from.kind_case()) {\n    case kBytesList: {\n      _internal_mutable_bytes_list()->::tensorflow::BytesList::MergeFrom(from._internal_bytes_list());\n      break;\n    }\n    case kFloatList: {\n      _internal_mutable_float_list()->::tensorflow::FloatList::MergeFrom(from._internal_float_list());\n      break;\n    }\n    case kInt64List: {\n      _internal_mutable_int64_list()->::tensorflow::Int64List::MergeFrom(from._internal_int64_list());\n      break;\n    }\n    case KIND_NOT_SET: {\n      break;\n    }\n  }\n}\n\nvoid Feature::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.Feature)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid Feature::CopyFrom(const Feature& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.Feature)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool Feature::IsInitialized() const {\n  return true;\n}\n\nvoid Feature::InternalSwap(Feature* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  swap(kind_, other->kind_);\n  swap(_oneof_case_[0], other->_oneof_case_[0]);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata Feature::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nFeatures_FeatureEntry_DoNotUse::Features_FeatureEntry_DoNotUse() {}\nFeatures_FeatureEntry_DoNotUse::Features_FeatureEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n    : SuperType(arena) {}\nvoid Features_FeatureEntry_DoNotUse::MergeFrom(const Features_FeatureEntry_DoNotUse& other) {\n  MergeFromInternal(other);\n}\n::PROTOBUF_NAMESPACE_ID::Metadata Features_FeatureEntry_DoNotUse::GetMetadata() const {\n  return GetMetadataStatic();\n}\nvoid Features_FeatureEntry_DoNotUse::MergeFrom(\n    const ::PROTOBUF_NAMESPACE_ID::Message& other) {\n  ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom(other);\n}\n\n\n// ===================================================================\n\nclass Features::_Internal {\n public:\n};\n\nFeatures::Features(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena),\n  feature_(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.Features)\n}\nFeatures::Features(const Features& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message() {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  feature_.MergeFrom(from.feature_);\n  // @@protoc_insertion_point(copy_constructor:tensorflow.Features)\n}\n\nvoid Features::SharedCtor() {\n}\n\nFeatures::~Features() {\n  // @@protoc_insertion_point(destructor:tensorflow.Features)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid Features::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n}\n\nvoid Features::ArenaDtor(void* object) {\n  Features* _this = reinterpret_cast< Features* >(object);\n  (void)_this;\n}\nvoid Features::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid Features::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid Features::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.Features)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  feature_.Clear();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* Features::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // map<string, .tensorflow.Feature> feature = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr -= 1;\n          do {\n            ptr += 1;\n            ptr = ctx->ParseMessage(&feature_, ptr);\n            CHK_(ptr);\n            if (!ctx->DataAvailable(ptr)) break;\n          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* Features::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.Features)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // map<string, .tensorflow.Feature> feature = 1;\n  if (!this->_internal_feature().empty()) {\n    typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >::const_pointer\n        ConstPtr;\n    typedef ConstPtr SortItem;\n    typedef ::PROTOBUF_NAMESPACE_ID::internal::CompareByDerefFirst<SortItem> Less;\n    struct Utf8Check {\n      static void Check(ConstPtr p) {\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(\n          p->first.data(), static_cast<int>(p->first.length()),\n          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,\n          \"tensorflow.Features.FeatureEntry.key\");\n      }\n    };\n\n    if (stream->IsSerializationDeterministic() &&\n        this->_internal_feature().size() > 1) {\n      ::std::unique_ptr<SortItem[]> items(\n          new SortItem[this->_internal_feature().size()]);\n      typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >::size_type size_type;\n      size_type n = 0;\n      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >::const_iterator\n          it = this->_internal_feature().begin();\n          it != this->_internal_feature().end(); ++it, ++n) {\n        items[static_cast<ptrdiff_t>(n)] = SortItem(&*it);\n      }\n      ::std::sort(&items[0], &items[static_cast<ptrdiff_t>(n)], Less());\n      for (size_type i = 0; i < n; i++) {\n        target = Features_FeatureEntry_DoNotUse::Funcs::InternalSerialize(1, items[static_cast<ptrdiff_t>(i)]->first, items[static_cast<ptrdiff_t>(i)]->second, target, stream);\n        Utf8Check::Check(&(*items[static_cast<ptrdiff_t>(i)]));\n      }\n    } else {\n      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >::const_iterator\n          it = this->_internal_feature().begin();\n          it != this->_internal_feature().end(); ++it) {\n        target = Features_FeatureEntry_DoNotUse::Funcs::InternalSerialize(1, it->first, it->second, target, stream);\n        Utf8Check::Check(&(*it));\n      }\n    }\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.Features)\n  return target;\n}\n\nsize_t Features::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.Features)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // map<string, .tensorflow.Feature> feature = 1;\n  total_size += 1 *\n      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->_internal_feature_size());\n  for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >::const_iterator\n      it = this->_internal_feature().begin();\n      it != this->_internal_feature().end(); ++it) {\n    total_size += Features_FeatureEntry_DoNotUse::Funcs::ByteSizeLong(it->first, it->second);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid Features::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.Features)\n  GOOGLE_DCHECK_NE(&from, this);\n  const Features* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Features>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.Features)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.Features)\n    MergeFrom(*source);\n  }\n}\n\nvoid Features::MergeFrom(const Features& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.Features)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  feature_.MergeFrom(from.feature_);\n}\n\nvoid Features::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.Features)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid Features::CopyFrom(const Features& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.Features)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool Features::IsInitialized() const {\n  return true;\n}\n\nvoid Features::InternalSwap(Features* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  feature_.Swap(&other->feature_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata Features::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nclass FeatureList::_Internal {\n public:\n};\n\nFeatureList::FeatureList(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena),\n  feature_(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.FeatureList)\n}\nFeatureList::FeatureList(const FeatureList& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message(),\n      feature_(from.feature_) {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  // @@protoc_insertion_point(copy_constructor:tensorflow.FeatureList)\n}\n\nvoid FeatureList::SharedCtor() {\n}\n\nFeatureList::~FeatureList() {\n  // @@protoc_insertion_point(destructor:tensorflow.FeatureList)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid FeatureList::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n}\n\nvoid FeatureList::ArenaDtor(void* object) {\n  FeatureList* _this = reinterpret_cast< FeatureList* >(object);\n  (void)_this;\n}\nvoid FeatureList::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid FeatureList::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid FeatureList::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.FeatureList)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  feature_.Clear();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* FeatureList::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // repeated .tensorflow.Feature feature = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr -= 1;\n          do {\n            ptr += 1;\n            ptr = ctx->ParseMessage(_internal_add_feature(), ptr);\n            CHK_(ptr);\n            if (!ctx->DataAvailable(ptr)) break;\n          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* FeatureList::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.FeatureList)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // repeated .tensorflow.Feature feature = 1;\n  for (unsigned int i = 0,\n      n = static_cast<unsigned int>(this->_internal_feature_size()); i < n; i++) {\n    target = stream->EnsureSpace(target);\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::\n      InternalWriteMessage(1, this->_internal_feature(i), target, stream);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.FeatureList)\n  return target;\n}\n\nsize_t FeatureList::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.FeatureList)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // repeated .tensorflow.Feature feature = 1;\n  total_size += 1UL * this->_internal_feature_size();\n  for (const auto& msg : this->feature_) {\n    total_size +=\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid FeatureList::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.FeatureList)\n  GOOGLE_DCHECK_NE(&from, this);\n  const FeatureList* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<FeatureList>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.FeatureList)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.FeatureList)\n    MergeFrom(*source);\n  }\n}\n\nvoid FeatureList::MergeFrom(const FeatureList& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.FeatureList)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  feature_.MergeFrom(from.feature_);\n}\n\nvoid FeatureList::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.FeatureList)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid FeatureList::CopyFrom(const FeatureList& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.FeatureList)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool FeatureList::IsInitialized() const {\n  return true;\n}\n\nvoid FeatureList::InternalSwap(FeatureList* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  feature_.InternalSwap(&other->feature_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata FeatureList::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// ===================================================================\n\nFeatureLists_FeatureListEntry_DoNotUse::FeatureLists_FeatureListEntry_DoNotUse() {}\nFeatureLists_FeatureListEntry_DoNotUse::FeatureLists_FeatureListEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n    : SuperType(arena) {}\nvoid FeatureLists_FeatureListEntry_DoNotUse::MergeFrom(const FeatureLists_FeatureListEntry_DoNotUse& other) {\n  MergeFromInternal(other);\n}\n::PROTOBUF_NAMESPACE_ID::Metadata FeatureLists_FeatureListEntry_DoNotUse::GetMetadata() const {\n  return GetMetadataStatic();\n}\nvoid FeatureLists_FeatureListEntry_DoNotUse::MergeFrom(\n    const ::PROTOBUF_NAMESPACE_ID::Message& other) {\n  ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom(other);\n}\n\n\n// ===================================================================\n\nclass FeatureLists::_Internal {\n public:\n};\n\nFeatureLists::FeatureLists(::PROTOBUF_NAMESPACE_ID::Arena* arena)\n  : ::PROTOBUF_NAMESPACE_ID::Message(arena),\n  feature_list_(arena) {\n  SharedCtor();\n  RegisterArenaDtor(arena);\n  // @@protoc_insertion_point(arena_constructor:tensorflow.FeatureLists)\n}\nFeatureLists::FeatureLists(const FeatureLists& from)\n  : ::PROTOBUF_NAMESPACE_ID::Message() {\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  feature_list_.MergeFrom(from.feature_list_);\n  // @@protoc_insertion_point(copy_constructor:tensorflow.FeatureLists)\n}\n\nvoid FeatureLists::SharedCtor() {\n}\n\nFeatureLists::~FeatureLists() {\n  // @@protoc_insertion_point(destructor:tensorflow.FeatureLists)\n  SharedDtor();\n  _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nvoid FeatureLists::SharedDtor() {\n  GOOGLE_DCHECK(GetArena() == nullptr);\n}\n\nvoid FeatureLists::ArenaDtor(void* object) {\n  FeatureLists* _this = reinterpret_cast< FeatureLists* >(object);\n  (void)_this;\n}\nvoid FeatureLists::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) {\n}\nvoid FeatureLists::SetCachedSize(int size) const {\n  _cached_size_.Set(size);\n}\n\nvoid FeatureLists::Clear() {\n// @@protoc_insertion_point(message_clear_start:tensorflow.FeatureLists)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  feature_list_.Clear();\n  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();\n}\n\nconst char* FeatureLists::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {\n#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n  while (!ctx->Done(&ptr)) {\n    ::PROTOBUF_NAMESPACE_ID::uint32 tag;\n    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);\n    CHK_(ptr);\n    switch (tag >> 3) {\n      // map<string, .tensorflow.FeatureList> feature_list = 1;\n      case 1:\n        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {\n          ptr -= 1;\n          do {\n            ptr += 1;\n            ptr = ctx->ParseMessage(&feature_list_, ptr);\n            CHK_(ptr);\n            if (!ctx->DataAvailable(ptr)) break;\n          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));\n        } else goto handle_unusual;\n        continue;\n      default: {\n      handle_unusual:\n        if ((tag & 7) == 4 || tag == 0) {\n          ctx->SetLastTag(tag);\n          goto success;\n        }\n        ptr = UnknownFieldParse(tag,\n            _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),\n            ptr, ctx);\n        CHK_(ptr != nullptr);\n        continue;\n      }\n    }  // switch\n  }  // while\nsuccess:\n  return ptr;\nfailure:\n  ptr = nullptr;\n  goto success;\n#undef CHK_\n}\n\n::PROTOBUF_NAMESPACE_ID::uint8* FeatureLists::_InternalSerialize(\n    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {\n  // @@protoc_insertion_point(serialize_to_array_start:tensorflow.FeatureLists)\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  // map<string, .tensorflow.FeatureList> feature_list = 1;\n  if (!this->_internal_feature_list().empty()) {\n    typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >::const_pointer\n        ConstPtr;\n    typedef ConstPtr SortItem;\n    typedef ::PROTOBUF_NAMESPACE_ID::internal::CompareByDerefFirst<SortItem> Less;\n    struct Utf8Check {\n      static void Check(ConstPtr p) {\n        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(\n          p->first.data(), static_cast<int>(p->first.length()),\n          ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,\n          \"tensorflow.FeatureLists.FeatureListEntry.key\");\n      }\n    };\n\n    if (stream->IsSerializationDeterministic() &&\n        this->_internal_feature_list().size() > 1) {\n      ::std::unique_ptr<SortItem[]> items(\n          new SortItem[this->_internal_feature_list().size()]);\n      typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >::size_type size_type;\n      size_type n = 0;\n      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >::const_iterator\n          it = this->_internal_feature_list().begin();\n          it != this->_internal_feature_list().end(); ++it, ++n) {\n        items[static_cast<ptrdiff_t>(n)] = SortItem(&*it);\n      }\n      ::std::sort(&items[0], &items[static_cast<ptrdiff_t>(n)], Less());\n      for (size_type i = 0; i < n; i++) {\n        target = FeatureLists_FeatureListEntry_DoNotUse::Funcs::InternalSerialize(1, items[static_cast<ptrdiff_t>(i)]->first, items[static_cast<ptrdiff_t>(i)]->second, target, stream);\n        Utf8Check::Check(&(*items[static_cast<ptrdiff_t>(i)]));\n      }\n    } else {\n      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >::const_iterator\n          it = this->_internal_feature_list().begin();\n          it != this->_internal_feature_list().end(); ++it) {\n        target = FeatureLists_FeatureListEntry_DoNotUse::Funcs::InternalSerialize(1, it->first, it->second, target, stream);\n        Utf8Check::Check(&(*it));\n      }\n    }\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(\n        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);\n  }\n  // @@protoc_insertion_point(serialize_to_array_end:tensorflow.FeatureLists)\n  return target;\n}\n\nsize_t FeatureLists::ByteSizeLong() const {\n// @@protoc_insertion_point(message_byte_size_start:tensorflow.FeatureLists)\n  size_t total_size = 0;\n\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  // Prevent compiler warnings about cached_has_bits being unused\n  (void) cached_has_bits;\n\n  // map<string, .tensorflow.FeatureList> feature_list = 1;\n  total_size += 1 *\n      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->_internal_feature_list_size());\n  for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >::const_iterator\n      it = this->_internal_feature_list().begin();\n      it != this->_internal_feature_list().end(); ++it) {\n    total_size += FeatureLists_FeatureListEntry_DoNotUse::Funcs::ByteSizeLong(it->first, it->second);\n  }\n\n  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(\n        _internal_metadata_, total_size, &_cached_size_);\n  }\n  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);\n  SetCachedSize(cached_size);\n  return total_size;\n}\n\nvoid FeatureLists::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_merge_from_start:tensorflow.FeatureLists)\n  GOOGLE_DCHECK_NE(&from, this);\n  const FeatureLists* source =\n      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<FeatureLists>(\n          &from);\n  if (source == nullptr) {\n  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tensorflow.FeatureLists)\n    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);\n  } else {\n  // @@protoc_insertion_point(generalized_merge_from_cast_success:tensorflow.FeatureLists)\n    MergeFrom(*source);\n  }\n}\n\nvoid FeatureLists::MergeFrom(const FeatureLists& from) {\n// @@protoc_insertion_point(class_specific_merge_from_start:tensorflow.FeatureLists)\n  GOOGLE_DCHECK_NE(&from, this);\n  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);\n  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;\n  (void) cached_has_bits;\n\n  feature_list_.MergeFrom(from.feature_list_);\n}\n\nvoid FeatureLists::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {\n// @@protoc_insertion_point(generalized_copy_from_start:tensorflow.FeatureLists)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nvoid FeatureLists::CopyFrom(const FeatureLists& from) {\n// @@protoc_insertion_point(class_specific_copy_from_start:tensorflow.FeatureLists)\n  if (&from == this) return;\n  Clear();\n  MergeFrom(from);\n}\n\nbool FeatureLists::IsInitialized() const {\n  return true;\n}\n\nvoid FeatureLists::InternalSwap(FeatureLists* other) {\n  using std::swap;\n  _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_);\n  feature_list_.Swap(&other->feature_list_);\n}\n\n::PROTOBUF_NAMESPACE_ID::Metadata FeatureLists::GetMetadata() const {\n  return GetMetadataStatic();\n}\n\n\n// @@protoc_insertion_point(namespace_scope)\n}  // namespace tensorflow\nPROTOBUF_NAMESPACE_OPEN\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::BytesList* Arena::CreateMaybeMessage< ::tensorflow::BytesList >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::BytesList >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::FloatList* Arena::CreateMaybeMessage< ::tensorflow::FloatList >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::FloatList >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::Int64List* Arena::CreateMaybeMessage< ::tensorflow::Int64List >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::Int64List >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::Feature* Arena::CreateMaybeMessage< ::tensorflow::Feature >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::Feature >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::Features_FeatureEntry_DoNotUse* Arena::CreateMaybeMessage< ::tensorflow::Features_FeatureEntry_DoNotUse >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::Features_FeatureEntry_DoNotUse >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::Features* Arena::CreateMaybeMessage< ::tensorflow::Features >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::Features >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::FeatureList* Arena::CreateMaybeMessage< ::tensorflow::FeatureList >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::FeatureList >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::FeatureLists_FeatureListEntry_DoNotUse* Arena::CreateMaybeMessage< ::tensorflow::FeatureLists_FeatureListEntry_DoNotUse >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::FeatureLists_FeatureListEntry_DoNotUse >(arena);\n}\ntemplate<> PROTOBUF_NOINLINE ::tensorflow::FeatureLists* Arena::CreateMaybeMessage< ::tensorflow::FeatureLists >(Arena* arena) {\n  return Arena::CreateMessageInternal< ::tensorflow::FeatureLists >(arena);\n}\nPROTOBUF_NAMESPACE_CLOSE\n\n// @@protoc_insertion_point(global_scope)\n#include <google/protobuf/port_undef.inc>\n"
  },
  {
    "path": "src/proto/feature.pb.h",
    "content": "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: feature.proto\n\n#ifndef GOOGLE_PROTOBUF_INCLUDED_feature_2eproto\n#define GOOGLE_PROTOBUF_INCLUDED_feature_2eproto\n\n#include <limits>\n#include <string>\n\n#include <google/protobuf/port_def.inc>\n#if PROTOBUF_VERSION < 3015000\n#error This file was generated by a newer version of protoc which is\n#error incompatible with your Protocol Buffer headers. Please update\n#error your headers.\n#endif\n#if 3015000 < PROTOBUF_MIN_PROTOC_VERSION\n#error This file was generated by an older version of protoc which is\n#error incompatible with your Protocol Buffer headers. Please\n#error regenerate this file with a newer version of protoc.\n#endif\n\n#include <google/protobuf/port_undef.inc>\n#include <google/protobuf/io/coded_stream.h>\n#include <google/protobuf/arena.h>\n#include <google/protobuf/arenastring.h>\n#include <google/protobuf/generated_message_table_driven.h>\n#include <google/protobuf/generated_message_util.h>\n#include <google/protobuf/metadata_lite.h>\n#include <google/protobuf/generated_message_reflection.h>\n#include <google/protobuf/message.h>\n#include <google/protobuf/repeated_field.h>  // IWYU pragma: export\n#include <google/protobuf/extension_set.h>  // IWYU pragma: export\n#include <google/protobuf/map.h>  // IWYU pragma: export\n#include <google/protobuf/map_entry.h>\n#include <google/protobuf/map_field_inl.h>\n#include <google/protobuf/unknown_field_set.h>\n// @@protoc_insertion_point(includes)\n#include <google/protobuf/port_def.inc>\n#define PROTOBUF_INTERNAL_EXPORT_feature_2eproto\nPROTOBUF_NAMESPACE_OPEN\nnamespace internal {\nclass AnyMetadata;\n}  // namespace internal\nPROTOBUF_NAMESPACE_CLOSE\n\n// Internal implementation detail -- do not use these members.\nstruct TableStruct_feature_2eproto {\n  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]\n    PROTOBUF_SECTION_VARIABLE(protodesc_cold);\n  static const ::PROTOBUF_NAMESPACE_ID::internal::AuxiliaryParseTableField aux[]\n    PROTOBUF_SECTION_VARIABLE(protodesc_cold);\n  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[9]\n    PROTOBUF_SECTION_VARIABLE(protodesc_cold);\n  static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];\n  static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];\n  static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];\n};\nextern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_feature_2eproto;\n::PROTOBUF_NAMESPACE_ID::Metadata descriptor_table_feature_2eproto_metadata_getter(int index);\nnamespace tensorflow {\nclass BytesList;\nstruct BytesListDefaultTypeInternal;\nextern BytesListDefaultTypeInternal _BytesList_default_instance_;\nclass Feature;\nstruct FeatureDefaultTypeInternal;\nextern FeatureDefaultTypeInternal _Feature_default_instance_;\nclass FeatureList;\nstruct FeatureListDefaultTypeInternal;\nextern FeatureListDefaultTypeInternal _FeatureList_default_instance_;\nclass FeatureLists;\nstruct FeatureListsDefaultTypeInternal;\nextern FeatureListsDefaultTypeInternal _FeatureLists_default_instance_;\nclass FeatureLists_FeatureListEntry_DoNotUse;\nstruct FeatureLists_FeatureListEntry_DoNotUseDefaultTypeInternal;\nextern FeatureLists_FeatureListEntry_DoNotUseDefaultTypeInternal _FeatureLists_FeatureListEntry_DoNotUse_default_instance_;\nclass Features;\nstruct FeaturesDefaultTypeInternal;\nextern FeaturesDefaultTypeInternal _Features_default_instance_;\nclass Features_FeatureEntry_DoNotUse;\nstruct Features_FeatureEntry_DoNotUseDefaultTypeInternal;\nextern Features_FeatureEntry_DoNotUseDefaultTypeInternal _Features_FeatureEntry_DoNotUse_default_instance_;\nclass FloatList;\nstruct FloatListDefaultTypeInternal;\nextern FloatListDefaultTypeInternal _FloatList_default_instance_;\nclass Int64List;\nstruct Int64ListDefaultTypeInternal;\nextern Int64ListDefaultTypeInternal _Int64List_default_instance_;\n}  // namespace tensorflow\nPROTOBUF_NAMESPACE_OPEN\ntemplate<> ::tensorflow::BytesList* Arena::CreateMaybeMessage<::tensorflow::BytesList>(Arena*);\ntemplate<> ::tensorflow::Feature* Arena::CreateMaybeMessage<::tensorflow::Feature>(Arena*);\ntemplate<> ::tensorflow::FeatureList* Arena::CreateMaybeMessage<::tensorflow::FeatureList>(Arena*);\ntemplate<> ::tensorflow::FeatureLists* Arena::CreateMaybeMessage<::tensorflow::FeatureLists>(Arena*);\ntemplate<> ::tensorflow::FeatureLists_FeatureListEntry_DoNotUse* Arena::CreateMaybeMessage<::tensorflow::FeatureLists_FeatureListEntry_DoNotUse>(Arena*);\ntemplate<> ::tensorflow::Features* Arena::CreateMaybeMessage<::tensorflow::Features>(Arena*);\ntemplate<> ::tensorflow::Features_FeatureEntry_DoNotUse* Arena::CreateMaybeMessage<::tensorflow::Features_FeatureEntry_DoNotUse>(Arena*);\ntemplate<> ::tensorflow::FloatList* Arena::CreateMaybeMessage<::tensorflow::FloatList>(Arena*);\ntemplate<> ::tensorflow::Int64List* Arena::CreateMaybeMessage<::tensorflow::Int64List>(Arena*);\nPROTOBUF_NAMESPACE_CLOSE\nnamespace tensorflow {\n\n// ===================================================================\n\nclass BytesList PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.BytesList) */ {\n public:\n  inline BytesList() : BytesList(nullptr) {}\n  virtual ~BytesList();\n  explicit constexpr BytesList(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  BytesList(const BytesList& from);\n  BytesList(BytesList&& from) noexcept\n    : BytesList() {\n    *this = ::std::move(from);\n  }\n\n  inline BytesList& operator=(const BytesList& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline BytesList& operator=(BytesList&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const BytesList& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const BytesList* internal_default_instance() {\n    return reinterpret_cast<const BytesList*>(\n               &_BytesList_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    0;\n\n  friend void swap(BytesList& a, BytesList& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(BytesList* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(BytesList* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline BytesList* New() const final {\n    return CreateMaybeMessage<BytesList>(nullptr);\n  }\n\n  BytesList* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<BytesList>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const BytesList& from);\n  void MergeFrom(const BytesList& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(BytesList* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.BytesList\";\n  }\n  protected:\n  explicit BytesList(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kValueFieldNumber = 1,\n  };\n  // repeated bytes value = 1;\n  int value_size() const;\n  private:\n  int _internal_value_size() const;\n  public:\n  void clear_value();\n  const std::string& value(int index) const;\n  std::string* mutable_value(int index);\n  void set_value(int index, const std::string& value);\n  void set_value(int index, std::string&& value);\n  void set_value(int index, const char* value);\n  void set_value(int index, const void* value, size_t size);\n  std::string* add_value();\n  void add_value(const std::string& value);\n  void add_value(std::string&& value);\n  void add_value(const char* value);\n  void add_value(const void* value, size_t size);\n  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& value() const;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_value();\n  private:\n  const std::string& _internal_value(int index) const;\n  std::string* _internal_add_value();\n  public:\n\n  // @@protoc_insertion_point(class_scope:tensorflow.BytesList)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> value_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_feature_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass FloatList PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.FloatList) */ {\n public:\n  inline FloatList() : FloatList(nullptr) {}\n  virtual ~FloatList();\n  explicit constexpr FloatList(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  FloatList(const FloatList& from);\n  FloatList(FloatList&& from) noexcept\n    : FloatList() {\n    *this = ::std::move(from);\n  }\n\n  inline FloatList& operator=(const FloatList& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline FloatList& operator=(FloatList&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const FloatList& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const FloatList* internal_default_instance() {\n    return reinterpret_cast<const FloatList*>(\n               &_FloatList_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    1;\n\n  friend void swap(FloatList& a, FloatList& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(FloatList* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(FloatList* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline FloatList* New() const final {\n    return CreateMaybeMessage<FloatList>(nullptr);\n  }\n\n  FloatList* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<FloatList>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const FloatList& from);\n  void MergeFrom(const FloatList& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(FloatList* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.FloatList\";\n  }\n  protected:\n  explicit FloatList(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kValueFieldNumber = 1,\n  };\n  // repeated float value = 1 [packed = true];\n  int value_size() const;\n  private:\n  int _internal_value_size() const;\n  public:\n  void clear_value();\n  private:\n  float _internal_value(int index) const;\n  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >&\n      _internal_value() const;\n  void _internal_add_value(float value);\n  ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >*\n      _internal_mutable_value();\n  public:\n  float value(int index) const;\n  void set_value(int index, float value);\n  void add_value(float value);\n  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >&\n      value() const;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >*\n      mutable_value();\n\n  // @@protoc_insertion_point(class_scope:tensorflow.FloatList)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedField< float > value_;\n  mutable std::atomic<int> _value_cached_byte_size_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_feature_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass Int64List PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.Int64List) */ {\n public:\n  inline Int64List() : Int64List(nullptr) {}\n  virtual ~Int64List();\n  explicit constexpr Int64List(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  Int64List(const Int64List& from);\n  Int64List(Int64List&& from) noexcept\n    : Int64List() {\n    *this = ::std::move(from);\n  }\n\n  inline Int64List& operator=(const Int64List& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline Int64List& operator=(Int64List&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const Int64List& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const Int64List* internal_default_instance() {\n    return reinterpret_cast<const Int64List*>(\n               &_Int64List_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    2;\n\n  friend void swap(Int64List& a, Int64List& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(Int64List* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(Int64List* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline Int64List* New() const final {\n    return CreateMaybeMessage<Int64List>(nullptr);\n  }\n\n  Int64List* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<Int64List>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const Int64List& from);\n  void MergeFrom(const Int64List& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(Int64List* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.Int64List\";\n  }\n  protected:\n  explicit Int64List(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kValueFieldNumber = 1,\n  };\n  // repeated int64 value = 1 [packed = true];\n  int value_size() const;\n  private:\n  int _internal_value_size() const;\n  public:\n  void clear_value();\n  private:\n  ::PROTOBUF_NAMESPACE_ID::int64 _internal_value(int index) const;\n  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >&\n      _internal_value() const;\n  void _internal_add_value(::PROTOBUF_NAMESPACE_ID::int64 value);\n  ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >*\n      _internal_mutable_value();\n  public:\n  ::PROTOBUF_NAMESPACE_ID::int64 value(int index) const;\n  void set_value(int index, ::PROTOBUF_NAMESPACE_ID::int64 value);\n  void add_value(::PROTOBUF_NAMESPACE_ID::int64 value);\n  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >&\n      value() const;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >*\n      mutable_value();\n\n  // @@protoc_insertion_point(class_scope:tensorflow.Int64List)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > value_;\n  mutable std::atomic<int> _value_cached_byte_size_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_feature_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass Feature PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.Feature) */ {\n public:\n  inline Feature() : Feature(nullptr) {}\n  virtual ~Feature();\n  explicit constexpr Feature(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  Feature(const Feature& from);\n  Feature(Feature&& from) noexcept\n    : Feature() {\n    *this = ::std::move(from);\n  }\n\n  inline Feature& operator=(const Feature& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline Feature& operator=(Feature&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const Feature& default_instance() {\n    return *internal_default_instance();\n  }\n  enum KindCase {\n    kBytesList = 1,\n    kFloatList = 2,\n    kInt64List = 3,\n    KIND_NOT_SET = 0,\n  };\n\n  static inline const Feature* internal_default_instance() {\n    return reinterpret_cast<const Feature*>(\n               &_Feature_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    3;\n\n  friend void swap(Feature& a, Feature& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(Feature* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(Feature* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline Feature* New() const final {\n    return CreateMaybeMessage<Feature>(nullptr);\n  }\n\n  Feature* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<Feature>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const Feature& from);\n  void MergeFrom(const Feature& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(Feature* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.Feature\";\n  }\n  protected:\n  explicit Feature(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kBytesListFieldNumber = 1,\n    kFloatListFieldNumber = 2,\n    kInt64ListFieldNumber = 3,\n  };\n  // .tensorflow.BytesList bytes_list = 1;\n  bool has_bytes_list() const;\n  private:\n  bool _internal_has_bytes_list() const;\n  public:\n  void clear_bytes_list();\n  const ::tensorflow::BytesList& bytes_list() const;\n  ::tensorflow::BytesList* release_bytes_list();\n  ::tensorflow::BytesList* mutable_bytes_list();\n  void set_allocated_bytes_list(::tensorflow::BytesList* bytes_list);\n  private:\n  const ::tensorflow::BytesList& _internal_bytes_list() const;\n  ::tensorflow::BytesList* _internal_mutable_bytes_list();\n  public:\n  void unsafe_arena_set_allocated_bytes_list(\n      ::tensorflow::BytesList* bytes_list);\n  ::tensorflow::BytesList* unsafe_arena_release_bytes_list();\n\n  // .tensorflow.FloatList float_list = 2;\n  bool has_float_list() const;\n  private:\n  bool _internal_has_float_list() const;\n  public:\n  void clear_float_list();\n  const ::tensorflow::FloatList& float_list() const;\n  ::tensorflow::FloatList* release_float_list();\n  ::tensorflow::FloatList* mutable_float_list();\n  void set_allocated_float_list(::tensorflow::FloatList* float_list);\n  private:\n  const ::tensorflow::FloatList& _internal_float_list() const;\n  ::tensorflow::FloatList* _internal_mutable_float_list();\n  public:\n  void unsafe_arena_set_allocated_float_list(\n      ::tensorflow::FloatList* float_list);\n  ::tensorflow::FloatList* unsafe_arena_release_float_list();\n\n  // .tensorflow.Int64List int64_list = 3;\n  bool has_int64_list() const;\n  private:\n  bool _internal_has_int64_list() const;\n  public:\n  void clear_int64_list();\n  const ::tensorflow::Int64List& int64_list() const;\n  ::tensorflow::Int64List* release_int64_list();\n  ::tensorflow::Int64List* mutable_int64_list();\n  void set_allocated_int64_list(::tensorflow::Int64List* int64_list);\n  private:\n  const ::tensorflow::Int64List& _internal_int64_list() const;\n  ::tensorflow::Int64List* _internal_mutable_int64_list();\n  public:\n  void unsafe_arena_set_allocated_int64_list(\n      ::tensorflow::Int64List* int64_list);\n  ::tensorflow::Int64List* unsafe_arena_release_int64_list();\n\n  void clear_kind();\n  KindCase kind_case() const;\n  // @@protoc_insertion_point(class_scope:tensorflow.Feature)\n private:\n  class _Internal;\n  void set_has_bytes_list();\n  void set_has_float_list();\n  void set_has_int64_list();\n\n  inline bool has_kind() const;\n  inline void clear_has_kind();\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  union KindUnion {\n    constexpr KindUnion() : _constinit_{} {}\n      ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_;\n    ::tensorflow::BytesList* bytes_list_;\n    ::tensorflow::FloatList* float_list_;\n    ::tensorflow::Int64List* int64_list_;\n  } kind_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  ::PROTOBUF_NAMESPACE_ID::uint32 _oneof_case_[1];\n\n  friend struct ::TableStruct_feature_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass Features_FeatureEntry_DoNotUse : public ::PROTOBUF_NAMESPACE_ID::internal::MapEntry<Features_FeatureEntry_DoNotUse, \n    std::string, ::tensorflow::Feature,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> {\npublic:\n  typedef ::PROTOBUF_NAMESPACE_ID::internal::MapEntry<Features_FeatureEntry_DoNotUse, \n    std::string, ::tensorflow::Feature,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> SuperType;\n  Features_FeatureEntry_DoNotUse();\n  explicit constexpr Features_FeatureEntry_DoNotUse(\n      ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n  explicit Features_FeatureEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  void MergeFrom(const Features_FeatureEntry_DoNotUse& other);\n  static const Features_FeatureEntry_DoNotUse* internal_default_instance() { return reinterpret_cast<const Features_FeatureEntry_DoNotUse*>(&_Features_FeatureEntry_DoNotUse_default_instance_); }\n  static bool ValidateKey(std::string* s) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(s->data(), static_cast<int>(s->size()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, \"tensorflow.Features.FeatureEntry.key\");\n }\n  static bool ValidateValue(void*) { return true; }\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& other) final;\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_feature_2eproto);\n    return ::descriptor_table_feature_2eproto.file_level_metadata[4];\n  }\n\n  public:\n};\n\n// -------------------------------------------------------------------\n\nclass Features PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.Features) */ {\n public:\n  inline Features() : Features(nullptr) {}\n  virtual ~Features();\n  explicit constexpr Features(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  Features(const Features& from);\n  Features(Features&& from) noexcept\n    : Features() {\n    *this = ::std::move(from);\n  }\n\n  inline Features& operator=(const Features& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline Features& operator=(Features&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const Features& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const Features* internal_default_instance() {\n    return reinterpret_cast<const Features*>(\n               &_Features_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    5;\n\n  friend void swap(Features& a, Features& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(Features* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(Features* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline Features* New() const final {\n    return CreateMaybeMessage<Features>(nullptr);\n  }\n\n  Features* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<Features>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const Features& from);\n  void MergeFrom(const Features& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(Features* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.Features\";\n  }\n  protected:\n  explicit Features(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kFeatureFieldNumber = 1,\n  };\n  // map<string, .tensorflow.Feature> feature = 1;\n  int feature_size() const;\n  private:\n  int _internal_feature_size() const;\n  public:\n  void clear_feature();\n  private:\n  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >&\n      _internal_feature() const;\n  ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >*\n      _internal_mutable_feature();\n  public:\n  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >&\n      feature() const;\n  ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >*\n      mutable_feature();\n\n  // @@protoc_insertion_point(class_scope:tensorflow.Features)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::PROTOBUF_NAMESPACE_ID::internal::MapField<\n      Features_FeatureEntry_DoNotUse,\n      std::string, ::tensorflow::Feature,\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> feature_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_feature_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass FeatureList PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.FeatureList) */ {\n public:\n  inline FeatureList() : FeatureList(nullptr) {}\n  virtual ~FeatureList();\n  explicit constexpr FeatureList(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  FeatureList(const FeatureList& from);\n  FeatureList(FeatureList&& from) noexcept\n    : FeatureList() {\n    *this = ::std::move(from);\n  }\n\n  inline FeatureList& operator=(const FeatureList& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline FeatureList& operator=(FeatureList&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const FeatureList& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const FeatureList* internal_default_instance() {\n    return reinterpret_cast<const FeatureList*>(\n               &_FeatureList_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    6;\n\n  friend void swap(FeatureList& a, FeatureList& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(FeatureList* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(FeatureList* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline FeatureList* New() const final {\n    return CreateMaybeMessage<FeatureList>(nullptr);\n  }\n\n  FeatureList* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<FeatureList>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const FeatureList& from);\n  void MergeFrom(const FeatureList& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(FeatureList* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.FeatureList\";\n  }\n  protected:\n  explicit FeatureList(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kFeatureFieldNumber = 1,\n  };\n  // repeated .tensorflow.Feature feature = 1;\n  int feature_size() const;\n  private:\n  int _internal_feature_size() const;\n  public:\n  void clear_feature();\n  ::tensorflow::Feature* mutable_feature(int index);\n  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature >*\n      mutable_feature();\n  private:\n  const ::tensorflow::Feature& _internal_feature(int index) const;\n  ::tensorflow::Feature* _internal_add_feature();\n  public:\n  const ::tensorflow::Feature& feature(int index) const;\n  ::tensorflow::Feature* add_feature();\n  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature >&\n      feature() const;\n\n  // @@protoc_insertion_point(class_scope:tensorflow.FeatureList)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature > feature_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_feature_2eproto;\n};\n// -------------------------------------------------------------------\n\nclass FeatureLists_FeatureListEntry_DoNotUse : public ::PROTOBUF_NAMESPACE_ID::internal::MapEntry<FeatureLists_FeatureListEntry_DoNotUse, \n    std::string, ::tensorflow::FeatureList,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> {\npublic:\n  typedef ::PROTOBUF_NAMESPACE_ID::internal::MapEntry<FeatureLists_FeatureListEntry_DoNotUse, \n    std::string, ::tensorflow::FeatureList,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,\n    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> SuperType;\n  FeatureLists_FeatureListEntry_DoNotUse();\n  explicit constexpr FeatureLists_FeatureListEntry_DoNotUse(\n      ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n  explicit FeatureLists_FeatureListEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  void MergeFrom(const FeatureLists_FeatureListEntry_DoNotUse& other);\n  static const FeatureLists_FeatureListEntry_DoNotUse* internal_default_instance() { return reinterpret_cast<const FeatureLists_FeatureListEntry_DoNotUse*>(&_FeatureLists_FeatureListEntry_DoNotUse_default_instance_); }\n  static bool ValidateKey(std::string* s) {\n    return ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(s->data(), static_cast<int>(s->size()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, \"tensorflow.FeatureLists.FeatureListEntry.key\");\n }\n  static bool ValidateValue(void*) { return true; }\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& other) final;\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_feature_2eproto);\n    return ::descriptor_table_feature_2eproto.file_level_metadata[7];\n  }\n\n  public:\n};\n\n// -------------------------------------------------------------------\n\nclass FeatureLists PROTOBUF_FINAL :\n    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tensorflow.FeatureLists) */ {\n public:\n  inline FeatureLists() : FeatureLists(nullptr) {}\n  virtual ~FeatureLists();\n  explicit constexpr FeatureLists(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  FeatureLists(const FeatureLists& from);\n  FeatureLists(FeatureLists&& from) noexcept\n    : FeatureLists() {\n    *this = ::std::move(from);\n  }\n\n  inline FeatureLists& operator=(const FeatureLists& from) {\n    CopyFrom(from);\n    return *this;\n  }\n  inline FeatureLists& operator=(FeatureLists&& from) noexcept {\n    if (GetArena() == from.GetArena()) {\n      if (this != &from) InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {\n    return GetDescriptor();\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {\n    return GetMetadataStatic().descriptor;\n  }\n  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {\n    return GetMetadataStatic().reflection;\n  }\n  static const FeatureLists& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const FeatureLists* internal_default_instance() {\n    return reinterpret_cast<const FeatureLists*>(\n               &_FeatureLists_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    8;\n\n  friend void swap(FeatureLists& a, FeatureLists& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(FeatureLists* other) {\n    if (other == this) return;\n    if (GetArena() == other->GetArena()) {\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(FeatureLists* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetArena() == other->GetArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  inline FeatureLists* New() const final {\n    return CreateMaybeMessage<FeatureLists>(nullptr);\n  }\n\n  FeatureLists* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<FeatureLists>(arena);\n  }\n  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;\n  void CopyFrom(const FeatureLists& from);\n  void MergeFrom(const FeatureLists& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(\n      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _cached_size_.Get(); }\n\n  private:\n  inline void SharedCtor();\n  inline void SharedDtor();\n  void SetCachedSize(int size) const final;\n  void InternalSwap(FeatureLists* other);\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"tensorflow.FeatureLists\";\n  }\n  protected:\n  explicit FeatureLists(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  private:\n  static void ArenaDtor(void* object);\n  inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena);\n  public:\n\n  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;\n  private:\n  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {\n    return ::descriptor_table_feature_2eproto_metadata_getter(kIndexInFileMessages);\n  }\n\n  public:\n\n  // nested types ----------------------------------------------------\n\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kFeatureListFieldNumber = 1,\n  };\n  // map<string, .tensorflow.FeatureList> feature_list = 1;\n  int feature_list_size() const;\n  private:\n  int _internal_feature_list_size() const;\n  public:\n  void clear_feature_list();\n  private:\n  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >&\n      _internal_feature_list() const;\n  ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >*\n      _internal_mutable_feature_list();\n  public:\n  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >&\n      feature_list() const;\n  ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >*\n      mutable_feature_list();\n\n  // @@protoc_insertion_point(class_scope:tensorflow.FeatureLists)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  ::PROTOBUF_NAMESPACE_ID::internal::MapField<\n      FeatureLists_FeatureListEntry_DoNotUse,\n      std::string, ::tensorflow::FeatureList,\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,\n      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> feature_list_;\n  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  friend struct ::TableStruct_feature_2eproto;\n};\n// ===================================================================\n\n\n// ===================================================================\n\n#ifdef __GNUC__\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n#endif  // __GNUC__\n// BytesList\n\n// repeated bytes value = 1;\ninline int BytesList::_internal_value_size() const {\n  return value_.size();\n}\ninline int BytesList::value_size() const {\n  return _internal_value_size();\n}\ninline void BytesList::clear_value() {\n  value_.Clear();\n}\ninline std::string* BytesList::add_value() {\n  // @@protoc_insertion_point(field_add_mutable:tensorflow.BytesList.value)\n  return _internal_add_value();\n}\ninline const std::string& BytesList::_internal_value(int index) const {\n  return value_.Get(index);\n}\ninline const std::string& BytesList::value(int index) const {\n  // @@protoc_insertion_point(field_get:tensorflow.BytesList.value)\n  return _internal_value(index);\n}\ninline std::string* BytesList::mutable_value(int index) {\n  // @@protoc_insertion_point(field_mutable:tensorflow.BytesList.value)\n  return value_.Mutable(index);\n}\ninline void BytesList::set_value(int index, const std::string& value) {\n  // @@protoc_insertion_point(field_set:tensorflow.BytesList.value)\n  value_.Mutable(index)->assign(value);\n}\ninline void BytesList::set_value(int index, std::string&& value) {\n  // @@protoc_insertion_point(field_set:tensorflow.BytesList.value)\n  value_.Mutable(index)->assign(std::move(value));\n}\ninline void BytesList::set_value(int index, const char* value) {\n  GOOGLE_DCHECK(value != nullptr);\n  value_.Mutable(index)->assign(value);\n  // @@protoc_insertion_point(field_set_char:tensorflow.BytesList.value)\n}\ninline void BytesList::set_value(int index, const void* value, size_t size) {\n  value_.Mutable(index)->assign(\n    reinterpret_cast<const char*>(value), size);\n  // @@protoc_insertion_point(field_set_pointer:tensorflow.BytesList.value)\n}\ninline std::string* BytesList::_internal_add_value() {\n  return value_.Add();\n}\ninline void BytesList::add_value(const std::string& value) {\n  value_.Add()->assign(value);\n  // @@protoc_insertion_point(field_add:tensorflow.BytesList.value)\n}\ninline void BytesList::add_value(std::string&& value) {\n  value_.Add(std::move(value));\n  // @@protoc_insertion_point(field_add:tensorflow.BytesList.value)\n}\ninline void BytesList::add_value(const char* value) {\n  GOOGLE_DCHECK(value != nullptr);\n  value_.Add()->assign(value);\n  // @@protoc_insertion_point(field_add_char:tensorflow.BytesList.value)\n}\ninline void BytesList::add_value(const void* value, size_t size) {\n  value_.Add()->assign(reinterpret_cast<const char*>(value), size);\n  // @@protoc_insertion_point(field_add_pointer:tensorflow.BytesList.value)\n}\ninline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&\nBytesList::value() const {\n  // @@protoc_insertion_point(field_list:tensorflow.BytesList.value)\n  return value_;\n}\ninline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*\nBytesList::mutable_value() {\n  // @@protoc_insertion_point(field_mutable_list:tensorflow.BytesList.value)\n  return &value_;\n}\n\n// -------------------------------------------------------------------\n\n// FloatList\n\n// repeated float value = 1 [packed = true];\ninline int FloatList::_internal_value_size() const {\n  return value_.size();\n}\ninline int FloatList::value_size() const {\n  return _internal_value_size();\n}\ninline void FloatList::clear_value() {\n  value_.Clear();\n}\ninline float FloatList::_internal_value(int index) const {\n  return value_.Get(index);\n}\ninline float FloatList::value(int index) const {\n  // @@protoc_insertion_point(field_get:tensorflow.FloatList.value)\n  return _internal_value(index);\n}\ninline void FloatList::set_value(int index, float value) {\n  value_.Set(index, value);\n  // @@protoc_insertion_point(field_set:tensorflow.FloatList.value)\n}\ninline void FloatList::_internal_add_value(float value) {\n  value_.Add(value);\n}\ninline void FloatList::add_value(float value) {\n  _internal_add_value(value);\n  // @@protoc_insertion_point(field_add:tensorflow.FloatList.value)\n}\ninline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >&\nFloatList::_internal_value() const {\n  return value_;\n}\ninline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >&\nFloatList::value() const {\n  // @@protoc_insertion_point(field_list:tensorflow.FloatList.value)\n  return _internal_value();\n}\ninline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >*\nFloatList::_internal_mutable_value() {\n  return &value_;\n}\ninline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >*\nFloatList::mutable_value() {\n  // @@protoc_insertion_point(field_mutable_list:tensorflow.FloatList.value)\n  return _internal_mutable_value();\n}\n\n// -------------------------------------------------------------------\n\n// Int64List\n\n// repeated int64 value = 1 [packed = true];\ninline int Int64List::_internal_value_size() const {\n  return value_.size();\n}\ninline int Int64List::value_size() const {\n  return _internal_value_size();\n}\ninline void Int64List::clear_value() {\n  value_.Clear();\n}\ninline ::PROTOBUF_NAMESPACE_ID::int64 Int64List::_internal_value(int index) const {\n  return value_.Get(index);\n}\ninline ::PROTOBUF_NAMESPACE_ID::int64 Int64List::value(int index) const {\n  // @@protoc_insertion_point(field_get:tensorflow.Int64List.value)\n  return _internal_value(index);\n}\ninline void Int64List::set_value(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) {\n  value_.Set(index, value);\n  // @@protoc_insertion_point(field_set:tensorflow.Int64List.value)\n}\ninline void Int64List::_internal_add_value(::PROTOBUF_NAMESPACE_ID::int64 value) {\n  value_.Add(value);\n}\ninline void Int64List::add_value(::PROTOBUF_NAMESPACE_ID::int64 value) {\n  _internal_add_value(value);\n  // @@protoc_insertion_point(field_add:tensorflow.Int64List.value)\n}\ninline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >&\nInt64List::_internal_value() const {\n  return value_;\n}\ninline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >&\nInt64List::value() const {\n  // @@protoc_insertion_point(field_list:tensorflow.Int64List.value)\n  return _internal_value();\n}\ninline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >*\nInt64List::_internal_mutable_value() {\n  return &value_;\n}\ninline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >*\nInt64List::mutable_value() {\n  // @@protoc_insertion_point(field_mutable_list:tensorflow.Int64List.value)\n  return _internal_mutable_value();\n}\n\n// -------------------------------------------------------------------\n\n// Feature\n\n// .tensorflow.BytesList bytes_list = 1;\ninline bool Feature::_internal_has_bytes_list() const {\n  return kind_case() == kBytesList;\n}\ninline bool Feature::has_bytes_list() const {\n  return _internal_has_bytes_list();\n}\ninline void Feature::set_has_bytes_list() {\n  _oneof_case_[0] = kBytesList;\n}\ninline void Feature::clear_bytes_list() {\n  if (_internal_has_bytes_list()) {\n    if (GetArena() == nullptr) {\n      delete kind_.bytes_list_;\n    }\n    clear_has_kind();\n  }\n}\ninline ::tensorflow::BytesList* Feature::release_bytes_list() {\n  // @@protoc_insertion_point(field_release:tensorflow.Feature.bytes_list)\n  if (_internal_has_bytes_list()) {\n    clear_has_kind();\n      ::tensorflow::BytesList* temp = kind_.bytes_list_;\n    if (GetArena() != nullptr) {\n      temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);\n    }\n    kind_.bytes_list_ = nullptr;\n    return temp;\n  } else {\n    return nullptr;\n  }\n}\ninline const ::tensorflow::BytesList& Feature::_internal_bytes_list() const {\n  return _internal_has_bytes_list()\n      ? *kind_.bytes_list_\n      : reinterpret_cast< ::tensorflow::BytesList&>(::tensorflow::_BytesList_default_instance_);\n}\ninline const ::tensorflow::BytesList& Feature::bytes_list() const {\n  // @@protoc_insertion_point(field_get:tensorflow.Feature.bytes_list)\n  return _internal_bytes_list();\n}\ninline ::tensorflow::BytesList* Feature::unsafe_arena_release_bytes_list() {\n  // @@protoc_insertion_point(field_unsafe_arena_release:tensorflow.Feature.bytes_list)\n  if (_internal_has_bytes_list()) {\n    clear_has_kind();\n    ::tensorflow::BytesList* temp = kind_.bytes_list_;\n    kind_.bytes_list_ = nullptr;\n    return temp;\n  } else {\n    return nullptr;\n  }\n}\ninline void Feature::unsafe_arena_set_allocated_bytes_list(::tensorflow::BytesList* bytes_list) {\n  clear_kind();\n  if (bytes_list) {\n    set_has_bytes_list();\n    kind_.bytes_list_ = bytes_list;\n  }\n  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:tensorflow.Feature.bytes_list)\n}\ninline ::tensorflow::BytesList* Feature::_internal_mutable_bytes_list() {\n  if (!_internal_has_bytes_list()) {\n    clear_kind();\n    set_has_bytes_list();\n    kind_.bytes_list_ = CreateMaybeMessage< ::tensorflow::BytesList >(GetArena());\n  }\n  return kind_.bytes_list_;\n}\ninline ::tensorflow::BytesList* Feature::mutable_bytes_list() {\n  // @@protoc_insertion_point(field_mutable:tensorflow.Feature.bytes_list)\n  return _internal_mutable_bytes_list();\n}\n\n// .tensorflow.FloatList float_list = 2;\ninline bool Feature::_internal_has_float_list() const {\n  return kind_case() == kFloatList;\n}\ninline bool Feature::has_float_list() const {\n  return _internal_has_float_list();\n}\ninline void Feature::set_has_float_list() {\n  _oneof_case_[0] = kFloatList;\n}\ninline void Feature::clear_float_list() {\n  if (_internal_has_float_list()) {\n    if (GetArena() == nullptr) {\n      delete kind_.float_list_;\n    }\n    clear_has_kind();\n  }\n}\ninline ::tensorflow::FloatList* Feature::release_float_list() {\n  // @@protoc_insertion_point(field_release:tensorflow.Feature.float_list)\n  if (_internal_has_float_list()) {\n    clear_has_kind();\n      ::tensorflow::FloatList* temp = kind_.float_list_;\n    if (GetArena() != nullptr) {\n      temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);\n    }\n    kind_.float_list_ = nullptr;\n    return temp;\n  } else {\n    return nullptr;\n  }\n}\ninline const ::tensorflow::FloatList& Feature::_internal_float_list() const {\n  return _internal_has_float_list()\n      ? *kind_.float_list_\n      : reinterpret_cast< ::tensorflow::FloatList&>(::tensorflow::_FloatList_default_instance_);\n}\ninline const ::tensorflow::FloatList& Feature::float_list() const {\n  // @@protoc_insertion_point(field_get:tensorflow.Feature.float_list)\n  return _internal_float_list();\n}\ninline ::tensorflow::FloatList* Feature::unsafe_arena_release_float_list() {\n  // @@protoc_insertion_point(field_unsafe_arena_release:tensorflow.Feature.float_list)\n  if (_internal_has_float_list()) {\n    clear_has_kind();\n    ::tensorflow::FloatList* temp = kind_.float_list_;\n    kind_.float_list_ = nullptr;\n    return temp;\n  } else {\n    return nullptr;\n  }\n}\ninline void Feature::unsafe_arena_set_allocated_float_list(::tensorflow::FloatList* float_list) {\n  clear_kind();\n  if (float_list) {\n    set_has_float_list();\n    kind_.float_list_ = float_list;\n  }\n  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:tensorflow.Feature.float_list)\n}\ninline ::tensorflow::FloatList* Feature::_internal_mutable_float_list() {\n  if (!_internal_has_float_list()) {\n    clear_kind();\n    set_has_float_list();\n    kind_.float_list_ = CreateMaybeMessage< ::tensorflow::FloatList >(GetArena());\n  }\n  return kind_.float_list_;\n}\ninline ::tensorflow::FloatList* Feature::mutable_float_list() {\n  // @@protoc_insertion_point(field_mutable:tensorflow.Feature.float_list)\n  return _internal_mutable_float_list();\n}\n\n// .tensorflow.Int64List int64_list = 3;\ninline bool Feature::_internal_has_int64_list() const {\n  return kind_case() == kInt64List;\n}\ninline bool Feature::has_int64_list() const {\n  return _internal_has_int64_list();\n}\ninline void Feature::set_has_int64_list() {\n  _oneof_case_[0] = kInt64List;\n}\ninline void Feature::clear_int64_list() {\n  if (_internal_has_int64_list()) {\n    if (GetArena() == nullptr) {\n      delete kind_.int64_list_;\n    }\n    clear_has_kind();\n  }\n}\ninline ::tensorflow::Int64List* Feature::release_int64_list() {\n  // @@protoc_insertion_point(field_release:tensorflow.Feature.int64_list)\n  if (_internal_has_int64_list()) {\n    clear_has_kind();\n      ::tensorflow::Int64List* temp = kind_.int64_list_;\n    if (GetArena() != nullptr) {\n      temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);\n    }\n    kind_.int64_list_ = nullptr;\n    return temp;\n  } else {\n    return nullptr;\n  }\n}\ninline const ::tensorflow::Int64List& Feature::_internal_int64_list() const {\n  return _internal_has_int64_list()\n      ? *kind_.int64_list_\n      : reinterpret_cast< ::tensorflow::Int64List&>(::tensorflow::_Int64List_default_instance_);\n}\ninline const ::tensorflow::Int64List& Feature::int64_list() const {\n  // @@protoc_insertion_point(field_get:tensorflow.Feature.int64_list)\n  return _internal_int64_list();\n}\ninline ::tensorflow::Int64List* Feature::unsafe_arena_release_int64_list() {\n  // @@protoc_insertion_point(field_unsafe_arena_release:tensorflow.Feature.int64_list)\n  if (_internal_has_int64_list()) {\n    clear_has_kind();\n    ::tensorflow::Int64List* temp = kind_.int64_list_;\n    kind_.int64_list_ = nullptr;\n    return temp;\n  } else {\n    return nullptr;\n  }\n}\ninline void Feature::unsafe_arena_set_allocated_int64_list(::tensorflow::Int64List* int64_list) {\n  clear_kind();\n  if (int64_list) {\n    set_has_int64_list();\n    kind_.int64_list_ = int64_list;\n  }\n  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:tensorflow.Feature.int64_list)\n}\ninline ::tensorflow::Int64List* Feature::_internal_mutable_int64_list() {\n  if (!_internal_has_int64_list()) {\n    clear_kind();\n    set_has_int64_list();\n    kind_.int64_list_ = CreateMaybeMessage< ::tensorflow::Int64List >(GetArena());\n  }\n  return kind_.int64_list_;\n}\ninline ::tensorflow::Int64List* Feature::mutable_int64_list() {\n  // @@protoc_insertion_point(field_mutable:tensorflow.Feature.int64_list)\n  return _internal_mutable_int64_list();\n}\n\ninline bool Feature::has_kind() const {\n  return kind_case() != KIND_NOT_SET;\n}\ninline void Feature::clear_has_kind() {\n  _oneof_case_[0] = KIND_NOT_SET;\n}\ninline Feature::KindCase Feature::kind_case() const {\n  return Feature::KindCase(_oneof_case_[0]);\n}\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// Features\n\n// map<string, .tensorflow.Feature> feature = 1;\ninline int Features::_internal_feature_size() const {\n  return feature_.size();\n}\ninline int Features::feature_size() const {\n  return _internal_feature_size();\n}\ninline void Features::clear_feature() {\n  feature_.Clear();\n}\ninline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >&\nFeatures::_internal_feature() const {\n  return feature_.GetMap();\n}\ninline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >&\nFeatures::feature() const {\n  // @@protoc_insertion_point(field_map:tensorflow.Features.feature)\n  return _internal_feature();\n}\ninline ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >*\nFeatures::_internal_mutable_feature() {\n  return feature_.MutableMap();\n}\ninline ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::Feature >*\nFeatures::mutable_feature() {\n  // @@protoc_insertion_point(field_mutable_map:tensorflow.Features.feature)\n  return _internal_mutable_feature();\n}\n\n// -------------------------------------------------------------------\n\n// FeatureList\n\n// repeated .tensorflow.Feature feature = 1;\ninline int FeatureList::_internal_feature_size() const {\n  return feature_.size();\n}\ninline int FeatureList::feature_size() const {\n  return _internal_feature_size();\n}\ninline void FeatureList::clear_feature() {\n  feature_.Clear();\n}\ninline ::tensorflow::Feature* FeatureList::mutable_feature(int index) {\n  // @@protoc_insertion_point(field_mutable:tensorflow.FeatureList.feature)\n  return feature_.Mutable(index);\n}\ninline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature >*\nFeatureList::mutable_feature() {\n  // @@protoc_insertion_point(field_mutable_list:tensorflow.FeatureList.feature)\n  return &feature_;\n}\ninline const ::tensorflow::Feature& FeatureList::_internal_feature(int index) const {\n  return feature_.Get(index);\n}\ninline const ::tensorflow::Feature& FeatureList::feature(int index) const {\n  // @@protoc_insertion_point(field_get:tensorflow.FeatureList.feature)\n  return _internal_feature(index);\n}\ninline ::tensorflow::Feature* FeatureList::_internal_add_feature() {\n  return feature_.Add();\n}\ninline ::tensorflow::Feature* FeatureList::add_feature() {\n  // @@protoc_insertion_point(field_add:tensorflow.FeatureList.feature)\n  return _internal_add_feature();\n}\ninline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature >&\nFeatureList::feature() const {\n  // @@protoc_insertion_point(field_list:tensorflow.FeatureList.feature)\n  return feature_;\n}\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// FeatureLists\n\n// map<string, .tensorflow.FeatureList> feature_list = 1;\ninline int FeatureLists::_internal_feature_list_size() const {\n  return feature_list_.size();\n}\ninline int FeatureLists::feature_list_size() const {\n  return _internal_feature_list_size();\n}\ninline void FeatureLists::clear_feature_list() {\n  feature_list_.Clear();\n}\ninline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >&\nFeatureLists::_internal_feature_list() const {\n  return feature_list_.GetMap();\n}\ninline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >&\nFeatureLists::feature_list() const {\n  // @@protoc_insertion_point(field_map:tensorflow.FeatureLists.feature_list)\n  return _internal_feature_list();\n}\ninline ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >*\nFeatureLists::_internal_mutable_feature_list() {\n  return feature_list_.MutableMap();\n}\ninline ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::tensorflow::FeatureList >*\nFeatureLists::mutable_feature_list() {\n  // @@protoc_insertion_point(field_mutable_map:tensorflow.FeatureLists.feature_list)\n  return _internal_mutable_feature_list();\n}\n\n#ifdef __GNUC__\n  #pragma GCC diagnostic pop\n#endif  // __GNUC__\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n// -------------------------------------------------------------------\n\n\n// @@protoc_insertion_point(namespace_scope)\n\n}  // namespace tensorflow\n\n// @@protoc_insertion_point(global_scope)\n\n#include <google/protobuf/port_undef.inc>\n#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_feature_2eproto\n"
  },
  {
    "path": "src/proto/feature.proto",
    "content": "// Protocol messages for describing features for machine learning model\n// training or inference.\n//\n// There are three base Feature types:\n//   - bytes\n//   - float\n//   - int64\n//\n// A Feature contains Lists which may hold zero or more values.  These\n// lists are the base values BytesList, FloatList, Int64List.\n//\n// Features are organized into categories by name.  The Features message\n// contains the mapping from name to Feature.\n//\n// Example Features for a movie recommendation application:\n//   feature {\n//     key: \"age\"\n//     value { float_list {\n//       value: 29.0\n//     }}\n//   }\n//   feature {\n//     key: \"movie\"\n//     value { bytes_list {\n//       value: \"The Shawshank Redemption\"\n//       value: \"Fight Club\"\n//     }}\n//   }\n//   feature {\n//     key: \"movie_ratings\"\n//     value { float_list {\n//       value: 9.0\n//       value: 9.7\n//     }}\n//   }\n//   feature {\n//     key: \"suggestion\"\n//     value { bytes_list {\n//       value: \"Inception\"\n//     }}\n//   }\n//   feature {\n//     key: \"suggestion_purchased\"\n//     value { int64_list {\n//       value: 1\n//     }}\n//   }\n//   feature {\n//     key: \"purchase_price\"\n//     value { float_list {\n//       value: 9.99\n//     }}\n//   }\n//\n\nsyntax = \"proto3\";\n\npackage tensorflow;\n\noption cc_enable_arenas = true;\noption java_outer_classname = \"FeatureProtos\";\noption java_multiple_files = true;\noption java_package = \"org.tensorflow.example\";\noption go_package = \"github.com/tensorflow/tensorflow/tensorflow/go/core/example/example_protos_go_proto\";\n\n// LINT.IfChange\n// Containers to hold repeated fundamental values.\nmessage BytesList {\n  repeated bytes value = 1;\n}\nmessage FloatList {\n  repeated float value = 1 [packed = true];\n}\nmessage Int64List {\n  repeated int64 value = 1 [packed = true];\n}\n\n// Containers for non-sequential data.\nmessage Feature {\n  // Each feature can be exactly one kind.\n  oneof kind {\n    BytesList bytes_list = 1;\n    FloatList float_list = 2;\n    Int64List int64_list = 3;\n  }\n}\n\nmessage Features {\n  // Map from feature name to feature.\n  map<string, Feature> feature = 1;\n}\n\n// Containers for sequential data.\n//\n// A FeatureList contains lists of Features.  These may hold zero or more\n// Feature values.\n//\n// FeatureLists are organized into categories by name.  The FeatureLists message\n// contains the mapping from name to FeatureList.\n//\nmessage FeatureList {\n  repeated Feature feature = 1;\n}\n\nmessage FeatureLists {\n  // Map from feature name to feature list.\n  map<string, FeatureList> feature_list = 1;\n}\n// LINT.ThenChange(\n//     https://www.tensorflow.org/code/tensorflow/python/training/training.py)\n"
  },
  {
    "path": "src/refinerangedialog.cpp",
    "content": "#include \"refinerangedialog.h\"\n#include \"ui_refinerangedialog.h\"\n\nRefineRangeDialog::RefineRangeDialog(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::RefineRangeDialog)\n{\n    ui->setupUi(this);\n    setWindowModality(Qt::WindowModal);\n\n    connect(ui->startSpinBox, SIGNAL(valueChanged(int)), this, SLOT(check()));\n    connect(ui->endSpinBox, SIGNAL(valueChanged(int)), this, SLOT(check()));\n}\n\nvoid RefineRangeDialog::setMaxImage(int max_images){\n    ui->startSpinBox->setRange(1, max_images+1);\n    ui->endSpinBox->setRange(1, max_images+1);\n}\n\nint RefineRangeDialog::getStart(){\n    return ui->startSpinBox->value();\n}\n\n\nint RefineRangeDialog::getEnd(){\n    return ui->endSpinBox->value();\n}\n\nvoid RefineRangeDialog::setCurrentImage(int i){\n    ui->startSpinBox->setValue(i);\n    ui->endSpinBox->setValue(i);\n}\n\nbool RefineRangeDialog::check(){\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);\n\n    if(getStart() > getEnd()) return false;\n\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n\n    return true;\n}\n\nRefineRangeDialog::~RefineRangeDialog()\n{\n    delete ui;\n}\n"
  },
  {
    "path": "src/refinerangedialog.h",
    "content": "#ifndef REFINERANGEDIALOG_H\n#define REFINERANGEDIALOG_H\n\n#include <QDialog>\n#include <QPushButton>\n\nnamespace Ui {\nclass RefineRangeDialog;\n}\n\nclass RefineRangeDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit RefineRangeDialog(QWidget *parent = nullptr);\n    ~RefineRangeDialog();\n\n    int getStart();\n    int getEnd();\npublic slots:\n    void setMaxImage(int max_images);\n    void setCurrentImage(int i);\nprivate slots:\n    bool check();\nprivate:\n    Ui::RefineRangeDialog *ui;\n\n};\n\n#endif // REFINERANGEDIALOG_H\n"
  },
  {
    "path": "src/refinerangedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RefineRangeDialog</class>\n <widget class=\"QDialog\" name=\"RefineRangeDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>349</width>\n    <height>109</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QFormLayout\" name=\"formLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Start image: </string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label_2\">\n     <property name=\"text\">\n      <string>End image: </string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"1\">\n    <widget class=\"QSpinBox\" name=\"startSpinBox\"/>\n   </item>\n   <item row=\"2\" column=\"1\">\n    <widget class=\"QSpinBox\" name=\"endSpinBox\"/>\n   </item>\n   <item row=\"3\" column=\"1\">\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RefineRangeDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RefineRangeDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/tfrecordexporter.cpp",
    "content": "#include \"tfrecordexporter.h\"\n\nvoid TFRecordExporter::generateLabelIds(const QString names_file){\n    id_map.clear();\n\n    // Force sort list\n    QStringList class_list;\n    QFile fh(names_file);\n\n    if (fh.open(QIODevice::ReadOnly)) {\n\n        while (!fh.atEnd()) {\n            // Darknet name file is just a newline delimited list of classes\n            QByteArray line = fh.readLine();\n            class_list.append(line);\n        }\n    }\n\n    if(class_list.size() == 0){\n        qWarning() << \"No classes found in names file.\";\n        return;\n    }\n\n    int i = 1; // 0 is background\n    for(auto &name : class_list){\n        auto cleaned_name = name.simplified().toLower();\n        id_map[cleaned_name] = i++;\n        qDebug() << \"Adding: \" << cleaned_name << \" (\" << i << \")\";\n    }\n}\n\nvoid TFRecordExporter::add_int64_feature(tensorflow::Features &features,\n                                         std::string key,\n                                         std::vector<int64> values){\n\n    auto map = features.mutable_feature();\n\n    for(auto value : values){\n        (*map)[key].mutable_int64_list()->add_value(value);\n    }\n}\n\nvoid TFRecordExporter::add_bytes_feature(tensorflow::Features &features,\n                                         std::string key,\n                                         std::vector<void*> values,\n                                         std::vector<size_t> sizes){\n\n    auto map = features.mutable_feature();\n\n    for(size_t i=0; i < values.size(); ++i){\n        (*map)[key].mutable_bytes_list()->add_value(static_cast<char*>(values.at(i)),\n                                                    sizes.at(i));\n    }\n\n}\n\nvoid TFRecordExporter::add_bytes_feature(tensorflow::Features &features,\n                                         std::string key,\n                                         std::vector<std::string> values){\n\n    auto map = features.mutable_feature();\n\n    for(auto &value : values){\n        (*map)[key].mutable_bytes_list()->add_value(value);\n    }\n}\n\nvoid TFRecordExporter::add_float_feature(tensorflow::Features &features,\n                                         std::string key,\n                                         std::vector<float> values){\n    auto map = features.mutable_feature();\n\n    for(auto value : values){\n        (*map)[key].mutable_float_list()->add_value(value);\n    }\n}\n\nvoid TFRecordExporter::create_weights(){\n\n    QMap<int, int> counts;\n    project->getClassCounts(counts);\n    int n_class = counts.size();\n\n    int total_labels = 0;\n    for(auto class_id : counts.keys()){\n        total_labels += counts[class_id];\n    }\n\n    float total_weight = 0;\n    for(auto class_id : counts.keys()){\n        weight_map[class_id] = total_labels / counts[class_id];\n        total_weight += weight_map[class_id];\n    }\n\n    for(auto &class_id : weight_map.keys()){\n        weight_map[class_id] = weight_map[class_id]/total_weight * n_class;\n        qInfo() << \"Set weight for class \" << class_id << \"to: \" << weight_map[class_id];\n    }\n\n}\n\nbool TFRecordExporter::create_example(tensorflow::Example &example, QString image_path)\n{\n\n    QList<BoundingBox> labels;\n    project->getLabels(image_path, labels);\n\n    if(labels.size() == 0 && !export_unlabelled){\n        return false;\n    }else{\n        qInfo() << \"Processing \" << image_path;\n    }\n\n    auto db_dir = project->getDbFolder();\n    auto abs_path = QFileInfo(QDir::cleanPath(db_dir.filePath(image_path))).absoluteFilePath();\n\n    QFile image(abs_path);\n    image.open(QIODevice::ReadOnly);\n    QByteArray buffer = image.readAll();\n\n    auto im = cv::imdecode(cv::Mat(1, buffer.size(), CV_8UC1, buffer.data()), cv::IMREAD_UNCHANGED);\n    auto image_ext = QFileInfo(image_path).suffix();\n\n    tensorflow::Features features;\n\n    // Image data\n    add_int64_feature(features, \"image/width\", {im.cols});\n    add_int64_feature(features, \"image/height\", {im.rows});\n    add_bytes_feature(features, \"image/filename\", {image_path.toStdString()});\n    add_bytes_feature(features, \"image/source_id\", {QString(\"%1\").arg(qHash(image_path)).toStdString()});\n    add_bytes_feature(features, \"image/format\", {image_ext.toStdString()});\n\n    add_bytes_feature(features, \"image/encoded\", {buffer.data()},\n                                                 {static_cast<size_t>(buffer.size())});\n    image.close();\n\n    std::vector<float> xmins;\n    std::vector<float> xmaxs;\n    std::vector<float> ymins;\n    std::vector<float> ymaxs;\n    std::vector<std::string> classes_text;\n    std::vector<int64> classes;\n    std::vector<float> class_weights;\n\n    for(auto &label : labels){\n\n        // Check if this label exists in the database\n        if(id_map.find(label.classname.toLower()) == id_map.end()){\n            qWarning() << \"Couldn't find this label in the names file: \" << label.classname.toLower();\n            continue;\n        }\n\n        xmins.push_back(label.rect.left());\n        xmaxs.push_back(label.rect.right());\n        ymins.push_back(label.rect.bottom());\n        ymaxs.push_back(label.rect.top());\n        classes_text.push_back(label.classname.toStdString());\n        classes.push_back(id_map[label.classname]);\n        class_weights.push_back(weight_map[id_map[label.classname]]);\n\n    }\n\n    // Bounding boxes\n    add_float_feature(features, \"image/object/bbox/xmin\", xmins);\n    add_float_feature(features, \"image/object/bbox/xmax\", xmaxs );\n    add_float_feature(features, \"image/object/bbox/ymin\", ymins );\n    add_float_feature(features, \"image/object/bbox/ymaxs\", ymaxs );\n    add_bytes_feature(features, \"image/object/class/text\", classes_text );\n    add_int64_feature(features, \"image/object/class/label\", classes );\n    //add_float_feature(features, \"image/object/weight\", class_weights );\n\n    *example.mutable_features() = features;\n\n    return true;\n}\n\nvoid TFRecordExporter::setNumberShards(int n){\n    number_shards = n;\n}\n\nbool TFRecordExporter::processImages(const QString folder, const QString record_prefix, const QList<QString> images, export_image_type split_type){\n\n    QString image_path;\n    QList<BoundingBox> labels;\n\n    QProgressDialog progress(\"...\", \"Abort\", 0, images.size(), static_cast<QWidget*>(parent()));\n    progress.setWindowModality(Qt::WindowModal);\n\n    if(folder == \"\"){\n        qCritical() << \"Invalid folder specified.\";\n        return false;\n    }\n\n    QString split_text = \"\";\n    if(split_type == EXPORT_VAL){\n        split_text = \"VAL\";\n        progress.setWindowTitle(\"Exporting validation images\");\n    }else if(split_type == EXPORT_TRAIN){\n        split_text = \"TRAIN\";\n        progress.setWindowTitle(\"Exporting train images\");\n    }else if(split_type == EXPORT_TEST){\n        split_text = \"TEST\";\n        progress.setWindowTitle(\"Exporting test images\");\n    }else{\n        split_text = \"UNASSIGNED\";\n    }\n\n    if(!disable_progress){\n        progress.hide();\n    }\n\n    int i = 0;\n    int processed = 0;\n\n    // Open shards\n    QList<QFile *> shards;\n    for(int s=0; s < number_shards; s++){\n        qInfo() << \"Creating output shard files\";\n        QString shard_name = QString(\"%1-%2-of-%3.tfrecord\")\n                                    .arg(record_prefix)\n                                    .arg(s, 5, 10, static_cast<QChar>('0'))\n                                    .arg(number_shards, 5, 10, static_cast<QChar>('0'));\n        QString shard_path = QDir(output_folder).absoluteFilePath(shard_name);\n        auto shard = new QFile(shard_path);\n\n        if(shard->exists()){\n            shard->remove();\n            qWarning() << \"Deleting and overwriting \" << shard_path;\n        }\n\n        shard->open(QFile::Append);\n        shards.push_back(shard);\n    }\n\n    foreach(image_path, images){\n\n        if(progress.wasCanceled()){\n            break;\n        }\n\n        project->getLabels(image_path, labels);\n\n        tensorflow::Example example;\n        bool res = create_example(example, image_path);\n\n        if(res){\n            qDebug() << \"creating record for \" << image_path;\n            int shard_id = processed++ % number_shards;\n\n            auto serialised_example = example.SerializeAsString();\n            auto shard = shards.at(shard_id);\n\n            if(shard->isOpen()){\n                // Write header (data length, crc of data length)\n                size_t data_len = example.ByteSizeLong();\n                auto data_len_ptr = reinterpret_cast<const char*>(&data_len);\n                int nbytes = shard->write(data_len_ptr, 8);\n\n                uint32_t crc32 = tf_crc::Mask(tf_crc::Value(data_len_ptr, 8));\n                shard->write(reinterpret_cast<const char*>(&crc32), 4);\n\n                // Write record\n                nbytes += shard->write(serialised_example.data(),\n                                       serialised_example.size());\n\n                // Write footer (CRC32 of data)\n                crc32 = tf_crc::Mask(tf_crc::Value(serialised_example.data(),\n                                                   serialised_example.size()));\n                shard->write(reinterpret_cast<const char*>(&crc32), 4);\n\n                if(nbytes <=0){\n                    qCritical() << \"Failed to write shard\";\n                }else{\n                    qDebug() << \"Wrote \" << nbytes << \"bytes\";\n                }\n\n            }else{\n                qCritical() << \"Shard is not open for writing\";\n            }\n        }\n\n        if(!disable_progress){\n            progress.setValue(i++);\n            progress.setLabelText(image_path);\n        }\n\n    }\n\n    for(auto &shard : shards){\n        shard->close();\n        delete shard;\n    }\n\n    return true;\n}\n\nvoid TFRecordExporter::process(void){\n    create_weights();\n    processImages(train_folder, \"train\", train_set, EXPORT_TRAIN);\n    processImages(val_folder, \"val\", validation_set, EXPORT_VAL);\n}\n"
  },
  {
    "path": "src/tfrecordexporter.h",
    "content": "#ifndef TFRECORDEXPORTER_H\n#define TFRECORDEXPORTER_H\n\n#include \"baseexporter.h\"\n\n#include <proto/example.pb.h>\n#include <proto/feature.pb.h>\n#include <crc32.h>\n\n#include <fstream>\n\nclass TFRecordExporter : public BaseExporter\n{\npublic:\n    explicit TFRecordExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n    void setNumberShards(int n);\n    void generateLabelIds(const QString names_file);\n    void process(void);\nprivate:\n    bool create_example(tensorflow::Example &example, QString image_path);\n    void add_int64_feature(tensorflow::Features &features, std::string key, std::vector<int64> values);\n    void add_bytes_feature(tensorflow::Features &features, std::string key, std::vector<void*> values, std::vector<size_t> size);\n    void add_bytes_feature(tensorflow::Features &features, std::string key, std::vector<std::string> values);\n    void add_float_feature(tensorflow::Features &features, std::string key, std::vector<float> values);\n    QMap<int, float> weight_map;\n    void create_weights();\n    bool processImages(const QString folder, const QString record_prefix, const QList<QString> images, export_image_type split_type);\n    int number_shards = 10;\n};\n\n#endif // TFRECORDEXPORTER_H\n"
  },
  {
    "path": "src/tfrecordimporter.cpp",
    "content": "#include \"tfrecordimporter.h\"\n\nvoid TFRecordImporter::find_example_boundaries(QByteArray buffer, std::vector<size_t> &record_offsets, std::vector<size_t> &record_sizes){\n\n    size_t offset = 0;\n    size_t next_offset = 0;\n    size_t header_size = 12;\n    size_t footer_size = 4;\n\n    size_t buffer_size = static_cast<unsigned long long>(buffer.size());\n\n    while(offset < buffer_size){\n        QByteArray data_len_buf = buffer.mid(static_cast<int>(offset), 8);\n        QByteArray data_len_crc_buf = buffer.mid(static_cast<int>(offset)+8, 4);\n\n        size_t data_size = *reinterpret_cast<size_t*>(data_len_buf.data());\n        size_t data_len_crc = *reinterpret_cast<uint32_t*>(data_len_crc_buf.data());\n\n        uint32_t crc = tf_crc::Mask(tf_crc::Value(data_len_buf.data(), 8));\n\n        if(data_len_crc == crc){\n            // 4 to account for header and footer\n            next_offset = offset + data_size + header_size + footer_size;\n            record_offsets.push_back(offset + header_size);\n            record_sizes.push_back(data_size + footer_size);\n            qWarning() << \"Found record at \" << offset << \" with size: \" << data_size;\n        }else{\n            qWarning() << \"Incorrect CRC, corrupt file?\";\n            break;\n        }\n\n        offset = next_offset;\n    }\n}\n\nvoid TFRecordImporter::process_example(tensorflow::Example &example){\n    // Image metadata\n    auto map = example.features().feature();\n    int image_height = map[\"image/height\"].int64_list().value(0);\n    int image_width = map[\"image/width\"].int64_list().value(0);\n    std::string image_filename = map[\"image/filename\"].bytes_list().value(0);\n    std::string source_id = map[\"image/source_id\"].bytes_list().value(0);\n    std::string ext = map[\"image/format\"].bytes_list().value(0);\n    std::string image_data = map[\"image/encoded\"].bytes_list().value(0);\n\n    cv::Mat cv_buffer = cv::Mat(1,\n                                static_cast<int>(image_data.size()),\n                                CV_8UC1,\n                                const_cast<char*>(image_data.data()));\n\n    qDebug() << \"Performing image integrity check\";\n    cv::Mat image = cv::imdecode(cv_buffer, cv::IMREAD_UNCHANGED);\n    if(image_height != image.rows){\n        qWarning() << \"Ignoring example: decoded image height does not match record height\";\n        return;\n    }\n\n    if(image_width != image.cols){\n        qWarning() << \"Ignoring record: decoded image height does not match record height\";\n        return;\n    }\n\n    int n_boxes = map[\"image/object/bbox/xmin\"].float_list().value_size();\n    qDebug() << \"Image countains: \" << n_boxes << \" bounding boxes\";\n\n    if(n_boxes == 0 && !import_unlabelled){\n        return;\n    }\n\n    project->addAsset(QString::fromStdString(image_filename));\n\n    for(int i=0; i < n_boxes; ++i){\n\n        auto xmin = map[\"image/object/bbox/xmin\"].float_list().value(i);\n        auto xmax = map[\"image/object/bbox/xmax\"].float_list().value(i);\n        auto ymin = map[\"image/object/bbox/ymin\"].float_list().value(i);\n        auto ymax = map[\"image/object/bbox/ymax\"].float_list().value(i);\n        auto class_name = map[\"image/object/class/text\"].bytes_list().value(i);\n        auto class_id = map[\"image/object/class/label\"].int64_list().value(i);\n\n        BoundingBox box;\n        box.classid = class_id;\n        box.classname = QString::fromStdString(class_name);\n        box.rect = QRect(QPoint(xmin*image_width, ymin*image_height),\n                         QPoint(xmax*image_width, ymax*image_height));\n\n        qDebug() << printBoundingBox(box);\n        project->addLabel(QString::fromStdString(image_filename), box);\n    }\n\n\n}\n\nvoid TFRecordImporter::import_records(QString filename_mask){\n\n    QFileInfo filemask(filename_mask);\n    auto dir_name = filemask.absoluteDir().absolutePath();\n\n    QDirIterator it(dir_name,\n                    {filemask.baseName()},\n                    QDir::Files);\n    QStringList record_files;\n    while (it.hasNext()) {\n        auto record = QDir(it.next()).canonicalPath();\n        record_files.append(record);\n        qDebug() << \"Found : \" << record;\n    }\n\n    record_files.removeDuplicates();\n    record_files.sort();\n\n    for(auto &record : record_files){\n        import(record);\n    }\n}\n\nvoid TFRecordImporter::import(QString filename){\n\n    qDebug() << \"Processing record :\" << filename;\n\n    QFile f(filename);\n\n    f.open(QIODevice::ReadOnly);\n    auto buffer = f.readAll();\n\n    qDebug() << \"File size:\" << buffer.size();\n\n    std::vector<size_t> record_offsets;\n    std::vector<size_t> record_sizes;\n    find_example_boundaries(buffer, record_offsets, record_sizes);\n\n    qDebug() << \"Found : \" << record_offsets.size() << \" examples in file.\";\n\n    std::vector<tensorflow::Example> examples;\n    for(size_t i=0; i < record_offsets.size(); i++){\n        int curr_offset = static_cast<int>(record_offsets.at(i));\n        int record_length = static_cast<int>(record_sizes.at(i));\n        QByteArray example_buffer = buffer.mid(curr_offset, record_length);\n\n        tensorflow::Example example;\n        example.ParseFromArray(example_buffer.data(), example_buffer.size());\n        process_example(example);\n    }\n\n\n}\n"
  },
  {
    "path": "src/tfrecordimporter.h",
    "content": "#ifndef TFRECORDIMPORTER_H\n#define TFRECORDIMPORTER_H\n\n#include \"baseimporter.h\"\n\n#include <proto/example.pb.h>\n#include <proto/feature.pb.h>\n#include <crc32.h>\n#include <fstream>\n\nclass TFRecordImporter : public BaseImporter\n{\npublic:\n    using BaseImporter::import;\n\n    explicit TFRecordImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){\n        this->project = project;\n    }\n\n     void import_records(QString filename_mask);\n     void import(QString filename);\n\nprivate:\n    void find_example_boundaries(QByteArray buffer,\n                                 std::vector<size_t> &record_offsets,\n                                 std::vector<size_t> &record_sizes);\n    void process_example(tensorflow::Example &example);\n};\n\n#endif // TFRECORDIMPORTER_H\n"
  },
  {
    "path": "src/videoexporter.cpp",
    "content": "#include \"videoexporter.h\"\n\nstd::unordered_map<std::string ,int> VideoExporter::colour_hashmap{\n    { \"Cividis\", cv::COLORMAP_CIVIDIS },\n    { \"Inferno\", cv::COLORMAP_INFERNO },\n    { \"Magma\", cv::COLORMAP_MAGMA },\n    { \"Hot\", cv::COLORMAP_HOT },\n    { \"Bone\", cv::COLORMAP_BONE },\n    { \"Plasma\", cv::COLORMAP_PLASMA },\n    { \"Jet\", cv::COLORMAP_JET },\n    { \"Rainbow\", cv::COLORMAP_RAINBOW },\n    { \"Ocean\", cv::COLORMAP_OCEAN },\n    { \"Viridis\", cv::COLORMAP_VIRIDIS }\n};\n\nvoid VideoExporter::process()\n{\n\n    auto out_filename = QDir(output_folder).absoluteFilePath(filename);\n\n    cv::VideoWriter writer(out_filename.toStdString(), fourcc, fps, frame_size);\n    QList<QString> images;\n\n    if(export_unlabelled)\n        project->getImageList(images);\n    else\n        project->getLabelledImageList(images);\n\n    if(!writer.isOpened()){\n        qCritical() << \"Failed to open video writer\";\n    }\n\n    auto pbar = cliProgressBar();\n    double progress = 0;\n    int i = 0;\n\n    qInfo() << \"Writing video to file:\" << out_filename;\n\n    for(auto &abs_image_path : images){\n        // Read\n        auto image = cv::imread(abs_image_path.toStdString(), cv::IMREAD_UNCHANGED);\n\n        int w, h;\n        w = image.cols;\n        h = image.rows;\n\n        cv::Mat image_resized;\n        cv::resize(image, image_resized, frame_size);\n\n        if(image_resized.elemSize() == 2){\n            convert16(image_resized);\n        }\n\n        if (image_resized.channels() == 4){\n            cv::cvtColor(image_resized, image_resized, cv::COLOR_RGBA2RGB);\n        }else if(image_resized.channels() == 1){\n            cv::applyColorMap(image_resized, image_resized, colourmap);\n        }\n\n        if(this->display_boxes){\n            QList<BoundingBox> labels;\n            project->getLabels(abs_image_path, labels);\n\n            for(auto &label : labels){\n                double x_scale = static_cast<double>(w)/image_resized.cols;\n                double y_scale = static_cast<double>(h)/image_resized.rows;\n                drawBoundingBox(image_resized, label, x_scale, y_scale, this->box_thickness);\n\n            }\n        }\n\n        writer.write(image_resized);\n\n        progress = 100*static_cast<double>(i++)/images.size();\n        pbar.update(progress);\n        pbar.print();\n\n    }\n\n    writer.release();\n}\n\nvoid VideoExporter::drawBoundingBox(cv::Mat &source, BoundingBox box, double x_scale, double y_scale, int thickness){\n\n\n    int top_x = box.rect.x()/x_scale;\n    int top_y = box.rect.y()/y_scale;\n\n    auto rect = cv::Rect2i(top_x,\n                           top_y,\n                           box.rect.width()/x_scale,\n                           box.rect.height()/y_scale);\n\n    auto colour_list = QColor::colorNames();\n    QColor colour = QColor(colour_list.at(std::max(0, box.classid) % colour_list.size()) );\n    cv::Scalar color(colour.red(), colour.green(), colour.blue());\n\n    cv::rectangle(source, rect, color, thickness);\n\n    if(display_names){\n        auto label_string = QString(\"%1\").arg(box.classname).toStdString();\n\n        int baseline;\n        auto text_size = cv::getTextSize(label_string, cv::FONT_HERSHEY_COMPLEX_SMALL, this->font_scale, thickness, &baseline);\n\n        cv::Rect2i label_background(top_x,\n                                    top_y-text_size.height-5,\n                                    std::max(static_cast<int>(box.rect.width()/x_scale), text_size.width),\n                                    text_size.height+5);\n        cv::rectangle(source, label_background, color, -1);\n\n        auto text_colour = cv::Scalar(0,0,0);\n        if(colour.red() == 0\n            && colour.green() == 0\n            && colour.blue() == 0){\n            text_colour = {255,255,255};\n        }\n\n        cv::putText(source,\n                    label_string,\n                    {top_x, top_y-1},\n                    cv::FONT_HERSHEY_COMPLEX_SMALL,\n                    this->font_scale,\n                    text_colour,\n                    thickness);\n    }\n}\n\nvoid VideoExporter::convert16(cv::Mat &source, double minval, double maxval){\n\n    if(minval < 0 || maxval < 0){\n        cv::minMaxIdx(source, &minval, &maxval);\n    }\n\n    double range = maxval-minval;\n    double scale_factor = 255.0/range;\n\n    source.convertTo(source, CV_32FC1);\n    source -= minval;\n    source *= scale_factor;\n    source.convertTo(source, CV_8UC1);\n\n    return;\n}\n\nvoid VideoExporter::labelConfig(bool display_names, bool display_boxes, int box_thickness, double font_size){\n    this->display_names = display_names;\n    this->display_boxes = display_boxes;\n    this->box_thickness = box_thickness;\n    this->font_scale = font_size;\n}\n\nvoid VideoExporter::videoConfig(QString filename, QString fourcc_string, double fps, cv::Size frame_size, QString colourmap)\n{\n    this->filename = filename;\n    this->fps = fps;\n    this->fourcc = cv::VideoWriter::fourcc(fourcc_string.at(0).toLatin1(),\n                                           fourcc_string.at(1).toLatin1(),\n                                           fourcc_string.at(2).toLatin1(),\n                                           fourcc_string.at(3).toLatin1());\n    this->frame_size = frame_size;\n    this->colourmap = colour_hashmap[colourmap.toStdString()];\n}\n"
  },
  {
    "path": "src/videoexporter.h",
    "content": "#ifndef VIDEOEXPORTER_H\n#define VIDEOEXPORTER_H\n\n#include \"baseexporter.h\"\n#include <unordered_map>\n\nclass VideoExporter : public BaseExporter\n{\npublic:\n    explicit VideoExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}\n    void videoConfig(QString filename, QString fourcc_string = \"h264\", double fps = 10, cv::Size frame_size = {1280,720}, QString colormap=\"Inferno\");\n    void process();\n    void labelConfig(bool display_names=true, bool label_boxes=true, int box_thickness=1, double font_scale=1);\nprivate:\n    QString filename;\n    int fourcc;\n    int colourmap;\n    double fps = 10;\n    cv::Size frame_size;\n    bool display_names = true;\n    bool display_boxes = true;\n    bool box_thickness = 1;\n    double font_scale = 0.8;\n\n    void normalise(cv::Mat &image_raw, cv::Mat &image_norm);\n    static std::unordered_map<std::string ,int> colour_hashmap;\n    void convert16(cv::Mat &source, double minval = -1, double maxval = -1);\n    void drawBoundingBox(cv::Mat &source, BoundingBox box, double x_scale, double y_scale, int thickness=1);\n};\n\n#endif // VIDEOEXPORTER_H\n"
  }
]