Showing preview only (592K chars total). Download the full file or copy to clipboard to get everything.
Repository: jveitchmichaelis/deeplabel
Branch: master
Commit: f5978a1349cf
Files: 83
Total size: 564.5 KB
Directory structure:
gitextract_q_bu5e9m/
├── .github/
│ └── workflows/
│ ├── build_osx.yml
│ ├── build_ubuntu.yml
│ └── build_windows.yml
├── .gitignore
├── .gitmodules
├── DeepLabel.pro
├── README.md
├── deploy_mac.sh
├── fix_paths_mac.py
└── src/
├── baseexporter.cpp
├── baseexporter.h
├── baseimporter.cpp
├── baseimporter.h
├── birdsaiimporter.cpp
├── birdsaiimporter.h
├── boundingbox.h
├── cliparser.cpp
├── cliparser.h
├── cliprogressbar.h
├── cocoexporter.cpp
├── cocoexporter.h
├── cocoimporter.cpp
├── cocoimporter.h
├── crc32.cpp
├── crc32.h
├── darknetexporter.cpp
├── darknetexporter.h
├── darknetimporter.cpp
├── darknetimporter.h
├── detection/
│ ├── detectoropencv.cpp
│ ├── detectoropencv.h
│ ├── detectorsetupdialog.cpp
│ ├── detectorsetupdialog.h
│ └── detectorsetupdialog.ui
├── exportdialog.cpp
├── exportdialog.h
├── exportdialog.ui
├── exporter.h
├── gcpexporter.cpp
├── gcpexporter.h
├── imagedisplay.cpp
├── imagedisplay.h
├── imagedisplay.ui
├── imagelabel.cpp
├── imagelabel.h
├── importdialog.cpp
├── importdialog.h
├── importdialog.ui
├── importer.h
├── kittiexporter.cpp
├── kittiexporter.h
├── labelproject.cpp
├── labelproject.h
├── labelrefinedialog.cpp
├── labelrefinedialog.h
├── labelrefinedialog.ui
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── motimporter.cpp
├── motimporter.h
├── multitracker.cpp
├── multitracker.h
├── pascalvocexporter.cpp
├── pascalvocexporter.h
├── pascalvocimporter.cpp
├── pascalvocimporter.h
├── proto/
│ ├── example.pb.cc
│ ├── example.pb.h
│ ├── example.proto
│ ├── feature.pb.cc
│ ├── feature.pb.h
│ └── feature.proto
├── refinerangedialog.cpp
├── refinerangedialog.h
├── refinerangedialog.ui
├── tfrecordexporter.cpp
├── tfrecordexporter.h
├── tfrecordimporter.cpp
├── tfrecordimporter.h
├── videoexporter.cpp
└── videoexporter.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build_osx.yml
================================================
name: Build OS X
on: [push]
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install Qt 5 + OpenCV + protobuf
run: brew install qt@5 opencv protobuf
- name: Force link Qt
run: brew link qt@5 --force
- name: Compile protobuf messages
run: protoc --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto
- name: Run QMake
run: qmake -makefile -o Makefile DeepLabel.pro
- name: Run Make
run: make -j4
- name: Deploy
run: bash deploy_mac.sh ./release
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: osx-release
path: ./release/deeplabel.dmg
================================================
FILE: .github/workflows/build_ubuntu.yml
================================================
name: Build Ubuntu
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install Qt 5 + protobuf
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
- name: Clone OpenCV
run: >
git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv;
git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv_contrib;
- name: Compile OpenCV
run: >
cd opencv
&& mkdir build
&& cd build
&& cmake ..
-DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules
-DBUILD_TESTS=OFF
-DBUILD_PERF_TESTS=OFF ..
-DCMAKE_BUILD_TYPE=RELEASE
-DBUILD_EXAMPLES=OFF
-DBUILD_DOCS=OFF
-DBUILD_JAVA=OFF
-DBUILD_opencv_apps=OFF
-DBUILD_opencv_aruco=OFF
-DBUILD_opencv_barcode=OFF
-DBUILD_opencv_bgsegm=OFF
-DBUILD_opencv_bioinspired=OFF
-DBUILD_opencv_calib3d=OFF
-DBUILD_opencv_datasets=OFF
-DBUILD_opencv_dpm=OFF
-DBUILD_opencv_dnn_superres=OFF
-DBUILD_opencv_face=OFF
-DBUILD_opencv_features2d=OFF
-DBUILD_opencv_flann=OFF
-DBUILD_opencv_fuzzy=OFF
-DBUILD_opencv_gapi=OFF
-DBUILD_opencv_highgui=ON
-DBUILD_opencv_img_hash=OFF
-DBUILD_opencv_intensity_transform=OFF
-DBUILD_opencv_line_descriptor=OFF
-DBUILD_opencv_mcc=OFF
-DBUILD_opencv_objc_bindings_generator=OFF
-DBUILD_opencv_objdetect=OFF
-DBUILD_opencv_optflow=OFF
-DBUILD_opencv_phase_unwrapping=OFF
-DBUILD_opencv_photo=OFF
-DBUILD_opencv_rgbd=OFF
-DBUILD_opencv_saliency=OFF
-DBUILD_opencv_shape=OFF
-DBUILD_opencv_js_bindings_generator=OFF
-DBUILD_opencv_freetype=OFF
-DBUILD_opencv_quality=OFF
-DBUILD_opencv_reg=OFF
-DBUILD_opencv_stereo=OFF
-DBUILD_opencv_stitching=OFF
-DBUILD_opencv_structured_light=OFF
-DBUILD_opencv_superres=OFF
-DBUILD_opencv_surface_matching=OFF
-DBUILD_opencv_text=OFF
-DBUILD_opencv_ts=OFF
-DBUILD_opencv_videostab=OFF
-DBUILD_opencv_viz=OFF
-DBUILD_opencv_wechat_qrcode=OFF
-DBUILD_opencv_ximgproc=OFF
-DBUILD_opencv_xobjdetect=OFF
-DBUILD_opencv_xphoto=OFF
-DBUILD_opencv_xfeatures2d=OFF
&& make -j4
&& sudo make install
- name: Compile protobuf messages
run: protoc --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto
- name: Run QMake
run: qmake -makefile -o Makefile DeepLabel.pro
- name: Run Make
run: make -j4
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: linux-release
path: ./release/deeplabel
================================================
FILE: .github/workflows/build_windows.yml
================================================
name: Build Windows
on: [push]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install Qt 5 and mingw
run: choco install qt5-default mingw --confirm -r --no-progress
- name: Clone protobuf
run: git clone https://github.com/protocolbuffers/protobuf
- name: Install protobuf
run: >
cd protobuf/cmake;
mkdir build;
cd build;
$install_path=Resolve-Path ../../;
cmake ..
-Dprotobuf_BUILD_SHARED_LIBS=ON
-Dprotobuf_BUILD_EXAMPLES=OFF
-Dprotobuf_BUILD_TESTS=OFF
-Dprotobuf_WITH_ZLIB=ON
-Dprotobuf_BUILD_PROTOC_BINARIES=ON
-DCMAKE_INSTALL_PREFIX="$install_path";
cmake --build . --parallel 4 --target install --config Release
- name: Compile protobuf messages
run: ./protobuf/bin/protoc.exe --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto
- name: Clone OpenCV
run: >
git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv;
git clone --branch 4.5.3 --depth 1 https://github.com/opencv/opencv_contrib
- name: Compile OpenCV
run: >
cd opencv;
mkdir build;
cd build;
$extra_path=Resolve-Path ../../opencv_contrib/modules;
$install_path=Resolve-Path .;
cmake ..
-DOPENCV_EXTRA_MODULES_PATH="$extra_path"
-DBUILD_TESTS=OFF
-DBUILD_PERF_TESTS=OFF ..
-DCMAKE_BUILD_TYPE=RELEASE
-DBUILD_EXAMPLES=OFF
-DBUILD_DOCS=OFF
-DBUILD_JAVA=OFF
-DBUILD_opencv_apps=OFF
-DBUILD_opencv_aruco=OFF
-DBUILD_opencv_barcode=OFF
-DBUILD_opencv_bgsegm=OFF
-DBUILD_opencv_bioinspired=OFF
-DBUILD_opencv_calib3d=OFF
-DBUILD_opencv_datasets=OFF
-DBUILD_opencv_dpm=OFF
-DBUILD_opencv_dnn_superres=OFF
-DBUILD_opencv_face=OFF
-DBUILD_opencv_features2d=OFF
-DBUILD_opencv_flann=OFF
-DBUILD_opencv_fuzzy=OFF
-DBUILD_opencv_gapi=OFF
-DBUILD_opencv_highgui=ON
-DBUILD_opencv_img_hash=OFF
-DBUILD_opencv_intensity_transform=OFF
-DBUILD_opencv_line_descriptor=OFF
-DBUILD_opencv_mcc=OFF
-DBUILD_opencv_objc_bindings_generator=OFF
-DBUILD_opencv_objdetect=OFF
-DBUILD_opencv_optflow=OFF
-DBUILD_opencv_phase_unwrapping=OFF
-DBUILD_opencv_photo=OFF
-DBUILD_opencv_quality=OFF
-DBUILD_opencv_reg=OFF
-DBUILD_opencv_rgbd=OFF
-DBUILD_opencv_saliency=OFF
-DBUILD_opencv_shape=OFF
-DBUILD_opencv_js_bindings_generator=OFF
-DBUILD_opencv_freetype=OFF
-DBUILD_opencv_stereo=OFF
-DBUILD_opencv_stitching=OFF
-DBUILD_opencv_structured_light=OFF
-DBUILD_opencv_superres=OFF
-DBUILD_opencv_surface_matching=OFF
-DBUILD_opencv_text=OFF
-DBUILD_opencv_ts=OFF
-DBUILD_opencv_videostab=OFF
-DBUILD_opencv_viz=OFF
-DBUILD_opencv_wechat_qrcode=OFF
-DBUILD_opencv_ximgproc=OFF
-DBUILD_opencv_xobjdetect=OFF
-DBUILD_opencv_xphoto=OFF
-DBUILD_opencv_xfeatures2d=OFF
-DBUILD_opencv_world=ON
-DCMAKE_INSTALL_PREFIX="$install_path";
cmake --build . --parallel 4 --config Release --target install
- name: Run QMake
run: C:/Qt/5.15.2/mingw81_64/bin/qmake.exe -makefile -o Makefile DeepLabel.pro
- name: Run Make
run: mingw32-make -f Makefile.Release
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: windows-release
path: ./release/deeplabel
================================================
FILE: .gitignore
================================================
build/
*.pro.user*
Makefile
ui_*
.moc
.obj
*.stash
opencv
protobuf
================================================
FILE: .gitmodules
================================================
[submodule "QtAwesome"]
path = QtAwesome
url = https://github.com/gamecreature/QtAwesome.git
================================================
FILE: DeepLabel.pro
================================================
#-------------------------------------------------
#
# Project created by QtCreator 2017-10-04T17:31:00
#
#-------------------------------------------------
QT += core gui sql testlib concurrent xml
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = deeplabel
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
macx{
message("Mac")
#PKG_CONFIG = /usr/local/bin/pkg-config
CONFIG += c++17
# Build without FFMPEG unless you want a lot of pain later
CONFIG += link_pkgconfig
PKGCONFIG += protobuf
# If you use pkg-config, *everything* gets linked. Wasteful.
INCLUDEPATH += /usr/local/include/opencv4
LIBS += -L/usr/local/opt/opencv/lib/
LIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lopencv_tracking -lopencv_video -lopencv_videoio -lopencv_dnn
}
unix:!macx{
message("Linux")
#CONFIG += link_pkgconfig
#PKGCONFIG += opencv4
CONFIG += c++17
INCLUDEPATH += /usr/local/include/opencv4
LIBS += -L/usr/local/lib -lprotobuf -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lopencv_tracking -lopencv_video -lopencv_videoio -lopencv_dnn
}
win32{
message("Windows")
DEFINES += PROTOBUF_USE_DLLS
defined(WITH_CUDA){
message("Building with CUDA")
CONFIG += WITH_CUDA
INCLUDEPATH += "$$_PRO_FILE_PWD_/opencv/build_cuda/include"
LIBS += -L"$$_PRO_FILE_PWD_/opencv/build_cuda/x64/vc16/lib"
}else{
INCLUDEPATH += "$$_PRO_FILE_PWD_/opencv/build/include"
LIBS += -L"$$_PRO_FILE_PWD_/opencv/build/x64/vc16/lib"
}
INCLUDEPATH += "$$_PRO_FILE_PWD_/protobuf/include"
LIBS += -L"$$_PRO_FILE_PWD_/protobuf/lib"
#QMAKE_CXXFLAGS += "/std:c++17 /permissive-"
CONFIG(debug, debug|release) {
LIBS += -lopencv_world453d -llibprotobufd
}else{
LIBS += -lopencv_world453 -llibprotobuf
}
}
# For building in a single folder
CONFIG(debug, debug|release) {
CONFIG(WITH_CUDA){
DESTDIR = debug_cuda
}else{
DESTDIR = debug
}
OBJECTS_DIR = .obj_debug
MOC_DIR = .moc_debug
}else {
DEFINES += QT_NO_DEBUG_OUTPUT
CONFIG(WITH_CUDA){
message("Building with CUDA")
DESTDIR = release_cuda
}else{
DESTDIR = release
}
OBJECTS_DIR = .obj
MOC_DIR = .moc
}
INCLUDEPATH += "$$_PRO_FILE_PWD_/src"
VPATH = "$$_PRO_FILE_PWD_/src"
SOURCES += \
src/crc32.cpp \
src/main.cpp \
src/mainwindow.cpp \
src/labelproject.cpp \
src/imagelabel.cpp \
src/kittiexporter.cpp \
src/darknetexporter.cpp \
src/exportdialog.cpp \
src/multitracker.cpp \
src/baseexporter.cpp \
src/baseimporter.cpp \
src/birdsaiimporter.cpp \
src/cliparser.cpp \
src/cocoexporter.cpp \
src/darknetimporter.cpp \
src/pascalvocimporter.cpp \
src/proto/example.pb.cc \
src/proto/feature.pb.cc \
src/gcpexporter.cpp \
src/imagedisplay.cpp \
src/importdialog.cpp \
src/motimporter.cpp \
src/pascalvocexporter.cpp \
src/detection/detectoropencv.cpp \
src/detection/detectorsetupdialog.cpp \
src/refinerangedialog.cpp \
src/cocoimporter.cpp \
src/tfrecordexporter.cpp \
src/tfrecordimporter.cpp \
src/videoexporter.cpp
HEADERS += \
src/cliprogressbar.h \
src/crc32.h \
src/importer.h \
src/mainwindow.h \
src/labelproject.h \
src/imagelabel.h \
src/boundingbox.h \
src/kittiexporter.h \
src/darknetexporter.h \
src/exportdialog.h \
src/multitracker.h \
src/baseexporter.h \
src/baseimporter.h \
src/birdsaiimporter.h \
src/cliparser.h \
src/cocoexporter.h \
src/darknetimporter.h \
src/exporter.h \
src/gcpexporter.h \
src/imagedisplay.h \
src/importdialog.h \
src/motimporter.h \
src/pascalvocexporter.h \
src/detection/detectoropencv.h \
src/detection/detectorsetupdialog.h \
src/pascalvocimporter.h \
src/proto/example.pb.h \
src/proto/feature.pb.h \
src/refinerangedialog.h \
src/cocoimporter.h \
src/tfrecordexporter.h \
src/tfrecordimporter.h \
src/videoexporter.h
FORMS += \
src/importdialog.ui \
src/mainwindow.ui \
src/exportdialog.ui \
src/imagedisplay.ui \
detection/detectorsetupdialog.ui \
src/refinerangedialog.ui
include(QtAwesome/QtAwesome/QtAwesome.pri)
# Deploy apps in OS X and Windows
isEmpty(TARGET_EXT) {
win32 {
TARGET_CUSTOM_EXT = .exe
}
macx {
TARGET_CUSTOM_EXT = .app
}
} else {
TARGET_CUSTOM_EXT = $${TARGET_EXT}
}
CONFIG( debug, debug|release ) {
# debug
DEPLOY_TARGET = $$shell_quote($$shell_path($${OUT_PWD}/debug/$${TARGET}$${TARGET_CUSTOM_EXT}))
} else {
# release
DEPLOY_TARGET = $$shell_quote($$shell_path($${OUT_PWD}/release/$${TARGET}$${TARGET_CUSTOM_EXT}))
}
win32 {
DEPLOY_COMMAND = windeployqt.exe
}
macx {
DEPLOY_COMMAND = macdeployqt
}
# # Uncomment the following line to help debug the deploy command when running qmake
# warning($${DEPLOY_COMMAND} $${DEPLOY_TARGET})
# Use += instead of = if you use multiple QMAKE_POST_LINKs
win32 {
QMAKE_POST_LINK = $${DEPLOY_COMMAND} $${DEPLOY_TARGET}
}
macx {
QMAKE_POST_LINK = $${DEPLOY_COMMAND} $${DEPLOY_TARGET}
}
================================================
FILE: README.md
================================================
# DeepLabel
[](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_osx.yml) [](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_ubuntu.yml)[](https://github.com/jveitchmichaelis/deeplabel/actions/workflows/build_windows.yml)
DeepLabel 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.
DeepLabel 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.
Deeplabel 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.
**If you use DeepLabel for research or commercial purposes, please cite here!** [](https://zenodo.org/badge/latestdoi/105791274)
Download 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.
**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.**
Ready 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.

## Workflow
DeepLabel 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.
A typical workflow for DeepLabel is:
1. Create a new project database
2. Add images, or import an existing project in a variety of common ML formats
3. Load in a class list, or manually add classes
4. Label/inspect the images
5. Export data in the desired format
## Command Line Interface
Deeplabel now has a convenient command line interface to facilitate import and export of data:
```
(base) PS C:\Users\Josh> deeplabel.exe -h
Usage: deeplabel.exe [options] mode
Options:
-?, -h, --help Displays help on
commandline options.
--help-all Displays help including Qt
specific options.
-v, --version Displays version
information.
-f, --format <[kitti, darknet, gcp, voc, coco, mot export format
, birdsai, tfrecord]>
-o, --output <folder path> output folder
-i, --input <file path> label database
-s, --split <percentage> validation split
percentage
--no-subfolders export directly to
specified folder
--prefix <prefix> filename prefix
-n, --names <file path> names file
--bucket GCP bucket
--local use local paths for GCP
export
--export-map export label.pbtxt file
--shuffle shuffle images when
splitting
--append-labels append to label files
--export-unlabelled export images without
labels
--images <images> import image path/folder
--annotations <annotations> import annotation
path/folder
--import-unlabelled import images without
labels
--overwrite overwrite existing
databases
--records <images> mask for TF Records (*
wildcard)
Arguments:
mode [export, import]
```
For example, if you want to export a dataset to TFRecord:
```
deeplabel.exe export -i labels.lbdlb -f TFRecord -n project.names -o ./output/
```
## Data import
Currently you can import data in the following formats:
* Darknet (provide image list and names)
* COCO (provide an annotation .json file and image folder)
* MOT
* TFRecord (parsing works, but full import is not possible yet)
* Pascal VOC
## Data export
Currently you can export in:
* KITTI (e.g. for Nvidia DIGITS)
* Darknet for YOLO
* Pascal VOC
* COCO (experimental)
* Google Cloud Platform (e.g. for AutoML)
* TFRecord (for the Tensorflow Object Detection library)
* 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++.
* Video (experimental, command line only)
Deeplabel 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.
When 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:
```
{
item {
name: some_class
id: 1
displayname: some_class
}
```
DeepLabel 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).
Since 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.
## Model inference (automatic tagging)
DeepLabel 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.
### Supported models
You can load in a Darknet-type model, for example Yolov3, Yolov3-spp and Yolov3-Tiny or a Tensorflow model.
Most 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`.
You need to provide:
* The model configuration file (`.cfg, .pbtxt`)
* The model weights (`.weights, .pb`)
* A names file (`.names`)
The extension doesn't really matter, but these are the standard ones.
**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.**
You 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.
If 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.
Download Tensorflow models for testing from OpenCV [here](https://github.com/opencv/opencv/wiki/TensorFlow-Object-Detection-API).
### Target
You 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).
OpenCL 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
## 16-bit File Handling
Deeplabel supports 16-bit images and will automatically scale the colours to 8-bit grayscale.
You can optionally apply a colourmap to a single channel image, which may improve contrast.
In the future, support should be added for OpenCV's Tensorflow backend.
## Bounding box tracking and refinement
DeepLabel 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.
DeepLabel 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.
An experimental foreground/background segmenter is being tested, but it seems to be a bit overenthusiastic right now.
## Video support
DeepLabel 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.
Any video format that OpenCV (and its backends) can open should work.
Usage
--
Using 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.
First, add the classes that you want.
DeepLabel 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.
If 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.
All changes are immediately reflected in the database.
**Navigate** through images using the left/right or a/d keys. You can use ctrl+left/right to quickly advance through your dataset.
You 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.
Once 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.
Building from source
--
It's recommended that you use Qt5, but Qt4 will probably work. You need to have Qt's SQL extensions installed.
**OpenCV Requirements**
This 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.
You 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.
``` bash
git clone https://github.com/opencv/opencv
git clone https://github.com/opencv/opencv_contrib
cd opencv && git checkout 4.5.3 && cd ../
cd opencv_contrib && git checkout 4.5.3 && cd ../
cd opencv
mkdir build && cd build
cmake .. -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules
make -j8
sudo make install
```
On Mac, Homebrew automatically include pkg-config support and the contrib packages.
**Linux**
[See Github Action for representative build instructions.](https://github.com/jveitchmichaelis/deeplabel/blob/master/.github/workflows/build_ubuntu.yml)
Build opencv using your preferred method (e.g. above). You need Qt5 installed - not just Qt Creator.
```
sudo apt install git build-essential qt5-default libprotobuf-dev protobuf-compiler
```
Proto files are already compiled within the `src/proto` folder, but if you need to, you can compile with:
```
protoc --proto_path ./src/proto --cpp_out ./src/proto feature.proto example.proto
```
Clone the repository, then:
```bash
git submodule update --init --recursive
qmake -makefile -o Makefile DeepLabel.pro
make -j4
```
**Mac**
Install dependencies using Homebrew:
``` bash
brew install qt opencv protobuf
```
Note 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.
Clone the repo, open the pro file in Qt Creator and build. Deployment is automatic on Windows and OS X. Alternatively:
```bash
git submodule update --init --recursive
qmake -makefile -o Makefile DeepLabel.pro
make -j4
```
`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.
An extra script is provided to fix paths to certain libraries on OS X.
**Windows**
Unfortunately 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).
You will also need protobuf installed - make sure you compile with DLLs enabled, not static libraries.
Once you've installed OpenCV...(!)
Clone 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.
Notes
--
#### Overlapping bounding boxes
DeepLabel 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.
#### Image paths
Image 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.
#### Support for other descriptors (e.g. occluded, truncated)
In 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.
#### Database schema
DeepLabel uses a simple relational database with the following schema:
CREATE TABLE classes (class_id INTEGER PRIMARY KEY ASC, name varchar(32))
CREATE TABLE images (image_id INTEGER PRIMARY KEY ASC, path varchar(256))
CREATE TABLE labels (label_id INTEGER PRIMARY KEY ASC, image_id int, class_id int, x int, y int, width int, height int)
These 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.
#### License
This 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.
================================================
FILE: deploy_mac.sh
================================================
#!/usr/bin/env bash
build_folder=$1
app_path=$build_folder/DeepLabel.app
macdeployqt $app_path
python fix_paths_mac.py $app_path/Contents/MacOS/DeepLabel
hdiutil create -volname DeepLabel -srcfolder $app_path -ov -format UDZO $build_folder/deeplabel.dmg
================================================
FILE: fix_paths_mac.py
================================================
import subprocess
import os
import sys
from shutil import copyfile
executable = sys.argv[1]
app_folder = os.path.join(*executable.split('/')[:-3])
content_folder = os.path.join(app_folder, "Contents")
framework_path = os.path.join(content_folder, "Frameworks")
print(executable)
print("Working in {} ".format(app_folder))
def file_in_folder(file, folder):
return os.path.exists(os.path.join(folder, file))
def otool(s):
o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE)
for l in o.stdout:
l = l.decode()
if l[0] == '\t':
path = l.split(' ', 1)[0][1:]
if "@executable_path" in path:
path = path.replace("@executable_path", "")
path = os.path.join(content_folder, path[4:])
if "@loader_path" in path:
path = path.replace("@loader_path", framework_path)
if "@rpath" in path:
path = path.replace("@rpath", framework_path)
dependency_dylib_name = os.path.split(path)[-1]
if "usr/local" in path:
if app_folder in s:
print("Warning: {} depends on {}".format(s, path))
if file_in_folder(dependency_dylib_name, framework_path):
print("Dependent library {} is already in framework folder".format(dependency_dylib_name))
print("Running install name tool to fix {}.".format(s))
if dependency_dylib_name == os.path.split(s)[-1]:
_ = subprocess.Popen(['install_name_tool', '-id', os.path.join("@loader_path", dependency_dylib_name), s], stdout=subprocess.PIPE)
_ = subprocess.Popen(['install_name_tool', '-change', path, os.path.join("@loader_path", dependency_dylib_name), s], stdout=subprocess.PIPE)
else:
pass
yield path
need = set([executable])
done = set()
while need:
needed = set(need)
need = set()
for f in needed:
need.update(otool(f))
done.update(needed)
need.difference_update(done)
================================================
FILE: src/baseexporter.cpp
================================================
#include "baseexporter.h"
BaseExporter::BaseExporter(LabelProject *project, QObject *parent) : QObject(parent)
{
this->project = project;
}
void BaseExporter::disableProgress(bool disable){
disable_progress = disable;
}
void BaseExporter::setValidationSplit(bool split){
validation_split = split;
}
void BaseExporter::splitData(float split, bool shuffle, int seed){
if(split < 0 || split > 1){
qCritical() << "Invalid split fraction, should be [0,1]";
}
if(shuffle){
std::random_device rd;
std::mt19937 generator(rd());
generator.seed(static_cast<unsigned int>(seed));
std::shuffle(images.begin(), images.end(), generator);
}
int pivot = static_cast<int>(images.size() * split);
validation_set = images.mid(0, pivot);
train_set = images.mid(pivot);
qDebug() << "Split: " << split;
if(split > 0){
qInfo() << train_set.size() << " images selected for train set.";
qInfo() << validation_set.size() << " images selected for validation set.";
}else{
qInfo() << train_set.size() << " images selected for output.";
}
}
void BaseExporter::setExportUnlabelled(bool res){
export_unlabelled = res;
bool relative_path = true;
if(export_unlabelled){
project->getImageList(images, relative_path);
}else{
project->getLabelledImageList(images, relative_path);
}
qDebug() << "Selected " << images.size() << " images";
}
bool BaseExporter::setOutputFolder(const QString folder, bool no_subfolders){
if(folder == "") return false;
output_folder = folder;
//Make output folder if it doesn't exist
if (!QDir(output_folder).exists()){
qInfo() << "Making output folder" << output_folder;
QDir().mkpath(output_folder);
}
if(!no_subfolders){
//Make the training and validation folders
train_folder = QDir::cleanPath(output_folder+"/train");
if (!QDir(train_folder).exists()){
qInfo() << "Making training folder" << train_folder;
QDir().mkpath(train_folder);
}
if(validation_split){
val_folder = QDir::cleanPath(output_folder+"/val");
if (!QDir(val_folder).exists()){
qInfo() << "Making validation folder" << val_folder;
QDir().mkpath(val_folder);
}
val_label_folder = QDir::cleanPath(val_folder);
val_image_folder = QDir::cleanPath(val_folder);
}
train_label_folder = QDir::cleanPath(train_folder);
train_image_folder = QDir::cleanPath(train_folder);
}else{
train_folder = QDir::cleanPath(output_folder);
val_folder = QDir::cleanPath(output_folder);
val_label_folder = QDir::cleanPath(output_folder);
val_image_folder = QDir::cleanPath(output_folder);
train_label_folder = QDir::cleanPath(output_folder);
train_image_folder = QDir::cleanPath(output_folder);
qDebug() << "Output all to" << train_image_folder;
}
return true;
}
void BaseExporter::setFilenamePrefix(QString prefix){
if(prefix != ""){
filename_prefix = prefix;
}
}
bool BaseExporter::saveImage(cv::Mat &image, const QString output, const double scale_x, const double scale_y){
if(image.rows == 0 || image.cols == 0){
qCritical() << "Empty image ";
return false;
}
if(scale_x > 0 && scale_y > 0)
cv::resize(image, image, cv::Size(), scale_x, scale_y);
std::vector<int> compression_params;
// Png compression - maximum is super slow
// TODO: add support to adjust this
if(output.split(".").last().toLower() == "png"){
compression_params.push_back(cv::IMWRITE_PNG_COMPRESSION);
compression_params.push_back(6);
}
return cv::imwrite(output.toStdString(), image, compression_params);
}
================================================
FILE: src/baseexporter.h
================================================
#ifndef BASEEXPORTER_H
#define BASEEXPORTER_H
#include <QObject>
#include <QDateTime>
#include <QImageReader>
#include <QFileDialog>
#include <opencv2/opencv.hpp>
#include <random>
#include <cliprogressbar.h>
#include <labelproject.h>
#include <boundingbox.h>
enum export_image_type{
EXPORT_UNASSIGNED,
EXPORT_TRAIN,
EXPORT_VAL,
EXPORT_TEST
};
class BaseExporter : public QObject
{
Q_OBJECT
public:
explicit BaseExporter(LabelProject *project, QObject *parent = nullptr);
void disableProgress(bool disable);
signals:
void export_progress(int);
public slots:
void splitData(float split=1, bool shuffle=false, int seed=42);
bool setOutputFolder(QString folder, bool no_subfolders=false);
void setExportUnlabelled(bool res);
void setAppendLabels(bool res){append_labels = res;}
void setValidationSplit(bool split);
void setFilenamePrefix(QString prefix);
virtual void process() = 0;
protected:
LabelProject *project;
QList<QString> train_set;
QList<QString> validation_set;
QList<QString> images;
QString train_folder;
QString train_label_folder;
QString train_image_folder;
QString val_folder;
QString val_label_folder;
QString val_image_folder;
QString output_folder;
QString filename_prefix = "";
std::map<QString, int> id_map;
int image_id;
int label_id;
bool disable_progress = false;
bool append_labels = false;
bool validation_split = false;
bool export_unlabelled = false;
bool saveImage(cv::Mat &image, const QString output, const double scale_x = -1.0, const double scale_y = -1.0);
};
#endif // BASEEXPORTER_H
================================================
FILE: src/baseimporter.cpp
================================================
#include "baseimporter.h"
BaseImporter::BaseImporter(QObject *parent) : QObject(parent)
{
}
void BaseImporter::setImportUnlabelled(bool import){
import_unlabelled = import;
}
void BaseImporter::setRootFolder(QString folder){
label_root = folder;
}
void BaseImporter::addAsset(QString image_path, QList<BoundingBox> boxes){
if(boxes.size() == 0 && !import_unlabelled)
return;
if(project->addAsset(image_path)){
for(auto &box : boxes){
project->addLabel(image_path, box);
}
}
}
QList<QString> BaseImporter::readLines(QString path){
QFile file(path);
QList<QString> lines = {};
if (file.open(QFile::ReadOnly)) {
while (!file.atEnd()){
auto line = QString(file.readLine()).simplified();
lines.append(line);
}
}
return lines;
}
================================================
FILE: src/baseimporter.h
================================================
#ifndef BASEIMPORTER_H
#define BASEIMPORTER_H
#include <QObject>
#include <QFile>
#include <QRegularExpression>
#include <labelproject.h>
#include <boundingbox.h>
#include <cliprogressbar.h>
class BaseImporter : public QObject
{
Q_OBJECT
public:
explicit BaseImporter(QObject *parent = nullptr);
void import(){};
void setRootFolder(QString folder);
void setImportUnlabelled(bool import);
signals:
protected:
bool import_unlabelled = false;
QString label_root;
LabelProject *project;
void addAsset(QString image_path, QList<BoundingBox> boxes);
QList<QString> readLines(QString path);
public slots:
};
#endif // BASEIMPORTER_H
================================================
FILE: src/birdsaiimporter.cpp
================================================
#include "birdsaiimporter.h"
void BirdsAIImporter::import(QString sequence_folder, QString annotation_folder){
QDir seq_dir(sequence_folder);
// Find all sequence folders
QDirIterator it(sequence_folder, QDir::Dirs);
QList<QString> subfolders;
while (it.hasNext()) {
auto subfolder = QDir(it.next()).canonicalPath();
subfolders.append(subfolder);
}
if(subfolders.size() == 0){
qWarning() << "Couldn't find any sequences in " << sequence_folder;
return;
}
subfolders.removeDuplicates();
subfolders.sort();
for(auto &subfolder : subfolders){
qInfo() << "Checking: " << subfolder;
auto sequence_name = QFileInfo(subfolder).baseName();
auto annotation_dir = QDir(annotation_folder);
QString annotation_file = annotation_dir
.absoluteFilePath(QString("%1.csv")
.arg(sequence_name));
qDebug() << "Looking for: " << annotation_file;
if(annotation_dir.exists(annotation_file)){
// Find images:
auto labels = getLabels(annotation_file);
QList<QList<BoundingBox>> label_list;
QList<QString> image_list;
qInfo() << "Adding annotations from: " << annotation_file;
for(auto &image : QDir(subfolder).entryList(QDir::Files)){
qDebug() << "Adding labels for" << image;
// Extract image ID, ignoring leading zeros
auto split_file = QFileInfo(image).baseName().split("_");
int image_id = split_file.back().toInt();
// Get boxes for this ID and add to DB
auto boxes = findBoxes(labels, image_id);
QString abs_image_path = QDir(subfolder).absoluteFilePath(image);
if(boxes.empty() && !import_unlabelled){
continue;
}
label_list.append(boxes);
image_list.append(abs_image_path);
}
project->addLabelledAssets(image_list, label_list);
}else{
qWarning() << "Failed to find annotation file: " << annotation_file;
}
}
}
QList<BoundingBox> BirdsAIImporter::findBoxes(QVector<QStringList> labels, int id){
QList<BoundingBox> boxes = {};
// <frame>, <id>, <bb_left>, <bb_top>, <bb_width>, <bb_height>, <class>, <species>, etc
for(auto &label : labels){
if(label.at(0).toInt() != id){
continue;
}
BoundingBox bbox;
/* https://sites.google.com/view/elizabethbondi/dataset
* -1: unknown,
* 0: human,
* 1: elephant,
* 2: lion,
* 3: giraffe,
* 4: dog,
* 5: crocodile,
* 6: hippo,
* 7: zebra,
* 8: rhino
*/
int label_species = label.at(7).toInt();
bbox.classid = label_species + 2; // Since in the database they're 1-indexed
bbox.classname = project->getClassName(bbox.classid);
if(bbox.classname == ""){
qWarning() << "Class ID " << bbox.classid << " not found in names file.";
}
auto top_left = QPoint(label.at(2).toDouble(), label.at(3).toDouble());
auto bottom_right = top_left + QPoint(label.at(4).toDouble(), label.at(5).toDouble());
bbox.rect = QRect(top_left, bottom_right);
boxes.append(bbox);
}
return boxes;
}
================================================
FILE: src/birdsaiimporter.h
================================================
#ifndef BIRDSAIIMPORTER_H
#define BIRDSAIIMPORTER_H
#include "motimporter.h"
class BirdsAIImporter : public MOTImporter
{
public:
explicit BirdsAIImporter(LabelProject *project, QObject *parent = nullptr) : MOTImporter(project, parent){
this->project = project;
}
void import(QString sequence_folder, QString annotation_folder);
private:
QList<BoundingBox> findBoxes(QVector<QStringList> labels, int id);
};
#endif // BIRDSAIIMPORTER_H
================================================
FILE: src/boundingbox.h
================================================
#ifndef BOUNDINGBOX_H
#define BOUNDINGBOX_H
#include <QRect>
#include <QString>
enum SelectedEdge {
EDGE_LEFT,
EDGE_TOP,
EDGE_RIGHT,
EDGE_BOTTOM,
EDGE_NONE,
};
enum SelectedCorner {
CORNER_TOPLEFT,
CORNER_TOPRIGHT,
CORNER_BOTTOMLEFT,
CORNER_BOTTOMRIGHT,
CORNER_NONE,
};
struct BoundingBox{
QRect rect = QRect(0,0,0,0);
QString classname = "";
int occluded = 0;
bool truncated = false;
int classid = 0;
double confidence = 0;
// Drawing helpers
bool is_selected = false;
SelectedEdge selected_edge = EDGE_NONE;
SelectedCorner selected_corner = CORNER_NONE;
// ID
int label_id = -1;
};
inline QString printBoundingBox(BoundingBox box){
return QString("%1, xy(%2, %3) w: %4 h: %5")
.arg(box.classname)
.arg(box.rect.left())
.arg(box.rect.top())
.arg(box.rect.width())
.arg(box.rect.height());
}
#endif // BOUNDINGBOX_H
================================================
FILE: src/cliparser.cpp
================================================
#include "cliparser.h"
CliParser::CliParser(QObject *parent) : QObject(parent)
{
SetupOptions();
}
void CliParser::SetupOptions(){
exportFormatOption = new QCommandLineOption({"f", "format"}, "export format", "kitti, darknet, gcp, voc, coco, mot, birdsai, tfrecord");
exportOutputFolder = new QCommandLineOption({"o", "output"}, "output folder", "folder path");
exportInputFile = new QCommandLineOption({"i", "input"}, "label database", "file path");
exportValidationSplit = new QCommandLineOption({"s", "split"}, "validation split percentage", "percentage", "20");
exportNoSubfolders = new QCommandLineOption("no-subfolders", "export directly to specified folder");
exportFilePrefix = new QCommandLineOption("prefix", "filename prefix", "prefix", "");
exportNamesFile = new QCommandLineOption({"n", "names"}, "names file", "file path");
exportGCPBucket = new QCommandLineOption("bucket", "GCP bucket");
exportGCPLocal = new QCommandLineOption("local", "use local paths for GCP export");
exportPascalVOCLabelMap = new QCommandLineOption("export-map", "export label.pbtxt file");
exportShuffleImages = new QCommandLineOption("shuffle", "shuffle images when splitting");
exportAppendLabels = new QCommandLineOption("append-labels", "append to label files");
exportUnlabelledImages = new QCommandLineOption("export-unlabelled", "export images without labels");
exportVideoFilename = new QCommandLineOption("video-filename", "video output filename", "filename", "out.mp4");
exportVideoFourcc = new QCommandLineOption("fourcc", "video codec fourcc", "codec", "h264");
exportVideoFps = new QCommandLineOption("fps", "video framerate", "fps", "10");
exportVideoColourmap = new QCommandLineOption("colourmap", "video colourmap", "colourmap", "Inferno");
exportVideoSize = new QCommandLineOption("videosize", "video size: width, height", "w,h", "1280,720");
exportVideoDisplayBoxes = new QCommandLineOption("display-boxes", "display boxes in output", "on, off", "on");
exportVideoDisplayNames = new QCommandLineOption("display-names", "display class names", "on, off", "on");
importImages = new QCommandLineOption("images", "import image path/folder", "images");
importRelativePath = new QCommandLineOption("relative-path",
"relative file path (for darknet)",
"relative");
importTFRecordMask = new QCommandLineOption("records", "mask for TF Records (* wildcard)", "images");
importAnnotations = new QCommandLineOption("annotations", "import annotation path/folder", "annotations");
importUnlabelledImages = new QCommandLineOption("import-unlabelled", "import images without labels");
importOverwrite = new QCommandLineOption("overwrite", "overwrite existing databases");
detectChannels = new QCommandLineOption("detect-channels",
"number of channels",
"detect-channels",
"3");
detectTarget = new QCommandLineOption("detect-target", "Detection target", "CPU, CUDA", "CPU");
detectFramework = new QCommandLineOption("detect-framework", "Detection framework", "detect-framework", "Darknet");
detectConvertDepth = new QCommandLineOption("convert-depth", "Convert 16-bit to 8-bit");
detectGrayToRGB = new QCommandLineOption("convert-gray", "Convert RGB to grayscale");
detectNMSThresh = new QCommandLineOption("nms-thresh", "NMS Threshold", "threshold", "0.7");
detectConfThresh = new QCommandLineOption("conf-thresh", "Confidence Threshold", "threshold", "0.5");
detectConfig = new QCommandLineOption("config", "Path to model config file", "path");
detectWeights = new QCommandLineOption("weights", "Path to model weights file", "path");
configSilence = new QCommandLineOption({"q", "quiet"}, "no log messages");
parser.addHelpOption();
parser.addVersionOption();
parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions);
parser.addPositionalArgument("mode", "[export, import]");
parser.addOption(*exportFormatOption);
parser.addOption(*exportOutputFolder);
parser.addOption(*exportInputFile);
parser.addOption(*exportValidationSplit);
parser.addOption(*exportNoSubfolders);
parser.addOption(*exportFilePrefix);
parser.addOption(*exportNamesFile);
parser.addOption(*exportGCPBucket);
parser.addOption(*exportGCPLocal);
parser.addOption(*exportPascalVOCLabelMap);
parser.addOption(*exportShuffleImages);
parser.addOption(*exportAppendLabels);
parser.addOption(*exportVideoFourcc);
parser.addOption(*exportVideoFps);
parser.addOption(*exportVideoFilename);
parser.addOption(*exportVideoColourmap);
parser.addOption(*exportVideoSize);
parser.addOption(*exportVideoDisplayBoxes);
parser.addOption(*exportVideoDisplayNames);
parser.addOption(*exportUnlabelledImages);
parser.addOption(*importImages);
parser.addOption(*importAnnotations);
parser.addOption(*importRelativePath);
parser.addOption(*importUnlabelledImages);
parser.addOption(*importOverwrite);
parser.addOption(*importTFRecordMask);
parser.addOption(*detectChannels);
parser.addOption(*detectTarget);
parser.addOption(*detectFramework);
parser.addOption(*detectConvertDepth);
parser.addOption(*detectGrayToRGB);
parser.addOption(*detectNMSThresh);
parser.addOption(*detectConfThresh);
parser.addOption(*detectConfig);
parser.addOption(*detectWeights);
parser.addOption(*configSilence);
}
bool CliParser::Run(){
parser.process(*QCoreApplication::instance());
bool res = false;
auto mode = parser.positionalArguments().at(0);
for(auto &option : parser.unknownOptionNames()){
qCritical() << "Unknown option: " << option;
}
if(parser.isSet(*configSilence)){
qSetMessagePattern("");
}
if(mode == "export"){
res = handleExport();
}else if(mode == "import"){
res = handleImport();
}else if(mode == "merge"){
res = handleMerge();
}else if(mode == "detect"){
res = handleDetect();
}
return res;
}
bool CliParser::handleDetect(){
DetectorOpenCV detector;
qInfo() << "Using weights: " << parser.value("weights");
qInfo() << "Using config: " << parser.value("config");
detector.setChannels(parser.value("detect-channels").toInt());
detector.setTarget(parser.value("detect-target"));
detector.setFramework(parser.value("detect-framework"));
detector.setConvertGrayscale(parser.isSet("convert-gray"));
detector.setConvertDepth(parser.isSet("convert-depth"));
detector.setNMSThreshold(parser.value("nms-thresh").toDouble());
detector.setConfidenceThreshold(parser.value("conf-thresh").toDouble());
detector.loadNetwork(parser.value("names").toStdString(),
parser.value("config").toStdString(),
parser.value("weights").toStdString());
LabelProject project;
project.loadDatabase(parser.value("input"));
detector.runOnProject(&project);
return true;
}
bool CliParser::handleMerge(){
qCritical() << "Not implemented yet";
return false;
}
bool CliParser::handleImport(){
QString database = parser.value("input");
LabelProject project;
if(QFileInfo(database).exists()){
if(!parser.isSet("overwrite")){
qCritical() << "Database exists, will not ovewrite.";
return false;
}
}else{
project.createDatabase(database);
}
project.loadDatabase(database);
QThread* import_thread = new QThread;
bool import_unlabelled = parser.isSet(*importUnlabelledImages);
if(parser.value("format") == "darknet"){
if(!QFileInfo(parser.value("images")).exists()){
qCritical() << "Image list doesn't exist";
return false;
}
if(!QFileInfo(parser.value("names")).exists()){
qCritical() << "Names file doesn't exist";
return false;
}
DarknetImporter importer(&project);
importer.moveToThread(import_thread);
importer.setImportUnlabelled(import_unlabelled);
if (parser.isSet("relative-path")) {
importer.import(parser.value("images"),
parser.value("names"),
parser.value("relative-path"));
} else {
importer.import(parser.value("images"), parser.value("names"));
}
}else if(parser.value("format") == "coco"){
if(!QFileInfo(parser.value("annotations")).exists()){
qCritical() << "Image sequence folder doesn't exist";
return false;
}
CocoImporter importer(&project);
importer.moveToThread(import_thread);
importer.setImportUnlabelled(import_unlabelled);
importer.import(parser.value("annotations"), parser.value("images"));
}else if(parser.value("format") == "mot"){
if(!QDir(parser.value("images")).exists()){
qCritical() << "Image sequence folder doesn't exist";
return false;
}
if(!QFileInfo(parser.value("names")).exists()){
qCritical() << "Names file doesn't exist";
return false;
}
MOTImporter importer(&project);
importer.moveToThread(import_thread);
importer.setImportUnlabelled(import_unlabelled);
importer.loadClasses(parser.value("names"));
importer.import(parser.value("images"));
}else if(parser.value("format") == "birdsai"){
if(!QDir(parser.value("annotations")).exists()){
qCritical() << "Annotations folder doesn't exist";
return false;
}
if(!QDir(parser.value("images")).exists()){
qCritical() << "Image sequence folder doesn't exist";
return false;
}
BirdsAIImporter importer(&project);
importer.moveToThread(import_thread);
importer.setImportUnlabelled(import_unlabelled);
importer.loadClasses(parser.value("names"));
importer.import(parser.value("images"),
parser.value("annotations"));
}else if(parser.value("format") == "tfrecord"){
TFRecordImporter importer(&project);
importer.moveToThread(import_thread);
importer.setImportUnlabelled(import_unlabelled);
importer.import_records(parser.value("records"));
}else if(parser.value("format") == "pascalvoc"){
PascalVOCImporter importer(&project);
importer.moveToThread(import_thread);
importer.setImportUnlabelled(import_unlabelled);
importer.import(parser.value("images"), parser.value("annotations"));
}
return true;
}
bool CliParser::handleExport(){
QThread* export_thread = new QThread;
BaseExporter* exporter = nullptr;
LabelProject project;
QString database = parser.value("input");
if(!QFileInfo(database).exists()){
qCritical() << "Database file does not exist.";
return false;
}
if(!parser.isSet("input") || parser.value("input") == ""){
qCritical() << "No input file specified";
return false;
}else{
qInfo() << "Attemting to load: " << parser.value("input");
project.loadDatabase(parser.value("input"));
}
if(parser.value(*exportFormatOption) == "kitti"){
exporter = new KittiExporter(&project);
}else if(parser.value(*exportFormatOption) == "darknet"){
exporter = new DarknetExporter(&project);
if(parser.isSet(*exportNamesFile)){
static_cast<DarknetExporter*>(exporter)->generateLabelIds(parser.value(*exportNamesFile));
}else{
qCritical() << "No names file specifed.";
return false;
}
}else if(parser.value(*exportFormatOption) == "voc"){
exporter = new PascalVocExporter(&project);
if(parser.isSet(*exportPascalVOCLabelMap)){
static_cast<PascalVocExporter*>(exporter)->setExportMap(true);
}
exporter->process();
}else if(parser.value(*exportFormatOption) == "coco"){
exporter = new CocoExporter(&project);
}else if(parser.value(*exportFormatOption) == "gcp"){
exporter = new GCPExporter(&project);
if(parser.isSet(*exportGCPLocal)){
bool local = true;
static_cast<GCPExporter*>(exporter)->setBucket("", local);
}else if(parser.isSet(*exportGCPBucket)){
static_cast<GCPExporter*>(exporter)->setBucket(parser.value(*exportGCPBucket));
}else{
qCritical() << "Using bucket and no path specifed.";
return false;
}
}else if(parser.value(*exportFormatOption) == "tfrecord"){
exporter = new TFRecordExporter(&project);
if(parser.isSet(*exportNamesFile)){
static_cast<TFRecordExporter*>(exporter)->generateLabelIds(parser.value(*exportNamesFile));
}else{
qCritical() << "No names file specifed.";
return false;
}
}else if(parser.value(*exportFormatOption) == "video"){
exporter = new VideoExporter(&project);
auto filename = parser.value(*exportVideoFilename);
auto fourcc = parser.value(*exportVideoFourcc);
bool ok = false;
auto fps = parser.value(*exportVideoFps).toDouble(&ok);
if(!ok){
qCritical() << "Invalid FPS, you specified: " << parser.value(*exportVideoFps);
return false;
}
// Check video size
auto videosize = parser.value(*exportVideoSize).split(",");
if(videosize.size() != 2){
qCritical() << "Video size should be two comma separated values: w,h";
return false;
}
auto width = parser.value(*exportVideoSize).split(",").at(0).toInt(&ok);
if(!ok){
qCritical() << "Invalid video width, you specified: " << parser.value(*exportVideoSize).split(",").at(0);
return false;
}
auto height = parser.value(*exportVideoSize).split(",").at(1).toInt(&ok);
if(!ok){
qCritical() << "Invalid video height, you specified: " << parser.value(*exportVideoSize).split(",").at(1);
return false;
}
auto colourmap = parser.value(*exportVideoColourmap);
static_cast<VideoExporter*>(exporter)->videoConfig(filename, fourcc, fps, {width, height}, colourmap);
static_cast<VideoExporter*>(exporter)->labelConfig(parser.value(*exportVideoDisplayNames) == "on",
parser.value(*exportVideoDisplayBoxes) == "on");
if(!parser.isSet(*exportOutputFolder) || parser.value(*exportOutputFolder) == ""){
qCritical() << "Please specify an output folder";
return false;
}else{
exporter->setOutputFolder(parser.value(*exportOutputFolder), true);
}
exporter->process();
return true;
}else{
qCritical() << "Invalid exporter type specified";
return false;
}
if(exporter != nullptr){
exporter->moveToThread(export_thread);
// No progress bar
exporter->disableProgress(true);
exporter->setExportUnlabelled(parser.isSet(*exportUnlabelledImages));
if(parser.isSet(*exportFilePrefix)){
exporter->setFilenamePrefix(parser.value(*exportFilePrefix));
}
exporter->setAppendLabels(parser.isSet(*exportAppendLabels));
if(parser.isSet(*exportValidationSplit)){
exporter->setValidationSplit(true);
exporter->splitData(parser.value(*exportValidationSplit).toFloat(), parser.isSet(*exportShuffleImages));
}else{
exporter->setValidationSplit(false);
exporter->splitData(0, parser.isSet(*exportShuffleImages));
}
if(!parser.isSet(*exportOutputFolder) || parser.value(*exportOutputFolder) == ""){
qCritical() << "Please specify an output folder";
return false;
}else{
exporter->setOutputFolder(parser.value(*exportOutputFolder), parser.isSet(*exportNoSubfolders));
}
exporter->process();
}else{
qFatal("Failed to instantiate exporter");
return false;
}
return true;
}
================================================
FILE: src/cliparser.h
================================================
#ifndef CLIPARSER_H
#define CLIPARSER_H
#include <QObject>
#include <QCommandLineParser>
#include <exporter.h>
#include <importer.h>
#include <detection/detectoropencv.h>
class CliParser : public QObject
{
Q_OBJECT
public:
explicit CliParser(QObject *parent = nullptr);
bool Run();
private:
void SetupOptions();
bool handleExport();
bool handleImport();
bool handleMerge();
bool handleDetect();
LabelProject getProject(bool &ok);
QCommandLineParser parser;
QCommandLineOption *exportFormatOption;
QCommandLineOption *exportOutputFolder;
QCommandLineOption *exportInputFile;
QCommandLineOption *exportValidationSplit;
QCommandLineOption *exportNoSubfolders;
QCommandLineOption *exportFilePrefix;
QCommandLineOption *exportNamesFile;
QCommandLineOption *exportGCPBucket;
QCommandLineOption *exportGCPLocal;
QCommandLineOption *exportPascalVOCLabelMap;
QCommandLineOption *exportShuffleImages;
QCommandLineOption *exportAppendLabels;
QCommandLineOption *exportUnlabelledImages;
QCommandLineOption *exportVideoFilename;
QCommandLineOption *exportVideoFourcc;
QCommandLineOption *exportVideoFps;
QCommandLineOption *exportVideoColourmap;
QCommandLineOption *exportVideoSize;
QCommandLineOption *exportVideoDisplayNames;
QCommandLineOption *exportVideoDisplayBoxes;
QCommandLineOption *importImages;
QCommandLineOption *importAnnotations;
QCommandLineOption *importUnlabelledImages;
QCommandLineOption *importOverwrite;
QCommandLineOption *importTFRecordMask;
QCommandLineOption *importRelativePath;
QCommandLineOption *detectChannels;
QCommandLineOption *detectTarget;
QCommandLineOption *detectFramework;
QCommandLineOption *detectConvertDepth;
QCommandLineOption *detectGrayToRGB;
QCommandLineOption *detectNMSThresh;
QCommandLineOption *detectConfThresh;
QCommandLineOption *detectConfig;
QCommandLineOption *detectWeights;
QCommandLineOption *configSilence;
signals:
};
#endif // CLIPARSER_H
================================================
FILE: src/cliprogressbar.h
================================================
#ifndef CLIPROGRESSBAR_H
#define CLIPROGRESSBAR_H
#include <iostream>
// Thanks to https://github.com/GregoryConrad/pBar
class cliProgressBar {
public:
void update(double newProgress) {
currentProgress = std::ceil(newProgress);
amountOfFiller = (int)((currentProgress / neededProgress)*(double)pBarLength);
}
void print() {
currUpdateVal %= pBarUpdater.length();
std::cout << "\r" //Bring cursor to start of line
<< firstPartOfpBar; //Print out first part of pBar
for (int a = 0; a < amountOfFiller; a++) { //Print out current progress
std::cout << pBarFiller;
}
std::cout << pBarUpdater[currUpdateVal];
for (int b = 0; b < pBarLength - amountOfFiller; b++) { //Print out spaces
std::cout << " ";
}
std::cout << lastPartOfpBar //Print out last part of progress bar
<< " (" << (int)(100*(currentProgress/neededProgress)) << "%)" //This just prints out the percent
<< std::flush;
currUpdateVal += 1;
}
std::string firstPartOfpBar = "[", //Change these at will (that is why I made them public)
lastPartOfpBar = "]",
pBarFiller = "|",
pBarUpdater = "/-\\|";
private:
int amountOfFiller,
pBarLength = 50, //I would recommend NOT changing this
currUpdateVal = 0; //Do not change
double currentProgress = 0, //Do not change
neededProgress = 100; //I would recommend NOT changing this
};
#endif // CLIPROGRESSBAR_H
================================================
FILE: src/cocoexporter.cpp
================================================
#include "cocoexporter.h"
bool CocoExporter::processImages(const QString folder, const QString label_filename, const QList<QString> images){
QString image_path;
QList<BoundingBox> labels;
auto now = QDateTime::currentDateTime();
QJsonObject label_file;
QJsonObject label_info;
label_info["year"] = now.date().year();
label_info["version"] = "1";
label_info["description"] = "Description";
label_info["contributor"] = "Contributor";
label_info["date_created"] = now.date().toString(Qt::ISODate);
label_info["url"] = "";
label_file["info"] = label_info;
QJsonArray annotations_array;
QJsonArray licenses_array;
QJsonArray image_array;
QJsonArray category_array;
QList<QString> classnames;
project->getClassList(classnames);
for(auto &classname : classnames){
QJsonObject category;
category["id"] = project->getClassId(classname);
category["name"] = classname;
category["supercategory"] = classname;
category_array.append(category);
}
QProgressDialog progress("...", "Abort", 0, images.size(), static_cast<QWidget*>(parent()));
progress.setWindowModality(Qt::WindowModal);
progress.setWindowTitle("Exporting images");
if(disable_progress){
progress.hide();
}
int i = 0;
foreach(image_path, images){
if(progress.wasCanceled())
break;
project->getLabels(image_path, labels);
if(!export_unlabelled && labels.size() == 0){
if(!disable_progress){
progress.setValue(i);
progress.setLabelText(QString("%1 is unlabelled").arg(image_path));
progress.repaint();
QApplication::processEvents();
}
continue;
}
QString abs_image_path = project->getDbFolder().absoluteFilePath(image_path);
QString extension = QFileInfo(image_path).suffix();
QString filename_noext = QFileInfo(image_path).completeBaseName();
QString image_filename = QString("%1/%2%3.%4")
.arg(folder)
.arg(filename_prefix)
.arg(filename_noext)
.arg(extension);
// Correct for duplicate file names in output
int dupe_file = 1;
while(QFile(image_filename).exists()){
image_filename = QString("%1/%2%3_%4.%5")
.arg(folder)
.arg(filename_prefix)
.arg(filename_noext)
.arg(dupe_file++)
.arg(extension);
}
cv::Mat image = cv::imread(abs_image_path.toStdString());
//saveImage(image, image_filename);
if(image.empty()){
qWarning() << "Failed top open image" << abs_image_path;
continue;
}
auto copied = QFile::copy(abs_image_path, image_filename);
if(!copied){
qWarning() << "Failed to copy image" << image_filename;
}
QJsonObject image_info;
image_info["id"] = image_id;
image_info["width"] = image.cols;
image_info["height"] = image.rows;
image_info["file_name"] = image_filename;
image_info["license"] = 0;
image_info["flickr_url"] = "";
image_info["coco_url"] = "";
image_info["date_captured"] = now.date().toString(Qt::ISODate);
image_array.append(image_info);
for(auto &label : labels){
QJsonObject annotation;
annotation["id"] = label_id;
annotation["image_id"] = image_id;
annotation["category_id"] = label.classid;
QJsonArray segmentation;
segmentation.append(QString::number(label.rect.topLeft().x()));
segmentation.append(QString::number(label.rect.topLeft().y()));
segmentation.append(QString::number(label.rect.topRight().x()));
segmentation.append(QString::number(label.rect.topRight().y()));
segmentation.append(QString::number(label.rect.bottomRight().x()));
segmentation.append(QString::number(label.rect.bottomRight().y()));
segmentation.append(QString::number(label.rect.bottomLeft().x()));
segmentation.append(QString::number(label.rect.bottomLeft().y()));
QJsonArray segmentation_array;
segmentation_array.append(segmentation);
annotation["segmentation"] = segmentation_array;
annotation["area"] = label.rect.width()*label.rect.height();
QJsonArray bbox;
bbox.append(QString::number(label.rect.x()));
bbox.append(QString::number(label.rect.y()));
bbox.append(QString::number(label.rect.width()));
bbox.append(QString::number(label.rect.height()));
annotation["bbox"] = bbox;
annotation["iscrowd"] = 0;
annotations_array.append(annotation);
label_id++;
}
image_id++;
if(!disable_progress){
progress.setValue(++i);
progress.setLabelText(image_path);
}
}
QJsonObject license;
license["id"] = 0;
license["name"] = "";
license["url"] = "";
licenses_array.append(license);
label_file["images"] = image_array;
label_file["annotations"] = annotations_array;
label_file["license"] = licenses_array;
label_file["categories"] = category_array;
QString label_filepath = QString("%1/%2.json").arg(output_folder).arg(label_filename);
QJsonDocument json_output(label_file);
QFile f(label_filepath);
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
if(f.isOpen()){
f.write(json_output.toJson());
}
f.close();
return true;
}
void CocoExporter::process(){
image_id = 0;
label_id = 0;
processImages(train_folder, "train", train_set);
processImages(val_folder, "val", validation_set);
}
================================================
FILE: src/cocoexporter.h
================================================
#ifndef COCOEXPORTER_H
#define COCOEXPORTER_H
#include <baseexporter.h>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
class CocoExporter : public BaseExporter
{
Q_OBJECT
public:
explicit CocoExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}
public slots:
void process();
private:
bool processImages(const QString folder, const QString filename, const QList<QString> images);
};
#endif // COCOEXPORTER_H
================================================
FILE: src/cocoimporter.cpp
================================================
#include "cocoimporter.h"
void CocoImporter::import(QString annotation_file, QString image_folder){
// Load Json
qDebug() << "Loading COCO JSON";
QFile loadFile(annotation_file);
loadFile.open(QIODevice::ReadOnly | QIODevice::Text);
QByteArray json_data = loadFile.readAll();
auto json = QJsonDocument::fromJson(json_data);
// Load classes (categories)
auto categories = json.object().value("categories");
if(categories == QJsonValue::Undefined){
qCritical() << "Categories not found";
return;
}
QMap<int, QString> classes;
QSet<QString> class_set;
QJsonValue item;
foreach(item, categories.toArray()){
auto item_name = item.toObject().value("name");
if(item_name == QJsonValue::Undefined || !item_name.isString())
continue;
auto item_id = item.toObject().value("id");
if(item_id == QJsonValue::Undefined || !item_id.isDouble())
continue;
classes.insert(item_id.toInt(), item_name.toString());
qDebug() << "added: " << item_id.toInt() << " " << item_name.toString();
class_set.insert(item_name.toString());
}
for(auto &cls : class_set){
project->addClass(cls);
}
// Load images
auto images = json.object().value("images");
if(images == QJsonValue::Undefined){
qCritical() << "No images found";
return;
}
QJsonValue image;
QList<QString> image_list;
QList<QList<BoundingBox>> label_list;
QMap<int, int> image_index;
QMap<int, QString> image_map;
int i=0;
foreach(image, images.toArray()){
auto file_name = image.toObject().value("file_name");
if(file_name == QJsonValue::Undefined || !file_name.isString())
continue;
auto file_id = image.toObject().value("id");
if(file_id == QJsonValue::Undefined || !file_id.isDouble())
continue;
auto abs_file_name = QDir(image_folder).absoluteFilePath(file_name.toString());
image_map.insert(file_id.toInt(), abs_file_name);
image_list.append(abs_file_name);
label_list.append(QList<BoundingBox> {});
image_index.insert(file_id.toInt(), i++);
}
// Load labels
auto annotations = json.object().value("annotations");
if(annotations == QJsonValue::Undefined){
qCritical() << "No annotations found";
return;
}
QJsonValue annotation;
foreach(annotation, annotations.toArray()){
auto image_id = annotation.toObject().value("image_id");
if(image_id == QJsonValue::Undefined || !image_id.isDouble())
continue;
auto category_id = annotation.toObject().value("category_id");
if(category_id == QJsonValue::Undefined || !category_id.isDouble())
continue;
auto image_filename = image_map[image_id.toInt()];
auto bbox = annotation.toObject().value("bbox");
if(bbox == QJsonValue::Undefined || !bbox.isArray()){
qWarning() << "No bounding box found for" << image_id;
continue;
}
auto bbox_array = bbox.toArray();
if(bbox_array.size() != 4)
continue;
int x = static_cast<int>(bbox_array.at(0).toDouble());
int y = static_cast<int>(bbox_array.at(1).toDouble());
int w = static_cast<int>(bbox_array.at(2).toDouble());
int h = static_cast<int>(bbox_array.at(3).toDouble());
if(w == 0){
qWarning() << "Bounding box for" << image_filename << "has zero width";
continue;
}
if(h == 0){
qWarning() << "Bounding box for" << image_filename << "has zero height";
continue;
}
BoundingBox new_bbox;
new_bbox.rect.setX(x);
new_bbox.rect.setY(y);
new_bbox.rect.setWidth(w);
new_bbox.rect.setHeight(h);
int cat_id = static_cast<int>(category_id.toDouble());
new_bbox.classname = classes[cat_id];
new_bbox.classid = project->getClassId(new_bbox.classname);
label_list[image_index[image_id.toInt()]].push_back(new_bbox);
}
project->addLabelledAssets(image_list, label_list);
return;
}
================================================
FILE: src/cocoimporter.h
================================================
#ifndef COCOIMPORTER_H
#define COCOIMPORTER_H
#include<baseimporter.h>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
class CocoImporter : public BaseImporter
{
public:
using BaseImporter::import;
explicit CocoImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){
this->project = project;
}
void import(QString annotations_file, QString image_folder);
};
#endif // COCOIMPORTER_H
================================================
FILE: src/crc32.cpp
================================================
#include "crc32.h"
#include <stdint.h>
namespace tf_crc{
static const uint32_t table0_[256] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c,
0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c,
0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc,
0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512,
0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad,
0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf,
0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f,
0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f,
0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e,
0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e,
0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de,
0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4,
0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b,
0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5,
0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975,
0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905,
0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8,
0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8,
0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78,
0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6,
0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69,
0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351};
static const uint32_t table1_[256] = {
0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, 0x4e8a61dc, 0x5d28f9ab,
0x69cf5132, 0x7a6dc945, 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21,
0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, 0x3fc5f181, 0x2c6769f6,
0x1880c16f, 0x0b225918, 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4,
0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, 0xec5b53e5, 0xfff9cb92,
0xcb1e630b, 0xd8bcfb7c, 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b,
0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, 0xe29f20ba, 0xf13db8cd,
0xc5da1054, 0xd6788823, 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff,
0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, 0x0ec4735f, 0x1d66eb28,
0x298143b1, 0x3a23dbc6, 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2,
0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, 0xff17c604, 0xecb55e73,
0xd852f6ea, 0xcbf06e9d, 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41,
0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, 0x2c896460, 0x3f2bfc17,
0x0bcc548e, 0x186eccf9, 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c,
0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, 0x5dc6f43d, 0x4e646c4a,
0x7a83c4d3, 0x69215ca4, 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78,
0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, 0xce1644da, 0xddb4dcad,
0xe9537434, 0xfaf1ec43, 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27,
0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, 0xbf59d487, 0xacfb4cf0,
0x981ce469, 0x8bbe7c1e, 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2,
0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, 0x6cc776e3, 0x7f65ee94,
0x4b82460d, 0x5820de7a, 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260,
0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, 0x66d73941, 0x7575a136,
0x419209af, 0x523091d8, 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004,
0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, 0x8a8c6aa4, 0x992ef2d3,
0xadc95a4a, 0xbe6bc23d, 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059,
0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, 0x844819fb, 0x97ea818c,
0xa30d2915, 0xb0afb162, 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be,
0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, 0x57d6bb9f, 0x447423e8,
0x70938b71, 0x63311306, 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3,
0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, 0x26992bc2, 0x353bb3b5,
0x01dc1b2c, 0x127e835b, 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287,
0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, 0x4a5e5d21, 0x59fcc556,
0x6d1b6dcf, 0x7eb9f5b8, 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc,
0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, 0x3b11cd7c, 0x28b3550b,
0x1c54fd92, 0x0ff665e5, 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439,
0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, 0xe88f6f18, 0xfb2df76f,
0xcfca5ff6, 0xdc68c781, 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766,
0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, 0xe64b1c47, 0xf5e98430,
0xc10e2ca9, 0xd2acb4de, 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502,
0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, 0x0a104fa2, 0x19b2d7d5,
0x2d557f4c, 0x3ef7e73b, 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f,
0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483};
static const uint32_t table2_[256] = {
0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, 0x9edea41a, 0x3b9f3664,
0xd1b1f617, 0x74f06469, 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6,
0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, 0x70a27d8a, 0xd5e3eff4,
0x3fcd2f87, 0x9a8cbdf9, 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3,
0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, 0xd62de755, 0x736c752b,
0x9942b558, 0x3c032726, 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67,
0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, 0xd915c5d1, 0x7c5457af,
0x967a97dc, 0x333b05a2, 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8,
0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, 0x0f382284, 0xaa79b0fa,
0x40577089, 0xe516e2f7, 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828,
0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, 0xc76580d9, 0x622412a7,
0x880ad2d4, 0x2d4b40aa, 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0,
0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, 0x61ea1a06, 0xc4ab8878,
0x2e85480b, 0x8bc4da75, 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20,
0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, 0x8f96c396, 0x2ad751e8,
0xc0f9919b, 0x65b803e5, 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff,
0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, 0xb8ffdfd7, 0x1dbe4da9,
0xf7908dda, 0x52d11fa4, 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b,
0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, 0x56830647, 0xf3c29439,
0x19ec544a, 0xbcadc634, 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e,
0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, 0xf00c9c98, 0x554d0ee6,
0xbf63ce95, 0x1a225ceb, 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730,
0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, 0xb3764986, 0x1637dbf8,
0xfc191b8b, 0x595889f5, 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def,
0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, 0x655baed3, 0xc01a3cad,
0x2a34fcde, 0x8f756ea0, 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f,
0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, 0x6a638c57, 0xcf221e29,
0x250cde5a, 0x804d4c24, 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e,
0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, 0xccec1688, 0x69ad84f6,
0x83834485, 0x26c2d6fb, 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae,
0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, 0x2290cf18, 0x87d15d66,
0x6dff9d15, 0xc8be0f6b, 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71,
0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, 0xd29c5380, 0x77ddc1fe,
0x9df3018d, 0x38b293f3, 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c,
0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, 0x3ce08a10, 0x99a1186e,
0x738fd81d, 0xd6ce4a63, 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79,
0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, 0x9a6f10cf, 0x3f2e82b1,
0xd50042c2, 0x7041d0bc, 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd,
0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, 0x9557324b, 0x3016a035,
0xda386046, 0x7f79f238, 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622,
0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, 0x437ad51e, 0xe63b4760,
0x0c158713, 0xa954156d, 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2,
0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8};
static const uint32_t table3_[256] = {
0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, 0x7b2231f3, 0xa6679b4b,
0xc4451272, 0x1900b8ca, 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf,
0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, 0xe964b13d, 0x34211b85,
0x560392bc, 0x8b463804, 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7,
0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, 0x6402e328, 0xb9474990,
0xdb65c0a9, 0x06206a11, 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2,
0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, 0x2161776d, 0xfc24ddd5,
0x9e0654ec, 0x4343fe54, 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7,
0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, 0x45639445, 0x98263efd,
0xfa04b7c4, 0x27411d7c, 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69,
0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, 0xaba65fe7, 0x76e3f55f,
0x14c17c66, 0xc984d6de, 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d,
0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, 0x26c00df2, 0xfb85a74a,
0x99a72e73, 0x44e284cb, 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3,
0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, 0xb4868d3c, 0x69c32784,
0x0be1aebd, 0xd6a40405, 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6,
0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, 0x07a17a9f, 0xdae4d027,
0xb8c6591e, 0x6583f3a6, 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3,
0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, 0x95e7fa51, 0x48a250e9,
0x2a80d9d0, 0xf7c57368, 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b,
0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, 0x1881a844, 0xc5c402fc,
0xa7e68bc5, 0x7aa3217d, 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006,
0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, 0xa4e4aad9, 0x79a10061,
0x1b838958, 0xc6c623e0, 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213,
0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, 0xc0e649f1, 0x1da3e349,
0x7f816a70, 0xa2c4c0c8, 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd,
0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, 0x8585ddb4, 0x58c0770c,
0x3ae2fe35, 0xe7a7548d, 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e,
0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, 0x08e38fa1, 0xd5a62519,
0xb784ac20, 0x6ac10698, 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0,
0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, 0x9aa50f6f, 0x47e0a5d7,
0x25c22cee, 0xf8878656, 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5,
0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, 0x8224a72b, 0x5f610d93,
0x3d4384aa, 0xe0062e12, 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07,
0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, 0x106227e5, 0xcd278d5d,
0xaf050464, 0x7240aedc, 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f,
0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, 0x9d0475f0, 0x4041df48,
0x22635671, 0xff26fcc9, 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a,
0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, 0xd867e1b5, 0x05224b0d,
0x6700c234, 0xba45688c, 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f,
0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, 0xbc65029d, 0x6120a825,
0x0302211c, 0xde478ba4, 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1,
0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842};
// Used to fetch a naturally-aligned 32-bit word in little endian byte-order
static inline uint32_t LE_LOAD32(const uint8_t *p) {
// Taken from tensorflow/tensorflow/core/platform/raw_coding.h
return ((static_cast<uint32_t>(static_cast<unsigned char>(p[0]))) |
(static_cast<uint32_t>(static_cast<unsigned char>(p[1])) << 8) |
(static_cast<uint32_t>(static_cast<unsigned char>(p[2])) << 16) |
(static_cast<uint32_t>(static_cast<unsigned char>(p[3])) << 24));
}
uint32_t Extend(uint32_t crc, const char *buf, size_t size) {
const uint8_t *p = reinterpret_cast<const uint8_t *>(buf);
const uint8_t *e = p + size;
uint32_t l = crc ^ 0xffffffffu;
#define STEP1 \
do { \
int c = (l & 0xff) ^ *p++; \
l = table0_[c] ^ (l >> 8); \
} while (0)
#define STEP4 \
do { \
uint32_t c = l ^ LE_LOAD32(p); \
p += 4; \
l = table3_[c & 0xff] ^ table2_[(c >> 8) & 0xff] ^ \
table1_[(c >> 16) & 0xff] ^ table0_[c >> 24]; \
} while (0)
// Point x at first 4-byte aligned byte in string. This might be
// just past the end of the string.
const uintptr_t pval = reinterpret_cast<uintptr_t>(p);
const uint8_t *x = reinterpret_cast<const uint8_t *>(((pval + 3) >> 2) << 2);
if (x <= e) {
// Process bytes until finished or p is 4-byte aligned
while (p != x) {
STEP1;
}
}
// Process bytes 16 at a time
while ((e - p) >= 16) {
STEP4;
STEP4;
STEP4;
STEP4;
}
// Process bytes 4 at a time
while ((e - p) >= 4) {
STEP4;
}
// Process the last few bytes
while (p != e) {
STEP1;
}
#undef STEP4
#undef STEP1
return l ^ 0xffffffffu;
}
}
================================================
FILE: src/crc32.h
================================================
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef CRC32_H
#define CRC32_H
#include <cstdint>
#include <stddef.h>
namespace tf_crc{
// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the
// crc32c of some string A. Extend() is often used to maintain the
// crc32c of a stream of data.
extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n);
// Return the crc32c of data[0,n-1]
inline uint32_t Value(const char* data, size_t n) { return Extend(0, data, n); }
#if defined(PLATFORM_GOOGLE)
extern uint32_t Extend(uint32_t init_crc, const absl::Cord& cord);
inline uint32_t Value(const absl::Cord& cord) { return Extend(0, cord); }
#endif
static const uint32_t kMaskDelta = 0xa282ead8ul;
// Return a masked representation of crc.
//
// Motivation: it is problematic to compute the CRC of a string that
// contains embedded CRCs. Therefore we recommend that CRCs stored
// somewhere (e.g., in files) should be masked before being stored.
inline uint32_t Mask(uint32_t crc) {
// Rotate right by 15 bits and add a constant.
return ((crc >> 15) | (crc << 17)) + kMaskDelta;
}
// Return the crc whose masked representation is masked_crc.
inline uint32_t Unmask(uint32_t masked_crc) {
uint32_t rot = masked_crc - kMaskDelta;
return ((rot >> 17) | (rot << 15));
}
}
#endif // TENSORFLOW_CORE_LIB_HASH_CRC32C_H_
================================================
FILE: src/darknetexporter.cpp
================================================
#include "darknetexporter.h"
void DarknetExporter::generateLabelIds(const QString names_file){
id_map.clear();
// Force sort list
QStringList class_list;
QFile fh(names_file);
if (fh.open(QIODevice::ReadOnly)) {
while (!fh.atEnd()) {
// Darknet name file is just a newline delimited list of classes
QByteArray line = fh.readLine();
class_list.append(line);
}
}
if(class_list.size() == 0){
qWarning() << "No classes found in names file.";
return;
}
int i = 0;
for(auto &name : class_list){
auto cleaned_name = name.simplified().toLower();
id_map[cleaned_name] = i++;
qDebug() << "Adding: " << cleaned_name << " (" << i << ")";
}
}
double clamp(double val, double min, double max){
if(val < min) val = min;
if(val > max) val = max;
return val;
}
void DarknetExporter::writeLabels(const cv::Mat &image, const QString label_filename, const QList<BoundingBox> labels){
// Still make a label file even if there are no detections. This is important
// for background class detection.
QFile f(label_filename);
// Delete existing files for simplicity.
if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
BoundingBox label;
foreach(label, labels){
QString text;
// Check if this label exists in the database
if(id_map.find(label.classname.toLower()) == id_map.end()){
qWarning() << "Couldn't find this label in the names file: " << label.classname.toLower();
continue;
}
double x = clamp(static_cast<double>(label.rect.center().x())/image.cols, 0.0, 0.999);
double y = clamp(static_cast<double>(label.rect.center().y())/image.rows, 0.0, 0.999);
double width = clamp(static_cast<double>(label.rect.width())/image.cols, 0.0, 0.999);
double height = clamp(static_cast<double>(label.rect.height())/image.rows, 0.0, 0.999);
text += QString("%1").arg(id_map[label.classname.toLower()]);
text += QString(" %1").arg(x);
text += QString(" %1").arg(y);
text += QString(" %1").arg(width);
text += QString(" %1").arg(height);
text += "\n";
f.write(text.toUtf8());
}
}
}
bool DarknetExporter::processImages(const QString folder, const QList<QString> images, export_image_type split_type){
QString image_path;
QList<BoundingBox> labels;
QProgressDialog progress("...", "Abort", 0, images.size(), static_cast<QWidget*>(parent()));
progress.setWindowModality(Qt::WindowModal);
if(folder == ""){
qCritical() << "Invalid folder specified.";
return false;
}
QString split_text = "";
if(split_type == EXPORT_VAL){
split_text = "VAL";
progress.setWindowTitle("Exporting validation images");
}else if(split_type == EXPORT_TRAIN){
split_text = "TRAIN";
progress.setWindowTitle("Exporting train images");
}else if(split_type == EXPORT_TEST){
split_text = "TEST";
progress.setWindowTitle("Exporting test images");
}else{
split_text = "UNASSIGNED";
}
if(!disable_progress){
progress.hide();
}
int i = 0;
foreach(image_path, images){
if(progress.wasCanceled()){
break;
}
project->getLabels(image_path, labels);
if(!export_unlabelled && labels.size() == 0){
if(!disable_progress){
progress.setValue(i);
progress.setLabelText(QString("%1 is unlabelled").arg(image_path));
progress.repaint();
QApplication::processEvents();
}
qDebug() << image_path << "is unlabelled";
continue;
}else{
qDebug() << "Processing:" << image_path;
}
QString extension = QFileInfo(image_path).suffix();
QString filename_noext = QFileInfo(image_path).completeBaseName();
filename_noext = filename_noext.prepend(filename_prefix);
QString image_filename = QString("%1.%2").arg(filename_noext).arg(extension);
QString label_filename = QString("%1.txt").arg(filename_noext);
// Correct for duplicate file names in output
int dupe_file = 1;
while(QFile(image_filename).exists()){
image_filename = QString("%1%2.%3")
.arg(filename_noext)
.arg(dupe_file)
.arg(extension);
label_filename = QString("%1.txt").arg(filename_noext).arg(dupe_file);
dupe_file++;
}
image_filename = QString("%1/%2").arg(folder).arg(image_filename);
label_filename = QString("%1/%2").arg(folder).arg(label_filename);
auto db_dir = project->getDbFolder();
auto abs_path = QFileInfo(QDir::cleanPath(db_dir.filePath(image_path))).absoluteFilePath();
qDebug() << "Copy:" << abs_path << " to: " << image_filename;
auto copied = QFile::copy(abs_path, image_filename);
if(!copied){
qWarning() << "Failed to copy image" << image_filename;
}
cv::Mat image = cv::imread(abs_path.toStdString());
if(image.empty()){
qCritical() << "Failed to load image!" << abs_path;
}else{
writeLabels(image, label_filename, labels);
}
if(!disable_progress){
progress.setValue(i++);
progress.setLabelText(image_filename);
}
}
return true;
}
void DarknetExporter::process(){
processImages(train_folder, train_set, EXPORT_TRAIN);
processImages(val_folder, validation_set, EXPORT_VAL);
}
================================================
FILE: src/darknetexporter.h
================================================
#ifndef DARKNETEXPORTER_H
#define DARKNETEXPORTER_H
#include <baseexporter.h>
#include <algorithm>
class DarknetExporter : public BaseExporter
{
Q_OBJECT
public:
explicit DarknetExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}
signals:
void export_progress(int);
public slots:
void generateLabelIds(const QString names_file);
void process();
protected:
void writeLabels(const cv::Mat &image, const QString file, const QList<BoundingBox> labels);
bool processImages(const QString folder, const QList<QString> images, export_image_type split_type=EXPORT_TRAIN);
};
#endif // DARKNETEXPORTER_H
================================================
FILE: src/darknetimporter.cpp
================================================
#include "darknetimporter.h"
void DarknetImporter::import(QString image_list, QString names_file, QString root_folder)
{
loadClasses(names_file);
// Get image filenames
auto filenames = readLines(image_list);
filenames.sort();
QProgressDialog progress("...", "Abort", 0, filenames.size(), static_cast<QWidget*>(parent()));
progress.setWindowModality(Qt::WindowModal);
progress.setWindowTitle("Loading images and labels");
int i = 0;
QList<QList<BoundingBox>> bboxes;
if (root_folder != "")
qInfo() << "Using relative pathnames, relative to: " << root_folder;
for(auto &image_path : filenames){
if(progress.wasCanceled())
break;
if (root_folder != "") {
auto abs_path = QDir(root_folder).absoluteFilePath(image_path);
if (!QFileInfo(abs_path).exists()) {
qWarning() << "Image: " << abs_path << " doesn't exist. Check your root folder.";
continue;
} else {
image_path = QFileInfo(abs_path).canonicalFilePath();
}
} else {
if (QFileInfo(image_path).isRelative()) {
qWarning() << "Relative image path: " << image_path
<< " provided, but no root folder specified";
continue;
}
}
bboxes.append(loadLabels(image_path));
progress.setValue(++i);
progress.setLabelText(QString("%1/%2: %3").arg(i).arg(filenames.size()).arg(image_path));
}
if(progress.wasCanceled())
project->addLabelledAssets(filenames.mid(0, bboxes.size()), bboxes);
else
project->addLabelledAssets(filenames, bboxes);
}
QList<BoundingBox> DarknetImporter::loadLabels(QString image_path){
QList<BoundingBox> boxes = {};
auto info = QFileInfo(image_path);
QString filename_noext = QDir(info.absolutePath()).filePath(info.baseName());
QString label_filename = QString("%1.txt").arg(filename_noext);
auto image = cv::imread(image_path.toStdString());
auto width = image.cols;
auto height = image.rows;
if(width <= 0 || height <= 0) return boxes;
auto lines = readLines(label_filename);
for(auto &line : lines){
BoundingBox bbox;
auto label = line.simplified().split(" ");
if(label.size() != 5) continue;
bbox.classid = label.at(0).toInt() + 1; // Since in the database they're 1-indexed
bbox.classname = project->getClassName(bbox.classid);
if(bbox.classname == ""){
qWarning() << "Class" << bbox.classid << " not found in names file.";
}
int center_x = static_cast<int>(label.at(1).toFloat() * width);
int center_y = static_cast<int>(label.at(2).toFloat() * height);
int box_width = static_cast<int>(label.at(3).toFloat() * width);
int box_height = static_cast<int>(label.at(4).toFloat() * height);
auto top_left = QPoint(std::min(std::max(center_x - box_width/2, 0), width),
std::min(std::max(center_y - box_height/2, 0), height));
auto bottom_right = QPoint(std::min(std::max(center_x + box_width/2, 0), width),
std::min(std::max(center_y + box_height/2, 0), height));
bbox.rect = QRect(top_left, bottom_right);
boxes.append(bbox);
}
return boxes;
}
void DarknetImporter::loadClasses(QString names_file){
QFile fh(names_file);
if (fh.open(QIODevice::ReadOnly)) {
while (!fh.atEnd()) {
// Darknet name file is just a newline delimited list of classes
QByteArray line = fh.readLine();
if(QString(line) == "") continue;
project->addClass(line.simplified());
qInfo() << line.simplified();
}
}
}
================================================
FILE: src/darknetimporter.h
================================================
#ifndef DARKNETIMPORTER_H
#define DARKNETIMPORTER_H
#include<baseimporter.h>
class DarknetImporter : public BaseImporter
{
public:
using BaseImporter::import;
explicit DarknetImporter(LabelProject *project, QObject *parent = nullptr) : BaseImporter(parent){
this->project = project;
}
void import(QString image_list, QString names_file, QString root_folder = "");
private:
void loadClasses(QString names_file);
QList<BoundingBox> loadLabels(QString image);
};
#endif // DARKNETIMPORTER_H
================================================
FILE: src/detection/detectoropencv.cpp
================================================
#include "detectoropencv.h"
DetectorOpenCV::DetectorOpenCV()
{
}
model_framework DetectorOpenCV::frameworkFromString(QString framework_string){
model_framework framework = FRAMEWORK_DARKNET;
if(framework_string.toLower().startsWith("darknet")){
framework = FRAMEWORK_DARKNET;
qInfo() << "Detector framework: Darknet";
}else if(framework_string.toLower().startsWith("tensorflow")){
framework = FRAMEWORK_TENSORFLOW;
qInfo() << "Detector framework: Tensorflow";
}
return framework;
}
int DetectorOpenCV::targetFromString(QString target_string){
int target = cv::dnn::DNN_TARGET_CPU;
if(target_string == "CPU"){
target = cv::dnn::DNN_TARGET_CPU;
}else if(target_string == "OpenCL"){
target = cv::dnn::DNN_TARGET_OPENCL;
}else if(target_string == "OpenCL FP16"){
target = cv::dnn::DNN_TARGET_OPENCL_FP16;
}else if(target_string == "CUDA"){
target = cv::dnn::DNN_TARGET_CUDA;
}else if(target_string == "CUDA FP16"){
target = cv::dnn::DNN_TARGET_CUDA_FP16;
}
return target;
}
void DetectorOpenCV::setTarget(QString target){
setTarget(targetFromString(target));
}
void DetectorOpenCV::setFramework(QString framework){
setFramework(frameworkFromString(framework));
}
void DetectorOpenCV::setImageSize(int width, int height){
if(width > 0 && height > 0){
input_width = width;
input_height = height;
}
}
void DetectorOpenCV::readNamesFile(std::string class_file){
class_names.clear();
// Load names of classes
std::ifstream ifs(class_file.c_str());
std::string line;
int i=0;
while (std::getline(ifs, line)){
QString cleaned = QString::fromStdString(line).simplified().toLower();
class_names.push_back(cleaned.toStdString());
std::cout << "Added detection class: " << i++ << " " << class_names.back() << std::endl;
}
}
void DetectorOpenCV::setChannels(int channels){
input_channels = channels;
}
void DetectorOpenCV::setTarget(int target){
preferable_target = target;
if(preferable_target == cv::dnn::DNN_TARGET_OPENCL){
// Check for GPU
cv::ocl::Context context;
if(!cv::ocl::haveOpenCL()){
std::cout << "OpenCL is not available. Falling back to CPU" << std::endl;
preferable_target = cv::dnn::DNN_TARGET_CPU;
}
// Attempt to use a GPU
if(context.create(cv::ocl::Device::TYPE_GPU)){
std::cout << "Found OpenCL capable GPU - we're going to use it!" << std::endl;
cv::ocl::Device(context.device(1));
}
}
#ifdef WITH_CUDA
else if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){
try{
// Check for GPU
if(cv::cuda::getCudaEnabledDeviceCount() == 0){
qCritical() << "No CUDA enabled devices found";
preferable_target = cv::dnn::DNN_TARGET_CPU;
return;
}else{
cv::cuda::setDevice(0);
}
auto devinfo = cv::cuda::DeviceInfo();
if (!devinfo.isCompatible()){
qWarning() << "Device is not CUDA compatible. Falling back to CPU";
preferable_target = cv::dnn::DNN_TARGET_CPU;
}else{
qInfo() << "NVIDIA GPU detected: " << devinfo.name();
}
}catch(...){
qCritical() << "Problem calling GPU functions, defaulting to CPU inference mode.";
qCritical() << "Check that OpenCV was compiled with CUDA or try rebooting.";
preferable_target = cv::dnn::DNN_TARGET_CPU;
}
}
#endif
}
void DetectorOpenCV::loadNetwork(std::string names_file, std::string cfg_file, std::string model_file){
// Load the names
readNamesFile(names_file);
// Infer network type automatically
net = cv::dnn::readNet(model_file, cfg_file);
// Should default to DNN_BACKEND_OPENCV (otherwise Intel inference engine)
if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
qDebug() << "Set preferable backend and target to CUDA";
}else{
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
}
net.setPreferableTarget(preferable_target);
getOutputClassNames();
}
void DetectorOpenCV::getOutputClassNames()
{
output_names.clear();
//Get the indices of the output layers, i.e. the layers with unconnected outputs
std::vector<int> outLayers = net.getUnconnectedOutLayers();
//get the names of all the layers in the network
std::vector<std::string> layersNames = net.getLayerNames();
// Get the names of the output layers in names
output_names.resize(outLayers.size());
for (size_t i = 0; i < outLayers.size(); ++i){
output_names.at(i) = layersNames.at(static_cast<size_t>(outLayers[i]) - 1);
}
}
void DetectorOpenCV::postProcessTensorflow(cv::Mat& frame, const std::vector<cv::Mat>& outputs, std::vector<BoundingBox> &filtered_boxes){
std::vector<int> classIds;
std::vector<float> confidences;
std::vector<cv::Rect> boxes;
auto detections = outputs.at(0);
const int numDetections = detections.size[2];
std::cout << "Outputs_size: " << detections.total() << std::endl;
std::cout << "Number of detections: " << numDetections << std::endl;
// batch id, class id, confidence, bbox (x4)
detections = detections.reshape(1, static_cast<int>(detections.total()) / 7);
// There are top-k (= 100 typical) detections, most of which should have
// more or less zero confidence.
for (int i = 0; i < numDetections; ++i)
{
float confidence = detections.at<float>(i, 2);
if (confidence > confThreshold)
{
// Extract the bounding box
int classId = static_cast<int>(detections.at<float>(i, 1));
int left = static_cast<int>(frame.cols * detections.at<float>(i, 3));
int top = static_cast<int>(frame.rows * detections.at<float>(i, 4));
int right = static_cast<int>(frame.cols * detections.at<float>(i, 5));
int bottom = static_cast<int>(frame.rows * detections.at<float>(i, 6));
BoundingBox bbox;
bbox.rect.setLeft(std::max(0, std::min(left, frame.cols - 1)));
bbox.rect.setTop(std::max(0, std::min(top, frame.rows - 1)));
bbox.rect.setRight(std::max(0, std::min(right, frame.cols - 1)));
bbox.rect.setBottom(std::max(0, std::min(bottom, frame.rows - 1)));
bbox.confidence = static_cast<double>(confidence);
bbox.classid = classId;
bbox.classname = QString::fromStdString(class_names.at(static_cast<size_t>(bbox.classid)));
std::cout << "Found (" << bbox.classid << ") " << bbox.classname.toStdString()
<< " at" << " (" << bbox.rect.center().x() << ", " << bbox.rect.center().y()
<< "), conf: " << bbox.confidence
<< ", size (" << bbox.rect.width() << "x" << bbox.rect.height() << ")"
<< std::endl;
filtered_boxes.push_back(bbox);
}else{
//
}
}
}
void DetectorOpenCV::postProcess(cv::Mat& frame, const std::vector<cv::Mat>& outputs, std::vector<BoundingBox> &filtered_boxes)
{
std::vector<int> classIds;
std::vector<float> confidences;
std::vector<cv::Rect> boxes;
// Debug: this should be three because there are three scales that Yolo searches over
//std::cout << "Outputs: " << outputs.size() << std::endl;
for (size_t i = 0; i < outputs.size(); ++i)
{
// Scan through all the bounding boxes output from the network and keep only the
// ones with high confidence scores. Assign the box's class label as the class
// with the highest score for the box.
float* data = reinterpret_cast<float*>(outputs[i].data);
for (int j = 0; j < outputs[i].rows; ++j, data += outputs[i].cols)
{
cv::Mat scores = outputs[i].row(j).colRange(5, outputs[i].cols);
cv::Point classIdPoint;
double confidence;
// Get the value and location of the maximum score
minMaxLoc(scores, nullptr, &confidence, nullptr, &classIdPoint);
if (confidence > 0)
{
// Output is a percentage of the frame width/height
// so it doesn't matter that we're transforming boxes
// between the resized and full-size image.
int centerX = static_cast<int>(data[0] * frame.cols);
int centerY = static_cast<int>(data[1] * frame.rows);
int width = static_cast<int>(data[2] * frame.cols);
int height = static_cast<int>(data[3] * frame.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
classIds.push_back(classIdPoint.x);
confidences.push_back(static_cast<float>(confidence));
boxes.push_back(cv::Rect(left, top, width, height));
}else{
if(confidence == 0.0)
continue;
std::cout << "Detected "
<< class_names.at(static_cast<size_t>(classIdPoint.x))
<< " with low confidence: " << confidence << std::endl;
}
}
}
std::vector<int> indices;
// Perform non maximum suppression to eliminate redundant overlapping boxes with
// lower confidences
// We set the confidence threshold to zero here, we'll filter the boxes out later.
// This lets us provide some feedback to the user if their threshold is too high.
cv::dnn::NMSBoxes(boxes, confidences, static_cast<float>(confThreshold), static_cast<float>(nmsThreshold), indices);
for (size_t i = 0; i < indices.size(); ++i)
{
auto idx = static_cast<size_t>(indices.at(i));
BoundingBox box;
cv::Rect rect = boxes.at(idx);
box.confidence = static_cast<double>(confidences.at(idx));
box.classid = classIds.at(idx);
box.classname = QString::fromStdString(class_names.at(static_cast<size_t>(box.classid)));
// Darknet predicts box centres and half-width/height, so the
// box can go outside the image. Clamp to the image size:
QPoint top_left = {std::max(0, rect.x), std::max(0, rect.y)};
QPoint bottom_right = top_left + QPoint({rect.width, rect.height});
bottom_right.setX(std::min(bottom_right.x(), frame.cols));
bottom_right.setY(std::min(bottom_right.y(), frame.rows));
box.rect.setBottomRight(bottom_right);
box.rect.setTopLeft(top_left);
std::cout << "Found " << box.classname.toStdString()
<< " at" << " (" << box.rect.center().x() << ", " << box.rect.center().y()
<< "), conf: " << box.confidence
<< ", size (" << box.rect.width() << "x" << box.rect.height() << ")"
<< std::endl;
filtered_boxes.push_back(box);
}
return;
}
std::vector<BoundingBox> DetectorOpenCV::infer(cv::Mat image){
std::vector<BoundingBox> detections;
// Assume we have an alpha image if 4 channels
if(image.channels() == 4){
cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);
}
// 16-bit conversion, min-max scaling
if(convert_depth && image.elemSize() == 2){
double minval, maxval;
cv::minMaxIdx(image, &minval, &maxval);
double range = maxval-minval;
double scale_factor = 255.0/range;
image.convertTo(image, CV_32FC1);
image -= minval;
image *= scale_factor;
image.convertTo(image, CV_8UC1);
}
if(convert_grayscale && image.channels() == 1){
cv::cvtColor(image, image, cv::COLOR_GRAY2RGB);
}
if(image.channels() != input_channels){
std::cout << "Input channel mismatch. Expecting "
<< input_channels
<< " channels but image has " << image.channels()
<< " channels.";
return detections;
}
if(framework == FRAMEWORK_TENSORFLOW){
detections = inferTensorflow(image);
}else if(framework == FRAMEWORK_DARKNET){
detections = inferDarknet(image);
}
return detections;
}
std::vector<BoundingBox> DetectorOpenCV::inferTensorflow(cv::Mat image){
std::vector<BoundingBox> results;
auto mean = cv::Scalar(0,0,0);
if(image.channels() == 1){
mean = cv::Scalar(0);
}
// Check for 16-bit
double scale_factor = 1/255.0;
if(image.elemSize() == 2){
scale_factor = 1/65535.0;
}
auto input_size = cv::Size(image.cols, image.rows);
bool swap_rb = false; // BGR->RGB?
bool crop = false; // Use the entire image
// No normalising! The model will handle it.
auto blob = cv::dnn::blobFromImage(image, 1.0, input_size, mean, swap_rb, crop);
//Sets the input to the network
net.setInput(blob);
// Runs the forward pass to get output of the output layers
std::vector<cv::Mat> outputs;
net.forward(outputs, output_names);
postProcessTensorflow(image, outputs, results);
std::vector<double> layersTimes;
double freq = cv::getTickFrequency() / 1000;
processing_time = net.getPerfProfile(layersTimes) / freq;
std::cout << "Processed in: " << processing_time << std::endl;
return results;
}
void DetectorOpenCV::setNormalisation(double scale_factor, cv::Scalar mean){
this->scale_factor = scale_factor;
this->mean = mean;
}
std::vector<BoundingBox> DetectorOpenCV::inferDarknet(cv::Mat image){
std::vector<BoundingBox> results;
auto mean = cv::Scalar(0,0,0);
if(image.channels() == 1){
mean = cv::Scalar(0);
}
// Normalisation, check for 16-bit
// TODO: have this as a user option
if(image.elemSize() == 2){
scale_factor = 1/65535.0;
}else{
scale_factor= 1/255.0;
}
auto input_size = cv::Size(input_width, input_height);
bool swap_rb = true; // BGR->RGB?
bool crop = false; // Use the entire image
auto blob = cv::dnn::blobFromImage(image, scale_factor, input_size, mean, swap_rb, crop);
//Sets the input to the network
net.setInput(blob);
// Runs the forward pass to get output of the output layers
std::vector<cv::Mat> outputs;
net.forward(outputs, output_names);
// Put efficiency information. The function getPerfProfile returns the
// overall time for inference(t) and the timings for each of the layers(in layersTimes)
std::vector<double> layersTimes;
double freq = cv::getTickFrequency() / 1000;
processing_time = net.getPerfProfile(layersTimes) / freq;
std::cout << "Processed in: " << processing_time << std::endl;
// Remove the bounding boxes with low confidence
postProcess(image, outputs, results);
return results;
}
void DetectorOpenCV::annotateImage(cv::Mat &frame, std::vector<BoundingBox> boxes, cv::Scalar colour, cv::Scalar font_colour){
for(auto &box : boxes){
//Draw a rectangle displaying the bounding box
auto top_left = cv::Point(box.rect.left(), box.rect.top());
cv::rectangle(frame, top_left,
cv::Point(box.rect.right(), box.rect.bottom()),
colour);
//Get the label for the class name and its confidence
std::string label = cv::format("%.2f", box.confidence);
//Display the label at the top of the bounding box
int baseLine;
cv::Size labelSize = getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
cv::putText(frame, label, top_left, cv::FONT_HERSHEY_SIMPLEX, 0.5, font_colour);
}
}
void DetectorOpenCV::runOnProject(LabelProject *project){
QList<QString> images;
project->getImageList(images);
for(auto& image_path : images){
auto image = cv::imread(image_path.toStdString(), cv::IMREAD_UNCHANGED|cv::IMREAD_ANYDEPTH);
// Assume we have an alpha image if 4 channels
if(image.channels() == 4){
cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);
}
auto boxes = infer(image);
QList<BoundingBox> existing_boxes;
project->getLabels(image_path, existing_boxes);
for(auto &box : boxes){
if(!project->classInDB(box.classname)){
project->addClass(box.classname);
}
// Strip out boxes which are already in the image
// assume detector is deterministic
bool exists = false;
for(auto &existing : existing_boxes){
if(existing.rect == box.rect && existing.classname == box.classname){
exists = true;
}
}
if(!exists){
qDebug() << "Adding label";
project->addLabel(image_path, box);
}
}
}
}
================================================
FILE: src/detection/detectoropencv.h
================================================
#ifndef DETECTOROPENCV_H
#define DETECTOROPENCV_H
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<opencv2/opencv.hpp>
#include<opencv2/core/ocl.hpp>
#ifdef WITH_CUDA
#include <opencv2/core/cuda.hpp>
#endif
#include<opencv2/dnn.hpp>
#include<boundingbox.h>
#include<imagedisplay.h>
#include<labelproject.h>
typedef enum {
FRAMEWORK_TENSORFLOW,
FRAMEWORK_DARKNET
} model_framework;
class DetectorOpenCV
{
public:
DetectorOpenCV();
void setImageSize(int width, int height);
void loadNetwork(std::string names_file, std::string cfg_file, std::string model_file);
void annotateImage(cv::Mat &image,
std::vector<BoundingBox> boxes,
cv::Scalar colour = cv::Scalar(0,0,255),
cv::Scalar font_colour = cv::Scalar(255,255,255));
std::vector<BoundingBox> infer(cv::Mat image);
std::vector<BoundingBox> inferDarknet(cv::Mat image);
std::vector<BoundingBox> inferTensorflow(cv::Mat image);
void setFramework(model_framework framework){this->framework = framework;}
void setFramework(QString framework);
void setConfidenceThreshold(double thresh){confThreshold = std::max(0.0, thresh);}
void setNMSThreshold(double thresh){nmsThreshold = std::max(0.0, thresh);}
void setConvertGrayscale(bool convert){convert_grayscale = convert;}
void setConvertDepth(bool convert){convert_depth = convert;}
double getConfidenceThreshold(void){ return confThreshold;}
double getNMSThreshold(void){ return nmsThreshold;}
void setTarget(int target);
void setTarget(QString target);
void setChannels(int channels);
void setNormalisation(double scale_factor, cv::Scalar mean);
int getChannels(void){return input_channels;}
static model_framework frameworkFromString(QString framework_string);
static int targetFromString(QString target_string);
void runOnProject(LabelProject *project);
private:
void postProcess(cv::Mat& frame, const std::vector<cv::Mat>& outs, std::vector<BoundingBox> &filtered_outputs);
void readNamesFile(std::string class_file = "coco.names");
void getOutputClassNames(void);
bool convert_grayscale = true;
bool convert_depth = true;
double processing_time;
double confThreshold = 0.5; // Confidence threshold
double nmsThreshold = 0.4; // Non-maximum suppression threshold
int input_width = 416; // Width of network's input image
int input_height = 416; // Height of network's input image
int input_channels = 3;
int preferable_target = cv::dnn::DNN_TARGET_OPENCL;
model_framework framework = FRAMEWORK_DARKNET;
cv::Scalar mean = {0,0,0};
double scale_factor = 1/255.;
std::vector<std::string> class_names;
std::vector<std::string> output_names;
cv::dnn::Net net;
void postProcessTensorflow(cv::Mat &frame, const std::vector<cv::Mat> &outputs, std::vector<BoundingBox> &filtered_boxes);
};
#endif // DETECTOROPENCV_H
================================================
FILE: src/detection/detectorsetupdialog.cpp
================================================
#include "detectorsetupdialog.h"
#include "ui_detectorsetupdialog.h"
DetectorSetupDialog::DetectorSetupDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::DetectorSetupDialog)
{
ui->setupUi(this);
connect(ui->cfgPathButton, SIGNAL(clicked()), this, SLOT(setCfgFile()));
connect(ui->cfgPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
connect(ui->weightPathButton, SIGNAL(clicked(bool)), this, SLOT(setWeightsFile()));
connect(ui->weightPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
connect(ui->namesPathButton, SIGNAL(clicked(bool)), this, SLOT(setNamesFile()));
connect(ui->frameworkComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setFramework()));
connect(ui->targetComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setTarget()));
connect(ui->namesPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
connect(ui->convertGrayscaleCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertGrayscale()));
connect(ui->convertDepthCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertDepth()));
settings = new QSettings("DeepLabel", "DeepLabel");
cfg_file = settings->value("model_cfg", "").toString();
weight_file = settings->value("model_weights", "").toString();
names_file = settings->value("model_names", "").toString();
convert_grayscale = settings->value("model_convert_grayscale", true).toBool();
ui->convertGrayscaleCheckbox->setChecked(convert_grayscale);
convert_depth = settings->value("model_convert_depth", true).toBool();
ui->convertDepthCheckbox->setChecked(convert_depth);
image_width = settings->value("model_width", 0).toInt();
image_height = settings->value("model_height", 0).toInt();
image_channels = settings->value("model_channels", 3).toInt();
framework = static_cast<model_framework>(settings->value("model_framework", 0).toInt());
if(framework == FRAMEWORK_DARKNET){
ui->frameworkComboBox->setCurrentText("Darknet (YOLO)");
ui->imageHeightLabel->show();
ui->imageWidthLabel->show();
}else if(framework == FRAMEWORK_TENSORFLOW){
ui->frameworkComboBox->setCurrentText("Tensorflow");
ui->imageHeightLabel->hide();
ui->imageWidthLabel->hide();
}
target = settings->value("model_target", cv::dnn::DNN_TARGET_CPU).toInt();
if(target == cv::dnn::DNN_TARGET_CPU){
ui->targetComboBox->setCurrentText("CPU");
}else if(target == cv::dnn::DNN_TARGET_OPENCL){
ui->targetComboBox->setCurrentText("OpenCL");
}else if(target == cv::dnn::DNN_TARGET_OPENCL_FP16){
ui->targetComboBox->setCurrentText("OpenCL FP16");
}
#ifdef WITH_CUDA
else if(target == cv::dnn::DNN_TARGET_CUDA){
ui->targetComboBox->setCurrentText("CUDA");
}else if(target == cv::dnn::DNN_TARGET_CUDA_FP16){
ui->targetComboBox->setCurrentText("CUDA FP16");
}
#endif
updateFields();
checkForm();
}
void DetectorSetupDialog::updateFields(){
ui->cfgPathLineEdit->setText(cfg_file);
ui->weightPathLineEdit->setText(weight_file);
ui->namesPathLineEdit->setText(names_file);
ui->imageWidthLabel->setText(QString::number(image_width));
ui->imageHeightLabel->setText(QString::number(image_height));
ui->imageChannelsLabel->setText(QString::number(image_channels));
}
void DetectorSetupDialog::setConvertGrayscale(void){
convert_grayscale = ui->convertGrayscaleCheckbox->isChecked();
settings->setValue("model_convert_grayscale", convert_grayscale);
}
bool DetectorSetupDialog::getConvertGrayscale(void){
return convert_grayscale;
}
void DetectorSetupDialog::setConvertDepth(void){
convert_depth = ui->convertDepthCheckbox->isChecked();
settings->setValue("model_convert_depth", convert_depth);
}
bool DetectorSetupDialog::getConvertDepth(void){
return convert_depth;
}
void DetectorSetupDialog::checkForm(void){
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
cfg_file = ui->cfgPathLineEdit->text();
weight_file = ui->weightPathLineEdit->text();
names_file = ui->namesPathLineEdit->text();
if(cfg_file == "") return;
if(weight_file == "") return;
if(names_file == "") return;
if(!QFile(cfg_file).exists()){
qDebug() << "Config file doesn't exist";
return;
}else if(!getParamsFromConfig()){
return;
}
if(!QFile(weight_file).exists()){
qDebug() << "Weight file doesn't exist";
return;
}
if(!QFile(names_file).exists()){
qDebug() << "Names file doesn't exist";
return;
}
// At this point, all good.
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
settings->setValue("model_width", image_width);
settings->setValue("model_height", image_height);
settings->setValue("modelchannels", image_channels);
settings->setValue("model_cfg", cfg_file);
settings->setValue("model_weights", weight_file);
settings->setValue("model_names", names_file);
}
bool DetectorSetupDialog::getParamsFromConfig(void){
qDebug() << "Checking config file";
if(framework == FRAMEWORK_DARKNET){
QSettings darknet_settings(cfg_file, QSettings::IniFormat);
darknet_settings.beginGroup("net");
auto keys = darknet_settings.childKeys();
if(!darknet_settings.contains("width")){
qDebug() << "No width parameter";
return false;
}
if(!darknet_settings.contains("height")){
qDebug() << "No height parameter";
return false;
}
if(!darknet_settings.contains("channels")){
qDebug() << "No channels parameter";
return false;
}
auto width = darknet_settings.value("width").toInt();
auto height = darknet_settings.value("height").toInt();
auto channels = darknet_settings.value("channels").toInt();
darknet_settings.endGroup();
qDebug() << width << height << channels;
if(width > 0 && height > 0 && channels > 0){
qDebug() << width << height << channels;
image_width = width;
image_height = height;
image_channels = channels;
}else{
return false;
}
}else if(framework == FRAMEWORK_TENSORFLOW){
// In theory we can parse the .pbtxt file to figure out
// the input layer parameters, but that either means bringing in
// protobuf or loading the entire network via OpenCV.
}
updateFields();
return true;
}
QString DetectorSetupDialog::openFile(QString title, QString search_path, QString filter){
QString openDir;
if(search_path == ""){
openDir = settings->value("project_folder").toString();
}else{
openDir = QFileInfo(search_path).absoluteDir().absolutePath();
}
auto path = QFileDialog::getOpenFileName(this, title,
openDir,
QString("All files (*.*);;%1").arg(filter),
&filter);
return path;
}
void DetectorSetupDialog::setCfgFile(void){
QString filter, title;
if(framework == FRAMEWORK_DARKNET){
filter = tr("Config (*.cfg)");
title = tr("Select darknet config file");
}else if(framework == FRAMEWORK_TENSORFLOW){
filter = tr("Config (*.pbtxt)");
title = tr("Select tensorflow config file");
}else{
return;
}
auto path = openFile(title, cfg_file, filter);
if(path != ""){
ui->cfgPathLineEdit->setText(path);
}
checkForm();
}
void DetectorSetupDialog::setWeightsFile(void){
QString filter, title;
if(framework == FRAMEWORK_DARKNET){
filter = tr("Weights (*.weights)");
title = tr("Select darknet weights file");
}else if(framework == FRAMEWORK_TENSORFLOW){
filter = tr("Config (*.pb)");
title = tr("Select tensorflow frozen graph");
}else{
return;
}
auto path = openFile(title, weight_file, filter);
if(path != ""){
ui->weightPathLineEdit->setText(path);
}
checkForm();
}
void DetectorSetupDialog::setFramework(void){
qDebug() << "Attempting to set framework to " << ui->frameworkComboBox->currentText();
auto framework = DetectorOpenCV::frameworkFromString(ui->frameworkComboBox->currentText());
settings->setValue("model_framework", framework);
}
void DetectorSetupDialog::setTarget(void){
qInfo() << "Attempting to set target to " << ui->targetComboBox->currentText();
auto target = DetectorOpenCV::targetFromString(ui->targetComboBox->currentText());
settings->setValue("model_target", target);
}
int DetectorSetupDialog::getTarget(void){
return target;
}
void DetectorSetupDialog::setNamesFile(void){
QString filter = tr("Names (*.names)");
QString title = tr("Select darknet names file");
auto path = openFile(title, names_file, filter);
if(path != ""){
ui->namesPathLineEdit->setText(path);
}
checkForm();
}
DetectorSetupDialog::~DetectorSetupDialog()
{
delete ui;
}
================================================
FILE: src/detection/detectorsetupdialog.h
================================================
#ifndef DETECTORSETUPDIALOG_H
#define DETECTORSETUPDIALOG_H
#include <QDialog>
#include <QFileDialog>
#include <QSettings>
#include <QPushButton>
#include <QtDebug>
#include <QDir>
#include <opencv2/dnn.hpp>
#include <detection/detectoropencv.h>
namespace Ui {
class DetectorSetupDialog;
}
class DetectorSetupDialog : public QDialog
{
Q_OBJECT
public:
explicit DetectorSetupDialog(QWidget *parent = nullptr);
int getWidth(void){return image_width;}
int getHeight(void){return image_height;}
int getChannels(void){return image_channels;}
QString getNames(void){return names_file;}
QString getWeights(void){return weight_file;}
QString getCfg(void){return cfg_file;}
bool getConvertGrayscale(void);
bool getConvertDepth(void);
model_framework getFramework(void){return framework;}
int getTarget();
~DetectorSetupDialog();
private slots:
void setCfgFile();
void setNamesFile();
void setWeightsFile();
void setFramework();
void setTarget();
void setConvertGrayscale(void);
void setConvertDepth(void);
void checkForm();
private:
Ui::DetectorSetupDialog *ui;
bool getParamsFromConfig();
void updateFields();
QSettings* settings;
QString cfg_file;
QString weight_file;
QString names_file;
int image_width = 320;
int image_height = 240;
int image_channels = 3; // default
int target = 0;
bool convert_grayscale = true;
bool convert_depth = true;
model_framework framework = FRAMEWORK_TENSORFLOW;
QString openFile(QString title, QString search_path="", QString filter="");
};
#endif // DETECTORSETUPDIALOG_H
================================================
FILE: src/detection/detectorsetupdialog.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DetectorSetupDialog</class>
<widget class="QDialog" name="DetectorSetupDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>314</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Configure object detector</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Weight file</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="frameworkComboBox">
<item>
<property name="text">
<string>Darknet (YOLO)</string>
</property>
</item>
<item>
<property name="text">
<string>Tensorflow</string>
</property>
</item>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="targetComboBox">
<item>
<property name="text">
<string>CPU</string>
</property>
</item>
<item>
<property name="text">
<string>CUDA</string>
</property>
</item>
<item>
<property name="text">
<string>CUDA FP16</string>
</property>
</item>
<item>
<property name="text">
<string>OpenCL</string>
</property>
</item>
<item>
<property name="text">
<string>OpenCL FP16</string>
</property>
</item>
</widget>
</item>
<item row="3" column="3">
<widget class="QToolButton" name="namesPathButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Config file</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="weightPathLineEdit"/>
</item>
<item row="9" column="1">
<widget class="QLabel" name="imageHeightLabel">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Channels</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="convertGrayscaleCheckbox">
<property name="text">
<string>Convert grayscale images to RGB</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Image Height</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="imageChannelsLabel">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="cfgPathButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Loaded Network Parameters:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="imageWidthLabel">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QToolButton" name="weightPathButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="cfgPathLineEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Names file</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="namesPathLineEdit"/>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Image Width</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Target</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="convertDepthCheckbox">
<property name="text">
<string>Convert 16-bit images to 8-bit</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>cfgPathLineEdit</tabstop>
<tabstop>cfgPathButton</tabstop>
<tabstop>weightPathLineEdit</tabstop>
<tabstop>weightPathButton</tabstop>
<tabstop>namesPathLineEdit</tabstop>
<tabstop>namesPathButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DetectorSetupDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DetectorSetupDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
================================================
FILE: src/exportdialog.cpp
================================================
#include "exportdialog.h"
#include "ui_exportdialog.h"
ExportDialog::ExportDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ExportDialog)
{
ui->setupUi(this);
toggleExporter();
do_shuffle = ui->randomSplitCheckbox->isChecked();
validation_split_pc = ui->validationSplitSpinbox->value();
connect(ui->exportSelectComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(toggleExporter()));
connect(ui->randomSplitCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleShuffle(bool)));
connect(ui->validationSplitSpinbox, SIGNAL(valueChanged(int)), this, SLOT(setValidationSplit(int)));
connect(ui->exportLabelledCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleExportUnlabelled(bool)));
connect(ui->trainValCheckbox, SIGNAL(clicked(bool)), this, SLOT(toggleValidationSplit(bool)));
connect(ui->gcpBucketLineEdit, SIGNAL(textEdited(QString)), SLOT(setBucketUri(QString)));
connect(ui->filePrefixLineEdit, SIGNAL(textEdited(QString)), SLOT(setFilePrefix(QString)));
connect(ui->namesFileLineEdit, SIGNAL(textEdited(QString)), SLOT(setNamesFile(QString)));
connect(ui->namesFilePushButton, SIGNAL(clicked()), this, SLOT(setNamesFile()));
connect(ui->outputFolderLineEdit, SIGNAL(textEdited(QString)), SLOT(setOutputFolder(QString)));
connect(ui->outputFolderPushButton, SIGNAL(clicked()), this, SLOT(setOutputFolder()));
settings = new QSettings("DeepLabel", "DeepLabel");
ui->validationSplitSpinbox->setValue(settings->value("validation_split_pc", 80).toInt());
toggleShuffle(settings->value("do_shuffle", false).toBool());
toggleExportUnlabelled(settings->value("export_unlabelled", false).toBool());
ui->exportLabelledCheckbox->setChecked(export_unlabelled);
toggleValidationSplit(settings->value("validation_split_enable", false).toBool());
ui->trainValCheckbox->setChecked(validation_split_enable);
toggleAppendLabels(settings->value("append_labels", false).toBool());
ui->trainValCheckbox->setChecked(append_labels);
if(settings->contains("output_folder")){
auto path = settings->value("output_folder").toString();
if(path != ""){
setOutputFolder(path);
}
}
if(settings->contains("names_file")){
auto path = settings->value("names_file").toString();
if(path != ""){
setNamesFile(path);
}
}
if(settings->contains("file_prefix")){
setFilePrefix(settings->value("file_prefix").toString());
}
}
ExportDialog::~ExportDialog()
{
delete settings;
delete ui;
}
bool ExportDialog::getExportUnlablled(void){
return export_unlabelled;
}
bool ExportDialog::getValidationSplitEnabled(void){
return validation_split_enable;
}
QString ExportDialog::getFilePrefix(void){
return file_prefix;
}
void ExportDialog::toggleExportUnlabelled(bool res){
export_unlabelled = res;
settings->setValue("export_unlabelled", export_unlabelled);
}
void ExportDialog::setValidationSplit(int value){
if (value < 0 || value > 100) return;
validation_split_pc = value;
settings->setValue("validation_split_pc", validation_split_pc);
}
void ExportDialog::toggleValidationSplit(bool enable){
validation_split_enable = enable;
settings->setValue("validation_split_enable", enable);
}
void ExportDialog::toggleAppendLabels(bool enable){
append_labels = enable;
settings->setValue("append_labels", enable);
}
void ExportDialog::toggleShuffle(bool shuffle){
do_shuffle = shuffle;
settings->setValue("do_shuffle", do_shuffle);
ui->randomSplitCheckbox->setChecked(do_shuffle);
}
void ExportDialog::setFilePrefix(QString prefix){
if(prefix != ""){
file_prefix = prefix;
}
settings->setValue("validation_split_pc", validation_split_pc);
}
void ExportDialog::setOutputFolder(QString path){
if(path == ""){
QString openDir;
if(output_folder == ""){
openDir = QDir::homePath();
}else{
openDir = output_folder;
}
path = QFileDialog::getExistingDirectory(this, tr("Select output folder"),
openDir);
}
if(path != ""){
output_folder = path;
ui->outputFolderLineEdit->setText(output_folder);
settings->setValue("output_folder", output_folder);
}
checkOK();
}
void ExportDialog::setBucketUri(QString uri){
if(uri != ""){
bucket_uri = uri;
}
}
void ExportDialog::setNamesFile(QString path){
if(path == ""){
QString openDir;
if(names_file == ""){
openDir = QDir::homePath();
}else{
openDir = QFileInfo(names_file).absoluteDir().absolutePath();
}
path = QFileDialog::getOpenFileName(this, tr("Select darknet names file"),
openDir);
}
if(path != ""){
names_file = path;
ui->namesFileLineEdit->setText(names_file);
settings->setValue("names_file", names_file);
}
checkOK();
}
bool ExportDialog::checkOK(){
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
// If output folder exists
if(!QDir(output_folder).exists() || output_folder == ""){
qCritical() << "Export output folder doesn't exist";
return false;
}
if(ui->exportSelectComboBox->currentText() == "GCP AutoML"){
if(bucket_uri == "") return false;
}
if(ui->exportSelectComboBox->currentText() == "Darknet"){
// If we're using darknet, check the names
// file exists and contains something
if(!QFile::exists(names_file)){
qCritical() << "Names file doesn't exist";
return false;
}
QStringList class_list;
QFile fh(names_file);
if (fh.open(QIODevice::ReadOnly)) {
while (!fh.atEnd()) {
// Darknet name file is just a newline delimited list of classes
QByteArray line = fh.readLine();
class_list.append(line);
}
}
if(class_list.size() == 0){
qCritical() << "No classes?";
return false;
}
}
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
return true;
}
bool ExportDialog::getCreateLabelMap(void){
return ui->labelMapCheckbox->isChecked();
}
void ExportDialog::toggleExporter(){
current_exporter = ui->exportSelectComboBox->currentText();
ui->namesFileLineEdit->setEnabled(current_exporter == "Darknet");
ui->namesFilePushButton->setEnabled(current_exporter == "Darknet");
ui->labelMapCheckbox->setEnabled(current_exporter == "Pascal VOC");
ui->gcpBucketLineEdit->setEnabled(current_exporter == "GCP AutoML");
checkOK();
}
================================================
FILE: src/exportdialog.h
================================================
#ifndef EXPORTDIALOG_H
#define EXPORTDIALOG_H
#include <QDialog>
#include <QFile>
#include <QDir>
#include <QFileDialog>
#include <QSettings>
#include <QtDebug>
namespace Ui {
class ExportDialog;
}
class ExportDialog : public QDialog
{
Q_OBJECT
public:
explicit ExportDialog(QWidget *parent = nullptr);
~ExportDialog();
QString getExporter(){return current_exporter; }
QString getOutputFolder(){return output_folder; }
QString getNamesFile(){return names_file; }
int getShuffle(){return do_shuffle; }
double getValidationSplit(){
return static_cast<double>(validation_split_pc)/100.0;
}
bool getAppendLabels(){return append_labels;}
QString getBucket(){ return bucket_uri; }
bool getCreateLabelMap();
bool getExportUnlablled();
bool getValidationSplitEnabled();
QString getFilePrefix();
private slots:
void setNamesFile(QString path="");
void setOutputFolder(QString path="");
void setValidationSplit(int value);
void toggleShuffle(bool shuffle);
void toggleAppendLabels(bool enable);
void toggleValidationSplit(bool enable);
void toggleExportUnlabelled(bool res);
void toggleExporter();
void setBucketUri(QString uri);
void setFilePrefix(QString prefix);
private:
Ui::ExportDialog *ui;
bool checkOK();
QSettings *settings;
QString output_folder = "";
QString names_file = "";
QString bucket_uri = "";
QString file_prefix = "";
QString current_exporter = "Darknet";
bool do_shuffle = false;
bool append_labels = false;
bool validation_split_enable = false;
int validation_split_pc = 80;
bool export_unlabelled;
};
#endif // EXPORTDIALOG_H
================================================
FILE: src/exportdialog.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExportDialog</class>
<widget class="QDialog" name="ExportDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>338</width>
<height>410</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Names file</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="exportSelectComboBox">
<item>
<property name="text">
<string>COCO (JSON)</string>
</property>
</item>
<item>
<property name="text">
<string>Darknet</string>
</property>
</item>
<item>
<property name="text">
<string>GCP AutoML</string>
</property>
</item>
<item>
<property name="text">
<string>Kitti</string>
</property>
</item>
<item>
<property name="text">
<string>Pascal VOC</string>
</property>
</item>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Validation Split</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>File prefix</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Output Type: </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="outputFolderLineEdit">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Bucket URI</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="validationSplitSpinbox">
<property name="suffix">
<string>%</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>80</number>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="trainValCheckbox">
<property name="text">
<string>Train/Val Split</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="filePrefixLineEdit"/>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="namesFilePushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QCheckBox" name="randomSplitCheckbox">
<property name="text">
<string>Randomise Split</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Output Folder</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="namesFileLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="outputFolderPushButton">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="gcpBucketLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>gs://your/bucket/path</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="3">
<widget class="QCheckBox" name="exportLabelledCheckbox">
<property name="text">
<string>Export unlabelled images</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="10" column="0" colspan="3">
<widget class="QCheckBox" name="labelMapCheckbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save VOC label maps</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0" colspan="3">
<widget class="QCheckBox" name="appendLabelCheckbox">
<property name="text">
<string>Append existing label file</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ExportDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ExportDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
================================================
FILE: src/exporter.h
================================================
#ifndef EXPORTER_H
#define EXPORTER_H
#include <kittiexporter.h>
#include <darknetexporter.h>
#include <pascalvocexporter.h>
#include <cocoexporter.h>
#include <gcpexporter.h>
#include <tfrecordexporter.h>
#include <videoexporter.h>
#endif // EXPORTER_H
================================================
FILE: src/gcpexporter.cpp
================================================
#include "gcpexporter.h"
bool GCPExporter::processImages(const QString folder,
const QString filename,
const QList<QString> images,
export_image_type split_type){
QString image_path;
QList<BoundingBox> labels;
QString label_filename = QString("%1/%2.txt").arg(folder).arg(filename);
QFile f(label_filename);
if (!f.open(QIODevice::Append | QIODevice::Truncate)) {
return false;
}
QProgressDialog progress("...", "Abort", 0, images.size(), static_cast<QWidget*>(parent()));
progress.setWindowModality(Qt::WindowModal);
if(!disable_progress){
QString split_text = "";
if(split_type == EXPORT_VAL){
split_text = "VAL";
progress.setWindowTitle("Exporting validation images");
}else if(split_type == EXPORT_TRAIN){
split_text = "TRAIN";
progress.setWindowTitle("Exporting train images");
}else if(split_type == EXPORT_TEST){
split_text = "TEST";
progress.setWindowTitle("Exporting test images");
}else{
split_text = "UNASSIGNED";
}
}else{
progress.hide();
}
int i=0;
foreach(image_path, images){
if(progress.wasCanceled()){
break;
}
// Check image
cv::Mat image = cv::imread(image_path.toStdString());
if(image.empty()) continue;
double width = static_cast<double>(image.cols);
double height = static_cast<double>(image.rows);
project->getLabels(image_path, labels);
// Check labels
if(!export_unlabelled && labels.size() == 0){
if(!disable_progress){
progress.setValue(i);
progress.setLabelText(QString("%1 is unlabelled").arg(image_path));
progress.repaint();
QApplication::processEvents();
}
continue;
}
// Deal with duplicate filenames, etc
QString extension = QFileInfo(image_path).suffix();
QString filename_noext = QFileInfo(image_path).completeBaseName();
QString image_filename = QString("%1/%2.%3").arg(folder).arg(filename_noext).arg(extension);
// Correct for duplicate file names in output
int dupe_file = 1;
while(QFile(image_filename).exists()){
image_filename = QString("%1/%2_%3.%4").arg(folder).arg(filename_noext).arg(dupe_file++).arg(extension);
}
filename_noext = QFileInfo(image_filename).completeBaseName();
auto copied = QFile::copy(image_path, image_filename);
if(!copied){
qCritical() << "Failed to copy image" << image_filename;
}
auto bucket_path = QString("%1/%2.%3").arg(bucket_uri).arg(filename_noext).arg(extension);
for(auto &label : labels){
auto gcp_label = QString("%1,%2,%3,%4,%5,,,%6,%7,,\n")
.arg(split_type)
.arg(bucket_path)
.arg(label.classname)
.arg(QString::number(static_cast<double>(label.rect.topLeft().x())/width))
.arg(QString::number(static_cast<double>(label.rect.topLeft().y())/height))
.arg(QString::number(static_cast<double>(label.rect.bottomRight().x())/width))
.arg(QString::number(static_cast<double>(label.rect.bottomRight().x())/height));
qDebug() << gcp_label;
f.write(gcp_label.toUtf8());
}
if(!disable_progress){
progress.setValue(i++);
progress.setLabelText(image_filename);
QApplication::processEvents();
}
}
return true;
}
bool GCPExporter::setOutputFolder(const QString folder){
if(folder == "") return false;
output_folder = folder;
//Make output folder if it doesn't exist
if (!QDir(output_folder).exists()){
qInfo() << "Making output folder" << output_folder;
QDir().mkpath(output_folder);
}
image_folder = QDir::cleanPath(output_folder+"/images");
if (!QDir(image_folder).exists()){
qInfo() << "Making validation folder" << image_folder;
QDir().mkpath(image_folder);
}
image_folder = QDir::cleanPath(image_folder);
return true;
}
void GCPExporter::setBucket(QString uri, bool local){
if(!uri.startsWith("gs://") && !local){
bucket_uri = QString("gs://%1").arg(uri);
}else{
bucket_uri = uri;
}
}
void GCPExporter::process(){
processImages(image_folder, "labels", train_set, EXPORT_TRAIN);
processImages(image_folder, "labels", validation_set, EXPORT_VAL);
}
================================================
FILE: src/gcpexporter.h
================================================
#ifndef GCPEXPORTER_H
#define GCPEXPORTER_H
#include<baseexporter.h>
class GCPExporter : public BaseExporter
{
Q_OBJECT
public:
explicit GCPExporter(LabelProject *project, QObject *parent = nullptr) : BaseExporter(project, parent){}
public slots:
void process();
void setBucket(QString uri, bool local=false);
bool setOutputFolder(const QString folder);
private:
bool processImages(const QString output_folder, QString filename, const QList<QString> images, export_image_type split_type=EXPORT_TRAIN);
QString bucket_uri;
QString image_folder;
};
#endif // GCPEXPORTER_H
================================================
FILE: src/imagedisplay.cpp
================================================
#include "imagedisplay.h"
#include "ui_imagedisplay.h"
std::unordered_map<std::string ,int> ImageDisplay::colour_hashmap{
{ "Cividis", cv::COLORMAP_CIVIDIS },
{ "Inferno", cv::COLORMAP_INFERNO },
{ "Magma", cv::COLORMAP_MAGMA },
{ "Hot", cv::COLORMAP_HOT },
{ "Bone", cv::COLORMAP_BONE },
{ "Plasma", cv::COLORMAP_PLASMA },
{ "Jet", cv::COLORMAP_JET },
{ "Rainbow", cv::COLORMAP_RAINBOW },
{ "Ocean", cv::COLORMAP_OCEAN },
{ "Viridis", cv::COLORMAP_VIRIDIS }
};
ImageDisplay::ImageDisplay(QWidget *parent) :
QWidget(parent),
ui(new Ui::ImageDisplay)
{
ui->setupUi(this);
imageLabel = new ImageLabel;
scrollArea = new QScrollArea(this);
ui->mainLayout->addWidget(scrollArea);
scrollArea->setWidget(imageLabel);
scrollArea->setAlignment(Qt::AlignCenter);
scrollArea->setBackgroundRole(QPalette::Shadow);
connect(ui->fitToWindowButton, SIGNAL(clicked()), this, SLOT(updateDisplay()));
connect(ui->resetZoomButton, SIGNAL(clicked()), this, SLOT(resetZoom()));
connect(ui->zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));
connect(ui->zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
connect(ui->zoomSpinBox, SIGNAL(valueChanged(int)), this, SLOT(scaleImage()));
QtAwesome* awesome = new QtAwesome( qApp );
awesome->initFontAwesome();
ui->zoomInButton->setIcon(awesome->icon(fa::searchplus));
ui->zoomOutButton->setIcon(awesome->icon(fa::searchminus));
ui->resetZoomButton->setIcon(awesome->icon(fa::home));
updateDisplay();
}
ImageDisplay::~ImageDisplay()
{
delete ui;
}
void ImageDisplay::setImagePath(QString path){
if(path != ""){
current_imagepath = path;
loadPixmap();
}
}
void ImageDisplay::clearPixmap(void){
pixmap = QPixmap();
imageLabel->setPixmap(pixmap);
}
void ImageDisplay::convert16(cv::Mat &source, double minval, double maxval){
if(minval < 0 || maxval < 0){
cv::minMaxIdx(source, &minval, &maxval);
}
double range = maxval-minval;
double scale_factor = 255.0/range;
source.convertTo(source, CV_32FC1);
source -= minval;
source *= scale_factor;
source.convertTo(source, CV_8UC1);
return;
}
cv::Mat ImageDisplay::getOriginalImage(void){
return original_image;
}
void ImageDisplay::loadPixmap(){
if(current_imagepath == "")
return;
original_image = cv::imread(current_imagepath.toStdString(), cv::IMREAD_UNCHANGED|cv::IMREAD_ANYDEPTH);
if(original_image.empty()){
qCritical() << "Failed to load image " << current_imagepath;
return;
}
display_image = original_image.clone();
// Default to single channel 8-bit image
format = QImage::Format_Grayscale8;
bit_depth = 8;
if(original_image.elemSize() == 2){
convert16(display_image);
bit_depth = 16;
}
if(display_image.channels() == 3){
cv::cvtColor(display_image, display_image, cv::COLOR_BGR2RGB);
format = QImage::Format_RGB888;
bit_depth = 24;
}else if (display_image.channels() == 4){
cv::cvtColor(display_image, display_image, cv::COLOR_BGRA2RGBA);
format = QImage::Format_RGBA8888;
bit_depth = 32;
}else if(display_image.channels() == 1 && apply_colourmap){
cv::applyColorMap(display_image, display_image, colour_map);
cv::cvtColor(display_image, display_image, cv::COLOR_BGR2RGB);
format = QImage::Format_RGB888;
}
pixmap = QPixmap::fromImage(QImage(display_image.data, display_image.cols, display_image.rows, static_cast<int>(display_image.step), format));
imageLabel->setImage(display_image);
updateDisplay();
emit image_loaded();
}
void ImageDisplay::setColourMap(QString map){
auto map_str = map.toStdString();
if(colour_hashmap.count(map_str)){
colour_map = colour_hashmap[map_str];
loadPixmap();
}
}
void ImageDisplay::toggleColourMap(bool enable){
apply_colourmap = enable;
loadPixmap();
}
void ImageDisplay::adjustScrollBar(QScrollBar *scrollBar, double factor)
{
scrollBar->setValue(int(factor * scrollBar->value()
+ ((factor - 1) * scrollBar->pageStep()/2)));
}
void ImageDisplay::scaleImage(void)
{
if(pixmap.isNull()) return;
image_scale_factor = static_cast<double>(ui->zoomSpinBox->value()) / 100.0;
imageLabel->zoom(image_scale_factor);
adjustScrollBar(scrollArea->horizontalScrollBar(), image_scale_factor);
adjustScrollBar(scrollArea->verticalScrollBar(), image_scale_factor);
ui->zoomInButton->setEnabled(image_scale_factor <= 3.0);
ui->zoomOutButton->setEnabled(image_scale_factor >= 0.33);
}
void ImageDisplay::zoomIn()
{
if(fit_to_window) return;
ui->zoomSpinBox->setValue(ui->zoomSpinBox->value()*1.2);
}
void ImageDisplay::zoomOut()
{
if(fit_to_window) return;
ui->zoomSpinBox->setValue(ui->zoomSpinBox->value()*0.8);
}
void ImageDisplay::resetZoom(){
ui->zoomSpinBox->setValue(100);
}
void ImageDisplay::updateDisplay()
{
if(pixmap.isNull()) return;
fit_to_window = ui->fitToWindowButton->isChecked();
// Decide whether to use scrollbars
scrollArea->setWidgetResizable(fit_to_window);
// Auto-scale the image to the size of the scrollarea,
// or leave it full-size
imageLabel->setScaledContents(fit_to_window);
imageLabel->setPixmap(pixmap);
repaint();
}
================================================
FILE: src/imagedisplay.h
================================================
#ifndef IMAGEDISPLAY_H
#define IMAGEDISPLAY_H
#include <QWidget>
#include <QScrollArea>
#include <QScrollBar>
#include <QTemporaryDir>
#include <QApplication>
#include <QtAwesome.h>
#include <opencv2/opencv.hpp>
#include <imagelabel.h>
#include <unordered_map>
namespace Ui {
class ImageDisplay;
}
class ImageDisplay : public QWidget
{
Q_OBJECT
public:
explicit ImageDisplay(QWidget *parent = nullptr);
~ImageDisplay();
public slots:
void setImagePath(QString path);
ImageLabel* getImageLabel(void){return imageLabel;}
int getBitDepth(void){return bit_depth;}
void setColourMap(QString map);
void toggleColourMap(bool enable);
cv::Mat getOriginalImage();
void clearPixmap();
private:
cv::Mat display_image;
cv::Mat original_image;
QPixmap pixmap;
Ui::ImageDisplay *ui;
QString current_imagepath;
void convert16(cv::Mat &source, double minval=-1, double maxval=-1);
ImageLabel* imageLabel;
QScrollArea* scrollArea;
bool fit_to_window = false;
double image_scale_factor = 1.0;
bool apply_colourmap = true;
int colour_map = cv::COLORMAP_MAGMA;
QImage::Format format = QImage::Format_Grayscale8;
int bit_depth = 8;
static std::unordered_map<std::string, int> colour_hashmap;
private slots:
void loadPixmap();
void updateDisplay();
void resetZoom();
void scaleImage();
void adjustScrollBar(QScrollBar *scrollBar, double factor);
void zoomIn();
void zoomOut();
signals:
void image_loaded();
};
#endif // IMAGEDISPLAY_H
================================================
FILE: src/imagedisplay.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImageDisplay</class>
<widget class="QWidget" name="ImageDisplay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="mainLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="fitToWindowButton">
<property name="text">
<string>Fit to window</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetZoomButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="zoomOutButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="zoomInButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="zoomSpinBox">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>33</number>
</property>
<property name="maximum">
<number>300</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
================================================
FILE: src/imagelabel.cpp
================================================
#include "imagelabel.h"
ImageLabel::ImageLabel(QWidget *parent) :
QLabel(parent)
{
setMinimumSize(1,1);
setAlignment(Qt::AlignCenter);
setMouseTracking(true);
// This is important to preserve the aspect ratio
setScaledContents(true);
// Dark background
setBackgroundRole(QPalette::Shadow);
// Size policy
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
setFocusPolicy(Qt::StrongFocus);
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
setDrawMode();
}
void ImageLabel::setPixmap ( QPixmap & p)
{
base_pixmap = p;
drawLabels();
QPixmap pixmap;
resize(pixmap.size());
}
int ImageLabel::heightForWidth( int width ) const
{
return base_pixmap.isNull() ? height() : static_cast<int>((static_cast<qreal>(base_pixmap.height())*width)/base_pixmap.width());
}
QSize ImageLabel::sizeHint() const
{
int w = width();
return QSize( w, heightForWidth(w) );
}
void ImageLabel::resizeEvent(QResizeEvent * e)
{
if(!base_pixmap.isNull()){
drawLabels();
}else{
e->ignore();
}
}
void ImageLabel::zoom(double factor){
zoom_factor = factor;
scaledPixmap();
if(zoom_factor == 1.0){
resize(base_pixmap.size());
}else{
resize(scaled_pixmap.size());
}
}
QPixmap ImageLabel::scaledPixmap(void)
{
if(base_pixmap.isNull())
return QPixmap();
if(shouldScaleContents){
scaled_pixmap = base_pixmap.scaled( size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}else if(zoom_factor != 1.0){
scaled_pixmap = base_pixmap.scaled( zoom_factor*base_pixmap.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}else{
scaled_pixmap = base_pixmap;
}
scaled_width = scaled_pixmap.width();
scaled_height = scaled_pixmap.height();
scale_factor = static_cast<float>(scaled_height)/base_pixmap.height();
return scaled_pixmap;
}
void ImageLabel::setDrawMode()
{
current_mode = MODE_DRAW;
setCursor(Qt::CrossCursor);
rubberBand->setGeometry(QRect(bbox_origin, QSize()));
rubberBand->show();
setBoundingBoxes(bboxes);
}
void ImageLabel::setDrawDragMode()
{
current_mode = MODE_DRAW_DRAG;
rubberBand->setGeometry(QRect(bbox_origin, QSize()));
rubberBand->show();
setBoundingBoxes(bboxes);
}
void ImageLabel::setSelectMode(){
current_mode = MODE_SELECT;
setCursor(Qt::PointingHandCursor);
rubberBand->hide();
setBoundingBoxes(bboxes);
}
QPoint ImageLabel::getScaledImageLocation(QPoint location){
// If the image is fit to window
QPoint scaled_location = location;
if(scale_factor != 1.0){
// Get the location on the image, accounting for padding
scaled_location.setX(scaled_location.x() - (width() - scaled_width)/2);
scaled_location.setY(scaled_location.y() - (height() - scaled_height)/2);
scaled_location.setX(static_cast<int>(scaled_location.x() / scale_factor));
scaled_location.setY(static_cast<int>(scaled_location.y() / scale_factor));
}
return scaled_location;
}
void ImageLabel::mousePressEvent(QMouseEvent *ev){
if(base_pixmap.isNull()) return;
QPoint click_location = ev->pos();
if (current_mode == MODE_SELECT && ev->button() == Qt::LeftButton) {
bool clicked = true;
bool state_change = false;
auto active_box = checkBoundingBoxes(click_location, state_change, clicked);
if (state_change) {
drawLabels();
}
if (active_box.label_id > 0) {
edit_state = EDITING_BOX;
editing_bbox = active_box;
selected_bbox = active_box;
initial_edit_centre = click_location;
} else {
selected_bbox = BoundingBox();
edit_state = EDIT_WAIT;
}
} else if (current_mode == MODE_DRAW && ev->button() == Qt::LeftButton) {
if (bbox_state == WAIT_START) {
bbox_origin = click_location;
rubberBand->setGeometry(QRect(bbox_origin, QSize()));
rubberBand->show();
bbox_state = DRAWING_BBOX;
} else if (bbox_state == DRAWING_BBOX) {
bbox_final = click_location;
QRect bbox(bbox_origin, bbox_final);
bbox = clip(bbox.normalized());
bbox_origin = bbox.topLeft();
bbox_final = bbox.bottomRight();
rubberBand->setGeometry(bbox);
bbox_state = WAIT_START;
}
}
}
void ImageLabel::mouseReleaseEvent(QMouseEvent *ev)
{
if (current_mode == MODE_SELECT && edit_state == EDITING_BOX) {
auto temp = editing_bbox;
editing_bbox = BoundingBox();
updateLabel(selected_bbox, temp);
edit_state = EDIT_WAIT;
setBoundingBoxes(bboxes);
} else {
ev->ignore();
}
}
void ImageLabel::mouseMoveEvent(QMouseEvent *ev)
{
if (base_pixmap.isNull())
return;
if (bbox_state == DRAWING_BBOX && current_mode == MODE_DRAW) {
QRect bbox = QRect(bbox_origin, ev->pos()).normalized();
rubberBand->setGeometry(bbox);
}
if (current_mode == MODE_SELECT) {
if (edit_state == EDIT_WAIT) {
bool state_change;
checkBoundingBoxes(ev->pos(), state_change);
if (state_change) {
drawLabels();
}
} else if (edit_state == EDITING_BOX) {
editBoxCoords(editing_bbox, ev->pos());
drawLabels();
}
}
}
void ImageLabel::editBoxCoords(BoundingBox &bbox, QPoint location)
{
auto scaled_location = getScaledImageLocation(location);
// Edge cases
if (bbox.selected_edge == EDGE_TOP) {
bbox.rect.setTop(scaled_location.y());
} else if (bbox.selected_edge == EDGE_BOTTOM) {
bbox.rect.setBottom(scaled_location.y());
} else if (bbox.selected_edge == EDGE_LEFT) {
bbox.rect.setLeft(scaled_location.x());
} else if (bbox.selected_edge == EDGE_RIGHT) {
bbox.rect.setRight(scaled_location.x());
}
// Corner cases
if (bbox.selected_corner == CORNER_BOTTOMLEFT) {
bbox.rect.setBottomLeft(scaled_location);
} else if (bbox.selected_corner == CORNER_BOTTOMRIGHT) {
bbox.rect.setBottomRight(scaled_location);
} else if (bbox.selected_corner == CORNER_TOPLEFT) {
bbox.rect.setTopLeft(scaled_location);
} else if (bbox.selected_corner == CORNER_TOPRIGHT) {
bbox.rect.setTopRight(scaled_location);
}
if (bbox.is_selected) {
int width = bbox.rect.width();
int height = bbox.rect.height();
auto delta = scaled_location - getScaledImageLocation(initial_edit_centre);
bbox.rect.setTopLeft(
{bbox.rect.topLeft().x() + delta.x(), bbox.rect.topLeft().y() + delta.y()});
bbox.rect.setWidth(width);
bbox.rect.setHeight(height);
initial_edit_centre = location;
}
bbox.rect = bbox.rect.normalized();
}
bool ImageLabel::isSelected(BoundingBox bbox, QPoint location, int padding)
{
auto rect = QRect(QPoint(bbox.rect.topLeft().x() + padding, bbox.rect.topLeft().y() + padding),
QPoint(bbox.rect.bottomRight().x() - padding,
bbox.rect.bottomRight().y() - padding));
return rect.contains(location);
}
BoundingBox ImageLabel::checkBoundingBoxes(QPoint location, bool &state_change, bool click)
{
state_change = false;
bool something_selected = false;
BoundingBox active;
for (auto &bbox : bboxes) {
// Check for selected (centre hitbox)
if (click) {
auto prev_select = bbox.is_selected;
bool selected = isSelected(bbox, getScaledImageLocation(location));
if (selected && !something_selected) {
bbox.is_selected = true;
selected_bbox = bbox;
active = bbox;
something_selected = true;
emit setCurrentClass(selected_bbox.classname);
} else {
bbox.is_selected = false;
}
state_change |= (prev_select == bbox.is_selected);
}
// Check for selected edge (edge hitboxes)
auto prev_edge = bbox.selected_edge;
bbox.selected_edge = getSelectedEdge(bbox, getScaledImageLocation(location));
if (bbox.selected_edge != EDGE_NONE) {
active = bbox;
}
state_change |= (prev_edge == bbox.selected_edge);
// Check for selected corner (corner hitboxes)
auto prev_corner = bbox.selected_corner;
bbox.selected_corner = getSelectedCorner(bbox, getScaledImageLocation(location));
if (bbox.selected_corner != CORNER_NONE) {
active = bbox;
}
state_change |= (prev_corner == bbox.selected_corner);
}
return active;
}
QRect ImageLabel::clip(QRect bbox){
auto xpad = (width() - scaled_width)/2;
bbox.setLeft(std::max(xpad, bbox.left()));
bbox.setRight(std::min(width()-xpad, bbox.right()));
auto ypad = (height() - scaled_height)/2;
bbox.setTop(std::max(ypad, bbox.top()));
bbox.setBottom(std::min(height()-ypad, bbox.bottom()));
return bbox;
}
void ImageLabel::drawBoundingBox(BoundingBox bbox)
{
auto colour_list = QColor::colorNames();
QColor colour = QColor(colour_list.at(std::max(0, bbox.classid) % colour_list.size()));
drawBoundingBox(bbox, colour);
}
QRect ImageLabel::getPaddedRectangle(QPoint location, int pad)
{
return QRect(location.x() - pad, location.y() - pad, 2 * pad, 2 * pad);
}
void ImageLabel::drawBoundingBox(BoundingBox bbox, QColor colour)
{
if (scaled_pixmap.isNull())
return;
QPainter painter;
painter.begin(&scaled_pixmap);
QPen pen(colour, 2);
painter.setPen(pen);
auto scaled_bbox = bbox.rect;
scaled_bbox.setRight(static_cast<int>(scaled_bbox.right() * scale_factor));
scaled_bbox.setLeft(static_cast<int>(scaled_bbox.left() * scale_factor));
scaled_bbox.setTop(static_cast<int>(scaled_bbox.top() * scale_factor));
scaled_bbox.setBottom(static_cast<int>(scaled_bbox.bottom() * scale_factor));
if(bbox.classname != ""){
//painter.fillRect(QRect(scaled_bbox.bottomLeft(), scaled_bbox.bottomRight()+QPoint(0,-10)).normalized(), QBrush(Qt::white));
painter.setFont(QFont("Helvetica", 10));
painter.drawText(scaled_bbox.bottomLeft(), bbox.classname);
}
painter.drawRect(scaled_bbox);
if (current_mode == MODE_SELECT) {
QPen pen(Qt::white, 4);
painter.setPen(pen);
painter.drawRect(getPaddedRectangle(scaled_bbox.topLeft(), 5));
painter.drawRect(getPaddedRectangle(scaled_bbox.topRight(), 5));
painter.drawRect(getPaddedRectangle(scaled_bbox.bottomLeft(), 5));
painter.drawRect(getPaddedRectangle(scaled_bbox.bottomRight(), 5));
if (bbox.selected_edge != EDGE_NONE) {
QPen pen(Qt::red, 4);
painter.setPen(pen);
if (bbox.selected_edge == EDGE_LEFT) {
painter.drawLine(scaled_bbox.topLeft(), scaled_bbox.bottomLeft());
} else if (bbox.selected_edge == EDGE_RIGHT) {
painter.drawLine(scaled_bbox.topRight(), scaled_bbox.bottomRight());
} else if (bbox.selected_edge == EDGE_BOTTOM) {
painter.drawLine(scaled_bbox.bottomRight(), scaled_bbox.bottomLeft());
} else {
painter.drawLine(scaled_bbox.topLeft(), scaled_bbox.topRight());
}
}
if (bbox.selected_corner != CORNER_NONE) {
QPen pen(Qt::red, 4);
painter.setPen(pen);
if (bbox.selected_corner == CORNER_TOPLEFT) {
painter.drawRect(getPaddedRectangle(scaled_bbox.topLeft(), 5));
} else if (bbox.selected_corner == CORNER_TOPRIGHT) {
painter.drawRect(getPaddedRectangle(scaled_bbox.topRight(), 5));
} else if (bbox.selected_corner == CORNER_BOTTOMLEFT) {
painter.drawRect(getPaddedRectangle(scaled_bbox.bottomLeft(), 5));
} else {
painter.drawRect(getPaddedRectangle(scaled_bbox.bottomRight(), 5));
}
}
}
painter.end();
}
void ImageLabel::setBoundingBoxes(QList<BoundingBox> input_bboxes){
bboxes.clear();
bboxes = input_bboxes;
drawLabels();
}
void ImageLabel::setClassname(QString classname)
{
current_classname = classname;
if(selected_bbox.classname != classname){
auto new_bbox = selected_bbox;
new_bbox.classname = classname;
emit updateLabel(selected_bbox, new_bbox);
}
}
void ImageLabel::setScaledContents(bool should_scale){
shouldScaleContents = should_scale;
if(!shouldScaleContents){
scale_factor = 1.0;
}
}
bool ImageLabel::scaleContents(void){
return shouldScaleContents;
}
SelectedEdge ImageLabel::getSelectedEdge(BoundingBox bbox, QPoint location)
{
auto edge = EDGE_NONE;
auto rect = bbox.rect;
auto left_hitbox = QRect(QPoint(rect.topLeft().x() - point_threshold,
rect.topLeft().y() + point_threshold),
QPoint(rect.bottomLeft().x() + point_threshold,
rect.bottomLeft().y() - point_threshold));
auto top_hitbox = QRect(QPoint(rect.topLeft().x() + point_threshold,
rect.topLeft().y() - point_threshold),
QPoint(rect.topRight().x() - point_threshold,
rect.topRight().y() + point_threshold));
auto right_hitbox = QRect(QPoint(rect.topRight().x() - point_threshold,
rect.topRight().y() + point_threshold),
QPoint(rect.bottomRight().x() + point_threshold,
rect.bottomRight().y() - point_threshold));
auto bottom_hitbox = QRect(QPoint(rect.bottomLeft().x() + point_threshold,
rect.bottomLeft().y() - point_threshold),
QPoint(rect.bottomRight().x() - point_threshold,
rect.bottomRight().y() + point_threshold));
bool proper = true;
if (left_hitbox.contains(location, proper)) {
edge = EDGE_LEFT;
} else if (right_hitbox.contains(location, proper)) {
edge = EDGE_RIGHT;
} else if (top_hitbox.contains(location, proper)) {
edge = EDGE_TOP;
} else if (bottom_hitbox.contains(location, proper)) {
edge = EDGE_BOTTOM;
}
return edge;
}
double ImageLabel::pointDistance(QPoint p1, QPoint p2)
{
auto l = QLine(p1, p2);
return std::sqrt(std::pow(l.dx(), 2) + std::pow(l.dy(), 2));
}
SelectedCorner ImageLabel::getSelectedCorner(BoundingBox bbox, QPoint location)
{
auto corner = CORNER_NONE;
auto rect = bbox.rect;
auto top_left_dist = pointDistance(rect.topLeft(), location);
auto top_right_dist = pointDistance(rect.topRight(), location);
auto bottom_left_dist = pointDistance(rect.bottomLeft(), location);
auto bottom_right_dist = pointDistance(rect.bottomRight(), location);
if (top_left_dist < point_threshold) {
corner = CORNER_TOPLEFT;
} else if (top_right_dist < point_threshold) {
corner = CORNER_TOPRIGHT;
} else if (bottom_left_dist < point_threshold) {
corner = CORNER_BOTTOMLEFT;
} else if (bottom_right_dist < point_threshold) {
corner = CORNER_BOTTOMRIGHT;
}
return corner;
}
void ImageLabel::drawLabels(QPoint cursor_location)
{
scaledPixmap();
if (scale_factor == 1.0) {
scaled_pixmap = base_pixmap;
}
BoundingBox bbox;
auto colour_list = QColor::colorNames();
foreach (bbox, bboxes) {
if (bbox.label_id == editing_bbox.label_id) {
drawBoundingBox(editing_bbox, Qt::red);
} else {
if (bbox.label_id == selected_bbox.label_id) {
drawBoundingBox(bbox, Qt::green);
} else {
drawBoundingBox(bbox);
}
}
}
QLabel::setPixmap(scaled_pixmap);
}
void ImageLabel::addLabel(QRect rect, QString classname){
BoundingBox new_bbox;
new_bbox.classname = classname;
new_bbox.rect = rect;
bboxes.append(new_bbox);
emit newLabel(new_bbox);
drawLabels();
}
void ImageLabel::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete){
emit removeLabel(selected_bbox);
} else if (event->key() == Qt::Key_Escape) {
rubberBand->setGeometry(QRect(bbox_origin, QSize()));
bbox_state = WAIT_START;
selected_bbox = BoundingBox();
bool state;
checkBoundingBoxes({-1, -1}, state, true);
drawLabels();
} else if (event->key() == Qt::Key_Space && bbox_state == WAIT_START) {
if(rubberBand->width() > 0 && rubberBand->height() > 0){
if(current_classname == ""){
qWarning() << "No class selected!";
}else{
QRect bbox_rect;
bbox_rect = QRect(bbox_origin, bbox_final);
bbox_rect.setTopLeft(getScaledImageLocation(bbox_rect.topLeft()));
bbox_rect.setBottomRight(getScaledImageLocation(bbox_rect.bottomRight()));
addLabel(bbox_rect, current_classname);
rubberBand->setGeometry(QRect(bbox_origin, QSize()));
}
}else if(event->key() == 'o'){
emit setOccluded(selected_bbox);
drawLabels();
}
} else {
event->ignore();
}
}
================================================
FILE: src/imagelabel.h
================================================
#ifndef IMAGELABEL_H
#define IMAGELABEL_H
#include <QLabel>
#include <QPixmap>
#include <QPainter>
#include <QDebug>
#include <QRubberBand>
#include <QResizeEvent>
#include <opencv2/opencv.hpp>
#include<boundingbox.h>
enum drawState {
WAIT_START,
DRAWING_BBOX,
};
enum interactionState {
MODE_DRAW,
MODE_DRAW_DRAG,
MODE_EDIT,
MODE_MOVE,
MODE_SELECT,
};
enum editState { EDIT_WAIT, EDIT_START, EDITING_BOX, MOVING_BOX };
class
gitextract_q_bu5e9m/
├── .github/
│ └── workflows/
│ ├── build_osx.yml
│ ├── build_ubuntu.yml
│ └── build_windows.yml
├── .gitignore
├── .gitmodules
├── DeepLabel.pro
├── README.md
├── deploy_mac.sh
├── fix_paths_mac.py
└── src/
├── baseexporter.cpp
├── baseexporter.h
├── baseimporter.cpp
├── baseimporter.h
├── birdsaiimporter.cpp
├── birdsaiimporter.h
├── boundingbox.h
├── cliparser.cpp
├── cliparser.h
├── cliprogressbar.h
├── cocoexporter.cpp
├── cocoexporter.h
├── cocoimporter.cpp
├── cocoimporter.h
├── crc32.cpp
├── crc32.h
├── darknetexporter.cpp
├── darknetexporter.h
├── darknetimporter.cpp
├── darknetimporter.h
├── detection/
│ ├── detectoropencv.cpp
│ ├── detectoropencv.h
│ ├── detectorsetupdialog.cpp
│ ├── detectorsetupdialog.h
│ └── detectorsetupdialog.ui
├── exportdialog.cpp
├── exportdialog.h
├── exportdialog.ui
├── exporter.h
├── gcpexporter.cpp
├── gcpexporter.h
├── imagedisplay.cpp
├── imagedisplay.h
├── imagedisplay.ui
├── imagelabel.cpp
├── imagelabel.h
├── importdialog.cpp
├── importdialog.h
├── importdialog.ui
├── importer.h
├── kittiexporter.cpp
├── kittiexporter.h
├── labelproject.cpp
├── labelproject.h
├── labelrefinedialog.cpp
├── labelrefinedialog.h
├── labelrefinedialog.ui
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── motimporter.cpp
├── motimporter.h
├── multitracker.cpp
├── multitracker.h
├── pascalvocexporter.cpp
├── pascalvocexporter.h
├── pascalvocimporter.cpp
├── pascalvocimporter.h
├── proto/
│ ├── example.pb.cc
│ ├── example.pb.h
│ ├── example.proto
│ ├── feature.pb.cc
│ ├── feature.pb.h
│ └── feature.proto
├── refinerangedialog.cpp
├── refinerangedialog.h
├── refinerangedialog.ui
├── tfrecordexporter.cpp
├── tfrecordexporter.h
├── tfrecordimporter.cpp
├── tfrecordimporter.h
├── videoexporter.cpp
└── videoexporter.h
SYMBOL INDEX (308 symbols across 50 files)
FILE: fix_paths_mac.py
function file_in_folder (line 14) | def file_in_folder(file, folder):
function otool (line 17) | def otool(s):
FILE: src/baseexporter.h
type export_image_type (line 16) | enum export_image_type{
function class (line 23) | class BaseExporter : public QObject
FILE: src/baseimporter.h
function class (line 11) | class BaseImporter : public QObject
FILE: src/birdsaiimporter.h
function class (line 6) | class BirdsAIImporter : public MOTImporter
FILE: src/boundingbox.h
type SelectedEdge (line 7) | enum SelectedEdge {
type SelectedCorner (line 15) | enum SelectedCorner {
type BoundingBox (line 23) | struct BoundingBox{
function QString (line 40) | inline QString printBoundingBox(BoundingBox box){
FILE: src/cliparser.h
function class (line 11) | class CliParser : public QObject
FILE: src/cliprogressbar.h
function class (line 8) | class cliProgressBar {
function print (line 14) | void print() {
FILE: src/cocoexporter.cpp
function foreach (line 48) | foreach(image_path, images){
FILE: src/cocoexporter.h
function class (line 10) | class CocoExporter : public BaseExporter
FILE: src/cocoimporter.h
function class (line 9) | class CocoImporter : public BaseImporter
FILE: src/crc32.cpp
type tf_crc (line 4) | namespace tf_crc{
function LE_LOAD32 (line 184) | static inline uint32_t LE_LOAD32(const uint8_t *p) {
function Extend (line 192) | uint32_t Extend(uint32_t crc, const char *buf, size_t size) {
FILE: src/crc32.h
function namespace (line 19) | namespace tf_crc{
FILE: src/darknetexporter.cpp
function clamp (line 32) | double clamp(double val, double min, double max){
function foreach (line 48) | foreach(label, labels){
function foreach (line 108) | foreach(image_path, images){
FILE: src/darknetexporter.h
function class (line 7) | class DarknetExporter : public BaseExporter
FILE: src/darknetimporter.h
function class (line 6) | class DarknetImporter : public BaseImporter
FILE: src/detection/detectoropencv.cpp
function model_framework (line 8) | model_framework DetectorOpenCV::frameworkFromString(QString framework_st...
FILE: src/detection/detectoropencv.h
type model_framework (line 20) | typedef enum {
function class (line 25) | class DetectorOpenCV
FILE: src/detection/detectorsetupdialog.cpp
function QString (line 194) | QString DetectorSetupDialog::openFile(QString title, QString search_path...
FILE: src/detection/detectorsetupdialog.h
function namespace (line 14) | namespace Ui {
function class (line 18) | class DetectorSetupDialog : public QDialog
FILE: src/exportdialog.cpp
function QString (line 74) | QString ExportDialog::getFilePrefix(void){
FILE: src/exportdialog.h
function namespace (line 11) | namespace Ui {
function class (line 15) | class ExportDialog : public QDialog
FILE: src/gcpexporter.cpp
function foreach (line 40) | foreach(image_path, images){
FILE: src/gcpexporter.h
function class (line 6) | class GCPExporter : public BaseExporter
FILE: src/imagedisplay.h
function namespace (line 16) | namespace Ui {
function class (line 20) | class ImageDisplay : public QWidget
FILE: src/imagelabel.cpp
function QSize (line 41) | QSize ImageLabel::sizeHint() const
function QPixmap (line 68) | QPixmap ImageLabel::scaledPixmap(void)
function QPoint (line 114) | QPoint ImageLabel::getScaledImageLocation(QPoint location){
function BoundingBox (line 271) | BoundingBox ImageLabel::checkBoundingBoxes(QPoint location, bool &state_...
function QRect (line 316) | QRect ImageLabel::clip(QRect bbox){
function QRect (line 338) | QRect ImageLabel::getPaddedRectangle(QPoint location, int pad)
function SelectedEdge (line 445) | SelectedEdge ImageLabel::getSelectedEdge(BoundingBox bbox, QPoint location)
function SelectedCorner (line 490) | SelectedCorner ImageLabel::getSelectedCorner(BoundingBox bbox, QPoint lo...
function foreach (line 524) | foreach (bbox, bboxes) {
FILE: src/imagelabel.h
type drawState (line 15) | enum drawState {
type interactionState (line 20) | enum interactionState {
type editState (line 28) | enum editState { EDIT_WAIT, EDIT_START, EDITING_BOX, MOVING_BOX }
function class (line 30) | class ImageLabel : public QLabel
FILE: src/importdialog.h
function namespace (line 12) | namespace Ui {
function class (line 16) | class ImportDialog : public QDialog
FILE: src/kittiexporter.cpp
function foreach (line 37) | foreach(label, labels){
function foreach (line 75) | foreach(image, images){
FILE: src/kittiexporter.h
function class (line 6) | class KittiExporter : public BaseExporter
FILE: src/labelproject.cpp
function QString (line 444) | QString LabelProject::getClassName(int classID){
function foreach (line 941) | foreach (image_info, image_list) {
function QSqlDatabase (line 1026) | QSqlDatabase LabelProject::getDatabase()
FILE: src/labelproject.h
function class (line 25) | class LabelProject : public QObject
FILE: src/labelrefinedialog.h
function namespace (line 6) | namespace Ui {
function class (line 10) | class LabelRefineDialog : public QDialog
FILE: src/main.cpp
function main (line 5) | int main(int argc, char *argv[])
FILE: src/mainwindow.cpp
function foreach (line 472) | foreach(classname, classes){
function QRect (line 611) | QRect MainWindow::refineBoundingBoxSimple(cv::Mat image, QRect bbox, int...
function QRect (line 672) | QRect MainWindow::refineBoundingBox(cv::Mat image, QRect bbox, int margi...
function foreach (line 997) | foreach(path, image_filenames){
FILE: src/mainwindow.h
function namespace (line 34) | namespace Ui {
function class (line 38) | class MainWindow : public QMainWindow
FILE: src/motimporter.h
function class (line 8) | class MOTImporter : public BaseImporter
FILE: src/multitracker.cpp
function qrect2cv (line 3) | cv::Rect2d qrect2cv(QRect rect){
FILE: src/multitracker.h
type OpenCVTrackerType (line 11) | enum OpenCVTrackerType {BOOSTING, MIL, KCF, TLD, MEDIANFLOW, GOTURN, MOS...
type MultiTrackerType (line 12) | enum MultiTrackerType {OPENCV, CAMSHIFT}
function class (line 16) | class MultiTracker : public QObject
function class (line 34) | class MultiTrackerCV : public MultiTracker
function class (line 53) | class MultiTrackerCamshift : public MultiTracker
FILE: src/pascalvocexporter.cpp
function foreach (line 157) | foreach(image_path, images){
FILE: src/pascalvocexporter.h
function class (line 14) | class PascalVocExporter : public BaseExporter
FILE: src/pascalvocimporter.h
function class (line 7) | class PascalVOCImporter : public BaseImporter
FILE: src/proto/example.pb.cc
function PROTOBUF_PRAGMA_INIT_SEG (line 18) | PROTOBUF_PRAGMA_INIT_SEG
function descriptor_table_example_2eproto_metadata_getter (line 96) | PROTOBUF_ATTRIBUTE_WEAK ::PROTOBUF_NAMESPACE_ID::Metadata
type tensorflow (line 104) | namespace tensorflow {
class Example::_Internal (line 108) | class Example::_Internal {
class SequenceExample::_Internal (line 318) | class SequenceExample::_Internal {
function PROTOBUF_NAMESPACE_OPEN (line 582) | PROTOBUF_NAMESPACE_OPEN
FILE: src/proto/example.pb.h
function PROTOBUF_NAMESPACE_OPEN (line 38) | PROTOBUF_NAMESPACE_OPEN
function PROTOBUF_NAMESPACE_CLOSE (line 42) | PROTOBUF_NAMESPACE_CLOSE
function namespace (line 58) | namespace tensorflow {
function PROTOBUF_NAMESPACE_CLOSE (line 69) | PROTOBUF_NAMESPACE_CLOSE
function PROTOBUF_NAMESPACE_ID (line 100) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 103) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 106) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function Example (line 109) | static const Example& default_instance() {
function Example (line 112) | static inline const Example* internal_default_instance() {
function Swap (line 122) | inline void Swap(Example* other) {
function UnsafeArenaSwap (line 130) | void UnsafeArenaSwap(Example* other) {
function Example (line 138) | inline Example* New() const final {
function final (line 142) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 149) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 164) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function tensorflow (line 195) | const ::tensorflow::Features& features() const;
function PROTOBUF_NAMESPACE_ID (line 246) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 249) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 252) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function SequenceExample (line 255) | static const SequenceExample& default_instance() {
function SequenceExample (line 258) | static inline const SequenceExample* internal_default_instance() {
function Swap (line 268) | inline void Swap(SequenceExample* other) {
function UnsafeArenaSwap (line 276) | void UnsafeArenaSwap(SequenceExample* other) {
function SequenceExample (line 284) | inline SequenceExample* New() const final {
function final (line 288) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 295) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 310) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function tensorflow (line 402) | inline const ::tensorflow::Features& Example::_internal_features() const {
function unsafe_arena_set_allocated_features (line 411) | inline void Example::unsafe_arena_set_allocated_features(
function tensorflow (line 424) | inline ::tensorflow::Features* Example::release_features() {
function tensorflow (line 433) | inline ::tensorflow::Features* Example::unsafe_arena_release_features() {
function tensorflow (line 440) | inline ::tensorflow::Features* Example::_internal_mutable_features() {
function tensorflow (line 448) | inline ::tensorflow::Features* Example::mutable_features() {
function set_allocated_features (line 452) | inline void Example::set_allocated_features(::tensorflow::Features* feat...
function tensorflow (line 483) | inline const ::tensorflow::Features& SequenceExample::_internal_context(...
function unsafe_arena_set_allocated_context (line 492) | inline void SequenceExample::unsafe_arena_set_allocated_context(
function tensorflow (line 505) | inline ::tensorflow::Features* SequenceExample::release_context() {
function tensorflow (line 514) | inline ::tensorflow::Features* SequenceExample::unsafe_arena_release_con...
function tensorflow (line 521) | inline ::tensorflow::Features* SequenceExample::_internal_mutable_contex...
function tensorflow (line 529) | inline ::tensorflow::Features* SequenceExample::mutable_context() {
function set_allocated_context (line 533) | inline void SequenceExample::set_allocated_context(::tensorflow::Feature...
function tensorflow (line 560) | inline const ::tensorflow::FeatureLists& SequenceExample::_internal_feat...
function unsafe_arena_set_allocated_feature_lists (line 569) | inline void SequenceExample::unsafe_arena_set_allocated_feature_lists(
function tensorflow (line 582) | inline ::tensorflow::FeatureLists* SequenceExample::release_feature_list...
function tensorflow (line 591) | inline ::tensorflow::FeatureLists* SequenceExample::unsafe_arena_release...
function tensorflow (line 598) | inline ::tensorflow::FeatureLists* SequenceExample::_internal_mutable_fe...
function tensorflow (line 606) | inline ::tensorflow::FeatureLists* SequenceExample::mutable_feature_list...
function set_allocated_feature_lists (line 610) | inline void SequenceExample::set_allocated_feature_lists(::tensorflow::F...
FILE: src/proto/feature.pb.cc
function PROTOBUF_PRAGMA_INIT_SEG (line 18) | PROTOBUF_PRAGMA_INIT_SEG
function descriptor_table_feature_2eproto_metadata_getter (line 250) | PROTOBUF_ATTRIBUTE_WEAK ::PROTOBUF_NAMESPACE_ID::Metadata
type tensorflow (line 258) | namespace tensorflow {
class BytesList::_Internal (line 262) | class BytesList::_Internal {
class FloatList::_Internal (line 456) | class FloatList::_Internal {
class Int64List::_Internal (line 653) | class Int64List::_Internal {
class Feature::_Internal (line 854) | class Feature::_Internal {
class Features::_Internal (line 1235) | class Features::_Internal {
type Utf8Check (line 1339) | struct Utf8Check {
method Check (line 1340) | static void Check(ConstPtr p) {
method Check (line 1780) | static void Check(ConstPtr p) {
class FeatureList::_Internal (line 1464) | class FeatureList::_Internal {
class FeatureLists::_Internal (line 1675) | class FeatureLists::_Internal {
type Utf8Check (line 1779) | struct Utf8Check {
method Check (line 1340) | static void Check(ConstPtr p) {
method Check (line 1780) | static void Check(ConstPtr p) {
function PROTOBUF_NAMESPACE_OPEN (line 1904) | PROTOBUF_NAMESPACE_OPEN
FILE: src/proto/feature.pb.h
function PROTOBUF_NAMESPACE_OPEN (line 40) | PROTOBUF_NAMESPACE_OPEN
function PROTOBUF_NAMESPACE_CLOSE (line 44) | PROTOBUF_NAMESPACE_CLOSE
function namespace (line 60) | namespace tensorflow {
function PROTOBUF_NAMESPACE_CLOSE (line 99) | PROTOBUF_NAMESPACE_CLOSE
function PROTOBUF_NAMESPACE_ID (line 130) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 133) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 136) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function BytesList (line 139) | static const BytesList& default_instance() {
function BytesList (line 142) | static inline const BytesList* internal_default_instance() {
function Swap (line 152) | inline void Swap(BytesList* other) {
function UnsafeArenaSwap (line 160) | void UnsafeArenaSwap(BytesList* other) {
function BytesList (line 168) | inline BytesList* New() const final {
function final (line 172) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 179) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 194) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function PROTOBUF_NAMESPACE_ID (line 236) | const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& value() co...
function PROTOBUF_NAMESPACE_ID (line 282) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 285) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 288) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function FloatList (line 291) | static const FloatList& default_instance() {
function FloatList (line 294) | static inline const FloatList* internal_default_instance() {
function Swap (line 304) | inline void Swap(FloatList* other) {
function UnsafeArenaSwap (line 312) | void UnsafeArenaSwap(FloatList* other) {
function FloatList (line 320) | inline FloatList* New() const final {
function final (line 324) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 331) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 346) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function PROTOBUF_NAMESPACE_ID (line 379) | const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >&
function PROTOBUF_NAMESPACE_ID (line 433) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 436) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 439) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function Int64List (line 442) | static const Int64List& default_instance() {
function Int64List (line 445) | static inline const Int64List* internal_default_instance() {
function Swap (line 455) | inline void Swap(Int64List* other) {
function UnsafeArenaSwap (line 463) | void UnsafeArenaSwap(Int64List* other) {
function Int64List (line 471) | inline Int64List* New() const final {
function final (line 475) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 482) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 497) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function PROTOBUF_NAMESPACE_ID (line 530) | const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::i...
function PROTOBUF_NAMESPACE_ID (line 584) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 587) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 590) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function Feature (line 593) | static const Feature& default_instance() {
type KindCase (line 596) | enum KindCase {
function Feature (line 603) | static inline const Feature* internal_default_instance() {
function Swap (line 613) | inline void Swap(Feature* other) {
function UnsafeArenaSwap (line 621) | void UnsafeArenaSwap(Feature* other) {
function Feature (line 629) | inline Feature* New() const final {
function final (line 633) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 640) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 655) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function tensorflow (line 688) | const ::tensorflow::BytesList& bytes_list() const;
function PROTOBUF_NAMESPACE_ID (line 823) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 826) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 829) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function Features (line 832) | static const Features& default_instance() {
function Features (line 835) | static inline const Features* internal_default_instance() {
function Swap (line 845) | inline void Swap(Features* other) {
function UnsafeArenaSwap (line 853) | void UnsafeArenaSwap(Features* other) {
function Features (line 861) | inline Features* New() const final {
function final (line 865) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 872) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 887) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
type InternalArenaConstructable_ (line 935) | typedef void InternalArenaConstructable_;
type DestructorSkippable_ (line 936) | typedef void DestructorSkippable_;
function PROTOBUF_NAMESPACE_ID (line 973) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 976) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 979) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function FeatureList (line 982) | static const FeatureList& default_instance() {
function FeatureList (line 985) | static inline const FeatureList* internal_default_instance() {
function Swap (line 995) | inline void Swap(FeatureList* other) {
function UnsafeArenaSwap (line 1003) | void UnsafeArenaSwap(FeatureList* other) {
function FeatureList (line 1011) | inline FeatureList* New() const final {
function final (line 1015) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 1022) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 1037) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
function PROTOBUF_NAMESPACE_ID (line 1077) | const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature >&
function PROTOBUF_NAMESPACE_ID (line 1151) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
function PROTOBUF_NAMESPACE_ID (line 1154) | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
function PROTOBUF_NAMESPACE_ID (line 1157) | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
function FeatureLists (line 1160) | static const FeatureLists& default_instance() {
function FeatureLists (line 1163) | static inline const FeatureLists* internal_default_instance() {
function Swap (line 1173) | inline void Swap(FeatureLists* other) {
function UnsafeArenaSwap (line 1181) | void UnsafeArenaSwap(FeatureLists* other) {
function FeatureLists (line 1189) | inline FeatureLists* New() const final {
function final (line 1193) | const final {
function PROTOBUF_ATTRIBUTE_REINITIALIZES (line 1200) | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
function PROTOBUF_NAMESPACE_ID (line 1215) | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
type InternalArenaConstructable_ (line 1263) | typedef void InternalArenaConstructable_;
type DestructorSkippable_ (line 1264) | typedef void DestructorSkippable_;
function clear_value (line 1291) | inline void BytesList::clear_value() {
function std (line 1294) | inline std::string* BytesList::add_value() {
function std (line 1298) | inline const std::string& BytesList::_internal_value(int index) const {
function std (line 1301) | inline const std::string& BytesList::value(int index) const {
function std (line 1305) | inline std::string* BytesList::mutable_value(int index) {
function set_value (line 1309) | inline void BytesList::set_value(int index, const std::string& value) {
function set_value (line 1313) | inline void BytesList::set_value(int index, std::string&& value) {
function set_value (line 1317) | inline void BytesList::set_value(int index, const char* value) {
function set_value (line 1322) | inline void BytesList::set_value(int index, const void* value, size_t si...
function std (line 1327) | inline std::string* BytesList::_internal_add_value() {
function add_value (line 1330) | inline void BytesList::add_value(const std::string& value) {
function add_value (line 1334) | inline void BytesList::add_value(std::string&& value) {
function add_value (line 1338) | inline void BytesList::add_value(const char* value) {
function add_value (line 1343) | inline void BytesList::add_value(const void* value, size_t size) {
function PROTOBUF_NAMESPACE_ID (line 1352) | inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
function clear_value (line 1369) | inline void FloatList::clear_value() {
function _internal_value (line 1372) | inline float FloatList::_internal_value(int index) const {
function value (line 1375) | inline float FloatList::value(int index) const {
function set_value (line 1379) | inline void FloatList::set_value(int index, float value) {
function _internal_add_value (line 1383) | inline void FloatList::_internal_add_value(float value) {
function add_value (line 1386) | inline void FloatList::add_value(float value) {
function PROTOBUF_NAMESPACE_ID (line 1390) | inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >&
function PROTOBUF_NAMESPACE_ID (line 1403) | inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >*
function clear_value (line 1420) | inline void Int64List::clear_value() {
function set_value (line 1430) | inline void Int64List::set_value(int index, ::PROTOBUF_NAMESPACE_ID::int...
function _internal_add_value (line 1434) | inline void Int64List::_internal_add_value(::PROTOBUF_NAMESPACE_ID::int6...
function add_value (line 1437) | inline void Int64List::add_value(::PROTOBUF_NAMESPACE_ID::int64 value) {
function PROTOBUF_NAMESPACE_ID (line 1441) | inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPAC...
function PROTOBUF_NAMESPACE_ID (line 1454) | inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::...
function set_has_bytes_list (line 1471) | inline void Feature::set_has_bytes_list() {
function clear_bytes_list (line 1474) | inline void Feature::clear_bytes_list() {
function tensorflow (line 1482) | inline ::tensorflow::BytesList* Feature::release_bytes_list() {
function tensorflow (line 1496) | inline const ::tensorflow::BytesList& Feature::_internal_bytes_list() co...
function tensorflow (line 1505) | inline ::tensorflow::BytesList* Feature::unsafe_arena_release_bytes_list...
function unsafe_arena_set_allocated_bytes_list (line 1516) | inline void Feature::unsafe_arena_set_allocated_bytes_list(::tensorflow:...
function tensorflow (line 1524) | inline ::tensorflow::BytesList* Feature::_internal_mutable_bytes_list() {
function tensorflow (line 1532) | inline ::tensorflow::BytesList* Feature::mutable_bytes_list() {
function set_has_float_list (line 1544) | inline void Feature::set_has_float_list() {
function clear_float_list (line 1547) | inline void Feature::clear_float_list() {
function tensorflow (line 1555) | inline ::tensorflow::FloatList* Feature::release_float_list() {
function tensorflow (line 1569) | inline const ::tensorflow::FloatList& Feature::_internal_float_list() co...
function tensorflow (line 1578) | inline ::tensorflow::FloatList* Feature::unsafe_arena_release_float_list...
function unsafe_arena_set_allocated_float_list (line 1589) | inline void Feature::unsafe_arena_set_allocated_float_list(::tensorflow:...
function tensorflow (line 1597) | inline ::tensorflow::FloatList* Feature::_internal_mutable_float_list() {
function tensorflow (line 1605) | inline ::tensorflow::FloatList* Feature::mutable_float_list() {
function set_has_int64_list (line 1617) | inline void Feature::set_has_int64_list() {
function clear_int64_list (line 1620) | inline void Feature::clear_int64_list() {
function tensorflow (line 1628) | inline ::tensorflow::Int64List* Feature::release_int64_list() {
function tensorflow (line 1642) | inline const ::tensorflow::Int64List& Feature::_internal_int64_list() co...
function tensorflow (line 1651) | inline ::tensorflow::Int64List* Feature::unsafe_arena_release_int64_list...
function unsafe_arena_set_allocated_int64_list (line 1662) | inline void Feature::unsafe_arena_set_allocated_int64_list(::tensorflow:...
function tensorflow (line 1670) | inline ::tensorflow::Int64List* Feature::_internal_mutable_int64_list() {
function tensorflow (line 1678) | inline ::tensorflow::Int64List* Feature::mutable_int64_list() {
function clear_has_kind (line 1686) | inline void Feature::clear_has_kind() {
function clear_feature (line 1705) | inline void Features::clear_feature() {
function clear_feature (line 1738) | inline void FeatureList::clear_feature() {
function tensorflow (line 1741) | inline ::tensorflow::Feature* FeatureList::mutable_feature(int index) {
function PROTOBUF_NAMESPACE_ID (line 1745) | inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tensorflow::Feature >*
function tensorflow (line 1750) | inline const ::tensorflow::Feature& FeatureList::_internal_feature(int i...
function tensorflow (line 1753) | inline const ::tensorflow::Feature& FeatureList::feature(int index) const {
function tensorflow (line 1757) | inline ::tensorflow::Feature* FeatureList::_internal_add_feature() {
function tensorflow (line 1760) | inline ::tensorflow::Feature* FeatureList::add_feature() {
function clear_feature_list (line 1783) | inline void FeatureLists::clear_feature_list() {
FILE: src/refinerangedialog.h
function namespace (line 7) | namespace Ui {
function class (line 11) | class RefineRangeDialog : public QDialog
FILE: src/tfrecordexporter.cpp
function foreach (line 235) | foreach(image_path, images){
FILE: src/tfrecordexporter.h
function class (line 12) | class TFRecordExporter : public BaseExporter
FILE: src/tfrecordimporter.h
function class (line 11) | class TFRecordImporter : public BaseImporter
FILE: src/videoexporter.h
function class (line 7) | class VideoExporter : public BaseExporter
Condensed preview — 83 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (605K chars).
[
{
"path": ".github/workflows/build_osx.yml",
"chars": 821,
"preview": "name: Build OS X\n\non: [push]\n\njobs:\n build:\n runs-on: macos-latest\n steps:\n - uses: actions/checkout@v2\n "
},
{
"path": ".github/workflows/build_ubuntu.yml",
"chars": 3314,
"preview": "name: Build Ubuntu\n\non: [push]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v2\n "
},
{
"path": ".github/workflows/build_windows.yml",
"chars": 4073,
"preview": "name: Build Windows\n\non: [push]\n\njobs:\n build:\n runs-on: windows-latest\n steps:\n - uses: actions/checkout@v2"
},
{
"path": ".gitignore",
"chars": 66,
"preview": "build/\n*.pro.user*\nMakefile\nui_*\n.moc\n.obj\n*.stash\nopencv\nprotobuf"
},
{
"path": ".gitmodules",
"chars": 95,
"preview": "[submodule \"QtAwesome\"]\n\tpath = QtAwesome\n\turl = https://github.com/gamecreature/QtAwesome.git\n"
},
{
"path": "DeepLabel.pro",
"chars": 5802,
"preview": "#-------------------------------------------------\n#\n# Project created by QtCreator 2017-10-04T17:31:00\n#\n#-------------"
},
{
"path": "README.md",
"chars": 18453,
"preview": "# DeepLabel\n\n[](ht"
},
{
"path": "deploy_mac.sh",
"chars": 256,
"preview": "#!/usr/bin/env bash\n\nbuild_folder=$1\napp_path=$build_folder/DeepLabel.app\n\nmacdeployqt $app_path\npython fix_paths_mac.py"
},
{
"path": "fix_paths_mac.py",
"chars": 2187,
"preview": "import subprocess\nimport os\nimport sys\nfrom shutil import copyfile\n\nexecutable = sys.argv[1]\napp_folder = os.path.join(*"
},
{
"path": "src/baseexporter.cpp",
"chars": 3890,
"preview": "#include \"baseexporter.h\"\n\nBaseExporter::BaseExporter(LabelProject *project, QObject *parent) : QObject(parent)\n{\n th"
},
{
"path": "src/baseexporter.h",
"chars": 1672,
"preview": "#ifndef BASEEXPORTER_H\n#define BASEEXPORTER_H\n\n#include <QObject>\n#include <QDateTime>\n#include <QImageReader>\n#include "
},
{
"path": "src/baseimporter.cpp",
"chars": 849,
"preview": "#include \"baseimporter.h\"\n\nBaseImporter::BaseImporter(QObject *parent) : QObject(parent)\n{\n\n}\n\nvoid BaseImporter::setImp"
},
{
"path": "src/baseimporter.h",
"chars": 675,
"preview": "#ifndef BASEIMPORTER_H\n#define BASEIMPORTER_H\n\n#include <QObject>\n#include <QFile>\n#include <QRegularExpression>\n#includ"
},
{
"path": "src/birdsaiimporter.cpp",
"chars": 3487,
"preview": "#include \"birdsaiimporter.h\"\n\nvoid BirdsAIImporter::import(QString sequence_folder, QString annotation_folder){\n QDir"
},
{
"path": "src/birdsaiimporter.h",
"chars": 464,
"preview": "#ifndef BIRDSAIIMPORTER_H\n#define BIRDSAIIMPORTER_H\n\n#include \"motimporter.h\"\n\nclass BirdsAIImporter : public MOTImporte"
},
{
"path": "src/boundingbox.h",
"chars": 996,
"preview": "#ifndef BOUNDINGBOX_H\n#define BOUNDINGBOX_H\n\n#include <QRect>\n#include <QString>\n\nenum SelectedEdge {\n EDGE_LEFT,\n "
},
{
"path": "src/cliparser.cpp",
"chars": 16523,
"preview": "#include \"cliparser.h\"\n\nCliParser::CliParser(QObject *parent) : QObject(parent)\n{\n SetupOptions();\n}\n\nvoid CliParser:"
},
{
"path": "src/cliparser.h",
"chars": 2090,
"preview": "#ifndef CLIPARSER_H\n#define CLIPARSER_H\n\n#include <QObject>\n#include <QCommandLineParser>\n#include <exporter.h>\n#include"
},
{
"path": "src/cliprogressbar.h",
"chars": 1535,
"preview": "#ifndef CLIPROGRESSBAR_H\n#define CLIPROGRESSBAR_H\n\n#include <iostream>\n\n// Thanks to https://github.com/GregoryConrad/pB"
},
{
"path": "src/cocoexporter.cpp",
"chars": 6137,
"preview": "#include \"cocoexporter.h\"\n\nbool CocoExporter::processImages(const QString folder, const QString label_filename, const QL"
},
{
"path": "src/cocoexporter.h",
"chars": 493,
"preview": "#ifndef COCOEXPORTER_H\n#define COCOEXPORTER_H\n\n#include <baseexporter.h>\n\n#include <QJsonDocument>\n#include <QJsonArray>"
},
{
"path": "src/cocoimporter.cpp",
"chars": 4224,
"preview": "#include \"cocoimporter.h\"\n\nvoid CocoImporter::import(QString annotation_file, QString image_folder){\n // Load Json\n "
},
{
"path": "src/cocoimporter.h",
"chars": 461,
"preview": "#ifndef COCOIMPORTER_H\n#define COCOIMPORTER_H\n\n#include<baseimporter.h>\n#include <QJsonDocument>\n#include <QJsonArray>\n#"
},
{
"path": "src/crc32.cpp",
"chars": 15066,
"preview": "#include \"crc32.h\"\n#include <stdint.h>\n\nnamespace tf_crc{\n\nstatic const uint32_t table0_[256] = {\n 0x00000000, 0xf26b"
},
{
"path": "src/crc32.h",
"chars": 2073,
"preview": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"Lice"
},
{
"path": "src/darknetexporter.cpp",
"chars": 5853,
"preview": "#include \"darknetexporter.h\"\n\nvoid DarknetExporter::generateLabelIds(const QString names_file){\n id_map.clear();\n\n "
},
{
"path": "src/darknetexporter.h",
"chars": 669,
"preview": "#ifndef DARKNETEXPORTER_H\n#define DARKNETEXPORTER_H\n\n#include <baseexporter.h>\n#include <algorithm>\n\nclass DarknetExport"
},
{
"path": "src/darknetimporter.cpp",
"chars": 3844,
"preview": "#include \"darknetimporter.h\"\n\nvoid DarknetImporter::import(QString image_list, QString names_file, QString root_folder)\n"
},
{
"path": "src/darknetimporter.h",
"chars": 525,
"preview": "#ifndef DARKNETIMPORTER_H\n#define DARKNETIMPORTER_H\n\n#include<baseimporter.h>\n\nclass DarknetImporter : public BaseImport"
},
{
"path": "src/detection/detectoropencv.cpp",
"chars": 17427,
"preview": "#include \"detectoropencv.h\"\n\nDetectorOpenCV::DetectorOpenCV()\n{\n\n}\n\nmodel_framework DetectorOpenCV::frameworkFromString("
},
{
"path": "src/detection/detectoropencv.h",
"chars": 3015,
"preview": "#ifndef DETECTOROPENCV_H\n#define DETECTOROPENCV_H\n\n#include<iostream>\n#include<fstream>\n#include<string>\n#include<vector"
},
{
"path": "src/detection/detectorsetupdialog.cpp",
"chars": 9225,
"preview": "#include \"detectorsetupdialog.h\"\n#include \"ui_detectorsetupdialog.h\"\n\nDetectorSetupDialog::DetectorSetupDialog(QWidget *"
},
{
"path": "src/detection/detectorsetupdialog.h",
"chars": 1657,
"preview": "#ifndef DETECTORSETUPDIALOG_H\n#define DETECTORSETUPDIALOG_H\n\n#include <QDialog>\n#include <QFileDialog>\n#include <QSettin"
},
{
"path": "src/detection/detectorsetupdialog.ui",
"chars": 6797,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DetectorSetupDialog</class>\n <widget class=\"QDialog\" n"
},
{
"path": "src/exportdialog.cpp",
"chars": 6838,
"preview": "#include \"exportdialog.h\"\n#include \"ui_exportdialog.h\"\n\nExportDialog::ExportDialog(QWidget *parent) :\n QDialog(parent"
},
{
"path": "src/exportdialog.h",
"chars": 1712,
"preview": "#ifndef EXPORTDIALOG_H\n#define EXPORTDIALOG_H\n\n#include <QDialog>\n#include <QFile>\n#include <QDir>\n#include <QFileDialog"
},
{
"path": "src/exportdialog.ui",
"chars": 6959,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ExportDialog</class>\n <widget class=\"QDialog\" name=\"Ex"
},
{
"path": "src/exporter.h",
"chars": 256,
"preview": "#ifndef EXPORTER_H\n#define EXPORTER_H\n\n#include <kittiexporter.h>\n#include <darknetexporter.h>\n#include <pascalvocexport"
},
{
"path": "src/gcpexporter.cpp",
"chars": 4740,
"preview": "#include \"gcpexporter.h\"\n\nbool GCPExporter::processImages(const QString folder,\n const QS"
},
{
"path": "src/gcpexporter.h",
"chars": 608,
"preview": "#ifndef GCPEXPORTER_H\n#define GCPEXPORTER_H\n\n#include<baseexporter.h>\n\nclass GCPExporter : public BaseExporter\n{\n Q_O"
},
{
"path": "src/imagedisplay.cpp",
"chars": 5449,
"preview": "#include \"imagedisplay.h\"\n#include \"ui_imagedisplay.h\"\n\nstd::unordered_map<std::string ,int> ImageDisplay::colour_hashma"
},
{
"path": "src/imagedisplay.h",
"chars": 1558,
"preview": "#ifndef IMAGEDISPLAY_H\n#define IMAGEDISPLAY_H\n\n#include <QWidget>\n#include <QScrollArea>\n#include <QScrollBar>\n#include "
},
{
"path": "src/imagedisplay.ui",
"chars": 3242,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ImageDisplay</class>\n <widget class=\"QWidget\" name=\"Im"
},
{
"path": "src/imagelabel.cpp",
"chars": 17733,
"preview": "#include \"imagelabel.h\"\n\nImageLabel::ImageLabel(QWidget *parent) :\n QLabel(parent)\n{\n setMinimumSize(1,1);\n set"
},
{
"path": "src/imagelabel.h",
"chars": 3124,
"preview": "#ifndef IMAGELABEL_H\n#define IMAGELABEL_H\n\n\n#include <QLabel>\n#include <QPixmap>\n#include <QPainter>\n#include <QDebug>\n#"
},
{
"path": "src/importdialog.cpp",
"chars": 10777,
"preview": "#include \"importdialog.h\"\n#include \"ui_importdialog.h\"\n\nImportDialog::ImportDialog(QWidget *parent) :\n QDialog(parent"
},
{
"path": "src/importdialog.h",
"chars": 1465,
"preview": "#ifndef IMPORTDIALOG_H\n#define IMPORTDIALOG_H\n\n#include <QDialog>\n#include <QPushButton>\n#include <QFile>\n#include <QDir"
},
{
"path": "src/importdialog.ui",
"chars": 5435,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ImportDialog</class>\n <widget class=\"QDialog\" name=\"Im"
},
{
"path": "src/importer.h",
"chars": 231,
"preview": "#ifndef IMPORTER_H\n#define IMPORTER_H\n\n#include <darknetimporter.h>\n#include <motimporter.h>\n#include <birdsaiimporter.h"
},
{
"path": "src/kittiexporter.cpp",
"chars": 3757,
"preview": "#include \"kittiexporter.h\"\n\nbool KittiExporter::setOutputFolder(QString folder){\n\n if(folder == \"\") return false;\n\n "
},
{
"path": "src/kittiexporter.h",
"chars": 534,
"preview": "#ifndef KITTIEXPORTER_H\n#define KITTIEXPORTER_H\n\n#include <baseexporter.h>\n\nclass KittiExporter : public BaseExporter\n{\n"
},
{
"path": "src/labelproject.cpp",
"chars": 29339,
"preview": "#include \"labelproject.h\"\n\nLabelProject::LabelProject(QObject *parent) : QObject(parent)\n{\n connect(this, SIGNAL(vide"
},
{
"path": "src/labelproject.h",
"chars": 2634,
"preview": "#ifndef LABELPROJECT_H\n#define LABELPROJECT_H\n\n#include <QObject>\n#include <QApplication>\n#include <QDir>\n#include <QDir"
},
{
"path": "src/labelrefinedialog.cpp",
"chars": 264,
"preview": "#include \"labelrefinedialog.h\"\n#include \"ui_labelrefinedialog.h\"\n\nLabelRefineDialog::LabelRefineDialog(QWidget *parent) "
},
{
"path": "src/labelrefinedialog.h",
"chars": 345,
"preview": "#ifndef LABELREFINEDIALOG_H\n#define LABELREFINEDIALOG_H\n\n#include <QDialog>\n\nnamespace Ui {\nclass LabelRefineDialog;\n}\n\n"
},
{
"path": "src/labelrefinedialog.ui",
"chars": 9318,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>LabelRefineDialog</class>\n <widget class=\"QDialog\" nam"
},
{
"path": "src/main.cpp",
"chars": 473,
"preview": "#include \"mainwindow.h\"\n#include \"cliparser.h\"\n#include <QApplication>\n\nint main(int argc, char *argv[])\n{\n QCoreAppl"
},
{
"path": "src/mainwindow.cpp",
"chars": 41830,
"preview": "#include \"mainwindow.h\"\n#include \"ui_mainwindow.h\"\n\nMainWindow::MainWindow(QWidget *parent) :\n QMainWindow(parent),\n "
},
{
"path": "src/mainwindow.h",
"chars": 3485,
"preview": "#ifndef MAINWINDOW_H\n#define MAINWINDOW_H\n\n#include <QMainWindow>\n#include <QThread>\n#include <QFileDialog>\n#include <QD"
},
{
"path": "src/mainwindow.ui",
"chars": 21528,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\""
},
{
"path": "src/motimporter.cpp",
"chars": 5254,
"preview": "#include \"motimporter.h\"\n\nvoid MOTImporter::importSequence(QString folder){\n\n auto ini_filename = QDir(folder).absolu"
},
{
"path": "src/motimporter.h",
"chars": 727,
"preview": "#ifndef MOTIMPORTER_H\n#define MOTIMPORTER_H\n\n#include \"baseimporter.h\"\n#include <QSettings>\n#include <QTextCodec>\n\nclass"
},
{
"path": "src/multitracker.cpp",
"chars": 5719,
"preview": "#include \"multitracker.h\"\n\ncv::Rect2d qrect2cv(QRect rect){\n return cv::Rect2d(rect.x(), rect.y(), rect.width(),rect."
},
{
"path": "src/multitracker.h",
"chars": 1813,
"preview": "#ifndef TRACKER_H\n#define TRACKER_H\n\n#include <QObject>\n#include <QtConcurrent/qtconcurrentmap.h>\n#include <boundingbox."
},
{
"path": "src/pascalvocexporter.cpp",
"chars": 7800,
"preview": "#include \"pascalvocexporter.h\"\n\nvoid PascalVocExporter::splitData(float split, bool shuffle, int seed){\n\n if(split < "
},
{
"path": "src/pascalvocexporter.h",
"chars": 1594,
"preview": "#ifndef PASCALVOCEXPORTER_H\n#define PASCALVOCEXPORTER_H\n\n#include <baseexporter.h>\n#include <QObject>\n#include <QImageRe"
},
{
"path": "src/pascalvocimporter.cpp",
"chars": 2865,
"preview": "#include \"pascalvocimporter.h\"\n\nvoid PascalVOCImporter::import(QString image_folder, QString annotation_folder){\n\n QD"
},
{
"path": "src/pascalvocimporter.h",
"chars": 531,
"preview": "#ifndef PASCALVOCIMPORTER_H\n#define PASCALVOCIMPORTER_H\n\n#include \"baseimporter.h\"\n#include <QDomDocument>\n\nclass Pascal"
},
{
"path": "src/proto/example.pb.cc",
"chars": 21903,
"preview": "// Generated by the protocol buffer compiler. DO NOT EDIT!\n// source: example.proto\n\n#include \"example.pb.h\"\n\n#include "
},
{
"path": "src/proto/example.pb.h",
"chars": 22970,
"preview": "// Generated by the protocol buffer compiler. DO NOT EDIT!\n// source: example.proto\n\n#ifndef GOOGLE_PROTOBUF_INCLUDED_e"
},
{
"path": "src/proto/example.proto",
"chars": 10880,
"preview": "// Protocol messages for describing input data Examples for machine learning\n// model training or inference.\nsyntax = \"p"
},
{
"path": "src/proto/feature.pb.cc",
"chars": 72284,
"preview": "// Generated by the protocol buffer compiler. DO NOT EDIT!\n// source: feature.proto\n\n#include \"feature.pb.h\"\n\n#include "
},
{
"path": "src/proto/feature.pb.h",
"chars": 65771,
"preview": "// Generated by the protocol buffer compiler. DO NOT EDIT!\n// source: feature.proto\n\n#ifndef GOOGLE_PROTOBUF_INCLUDED_f"
},
{
"path": "src/proto/feature.proto",
"chars": 2628,
"preview": "// Protocol messages for describing features for machine learning model\n// training or inference.\n//\n// There are three "
},
{
"path": "src/refinerangedialog.cpp",
"chars": 1117,
"preview": "#include \"refinerangedialog.h\"\n#include \"ui_refinerangedialog.h\"\n\nRefineRangeDialog::RefineRangeDialog(QWidget *parent) "
},
{
"path": "src/refinerangedialog.h",
"chars": 525,
"preview": "#ifndef REFINERANGEDIALOG_H\n#define REFINERANGEDIALOG_H\n\n#include <QDialog>\n#include <QPushButton>\n\nnamespace Ui {\nclass"
},
{
"path": "src/refinerangedialog.ui",
"chars": 1995,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RefineRangeDialog</class>\n <widget class=\"QDialog\" nam"
},
{
"path": "src/tfrecordexporter.cpp",
"chars": 9771,
"preview": "#include \"tfrecordexporter.h\"\n\nvoid TFRecordExporter::generateLabelIds(const QString names_file){\n id_map.clear();\n\n "
},
{
"path": "src/tfrecordexporter.h",
"chars": 1248,
"preview": "#ifndef TFRECORDEXPORTER_H\n#define TFRECORDEXPORTER_H\n\n#include \"baseexporter.h\"\n\n#include <proto/example.pb.h>\n#include"
},
{
"path": "src/tfrecordimporter.cpp",
"chars": 5168,
"preview": "#include \"tfrecordimporter.h\"\n\nvoid TFRecordImporter::find_example_boundaries(QByteArray buffer, std::vector<size_t> &re"
},
{
"path": "src/tfrecordimporter.h",
"chars": 788,
"preview": "#ifndef TFRECORDIMPORTER_H\n#define TFRECORDIMPORTER_H\n\n#include \"baseimporter.h\"\n\n#include <proto/example.pb.h>\n#include"
},
{
"path": "src/videoexporter.cpp",
"chars": 5214,
"preview": "#include \"videoexporter.h\"\n\nstd::unordered_map<std::string ,int> VideoExporter::colour_hashmap{\n { \"Cividis\", cv::COL"
},
{
"path": "src/videoexporter.h",
"chars": 1120,
"preview": "#ifndef VIDEOEXPORTER_H\n#define VIDEOEXPORTER_H\n\n#include \"baseexporter.h\"\n#include <unordered_map>\n\nclass VideoExporter"
}
]
About this extraction
This page contains the full source code of the jveitchmichaelis/deeplabel GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 83 files (564.5 KB), approximately 144.4k tokens, and a symbol index with 308 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.