diff --git a/.gitignore b/.gitignore index 2244ce63..e5f6867c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,32 @@ -# Build files -/CMakeCache.txt -/CMakeFiles/ -/Makefile -/cmake_install.cmake -/yolo_mark \ No newline at end of file +*.o +*.a +*.dSYM +*.csv +*.out +*.png +*.so +*.exe +*.dll +*.lib +build_*/ +*.weights + +# OS Generated # +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db +*.swp + +# IDE generated # +.vs/ +.vscode/ + +#Build artifacts +yolo_mark + +#Examples +data/* +data +examples/ + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7b70f353 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,70 @@ +language: cpp +cache: + timeout: 1000 + directories: + - $HOME/vcpkg + +matrix: + include: + + - os: osx + compiler: clang + name: macOS - native clang - opencv@3 + env: + - OpenCV_DIR="/usr/local/opt/opencv@3/" + - additional_defines="-DOpenCV_DIR=${OpenCV_DIR}" + - MATRIX_EVAL="brew install opencv@3" + + - os: osx + compiler: clang + name: macOS - native clang - opencv(latest) + env: + - additional_defines="" + - MATRIX_EVAL="brew install opencv" + + - os: osx + name: macOS - vcpkg + osx_image: xcode10.1 + env: + - additional_defines="" + - MATRIX_EVAL="brew install gcc && unset CC && unset CXX" + - USE_VCPKG=true + - VCPKG_DEFINES="-DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake" + + - os: linux + dist: bionic + compiler: gcc + name: ubuntu 18.04 - gcc - vcpkg + env: + - additional_defines="" + - USE_VCPKG=true + - VCPKG_DEFINES="-DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake" + - MATRIX_EVAL="" + +before_install: + - travis_retry eval "${MATRIX_EVAL}" + +install: + # CMake upgrade on Linux + - if [[ "$TRAVIS_OS_NAME" == "linux" ]] ; then wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.13.4/cmake-3.13.4-Linux-x86_64.tar.gz ; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]] ; then tar -xzf cmake-3.13.4-Linux-x86_64.tar.gz ; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]] ; then export PATH=$PWD/cmake-3.13.4-Linux-x86_64/bin:$PATH ; fi + - pushd $HOME + - if [ -d "$HOME/vcpkg/.git" ] ; then echo vcpkg cached ; else rm -rf vcpkg ; git clone https://github.com/Microsoft/vcpkg ; fi + - cd vcpkg + - git pull + - if [ "${USE_VCPKG}" = true ] ; then ./bootstrap-vcpkg.sh ; fi + - if [ "${USE_VCPKG}" = true ] ; then echo "set(VCPKG_BUILD_TYPE release)" >> triplets/x64-osx.cmake ; fi + - if [ "${USE_VCPKG}" = true ] ; then echo "set(VCPKG_BUILD_TYPE release)" >> triplets/x64-linux.cmake ; fi + - if [ "${USE_VCPKG}" = true ] ; then travis_wait 45 ./vcpkg install opencv[contrib] --recurse ; fi + - popd + +before_script: + - echo ${additional_defines} + - mkdir build_release + - cd build_release + - cmake .. -DCMAKE_BUILD_TYPE="Release" ${VCPKG_DEFINES} ${additional_defines} + - cd .. + +script: + - cd build_release && cmake --build . --target install -- -j8 && cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c1875bd..a933d8fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,57 @@ -cmake_minimum_required(VERSION 2.8) -project( yolo_mark CXX ) -find_package( OpenCV REQUIRED ) +cmake_minimum_required(VERSION 3.2) -set( sources main.cpp ) +set(Yolo_mark_MAJOR_VERSION 1) +set(Yolo_mark_MINOR_VERSION 0) +set(Yolo_mark_PATCH_VERSION 0) +set(Yolo_mark_VERSION ${Yolo_mark_MAJOR_VERSION}.${Yolo_mark_MINOR_VERSION}.${Yolo_mark_PATCH_VERSION}) -add_executable( yolo_mark ${sources} ) +set(CMAKE_VERBOSE_MAKEFILE "FALSE" CACHE BOOL "Create verbose makefile") -target_compile_options( yolo_mark PUBLIC -std=c++11 -fpermissive -w -Wall ) +if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") + message(STATUS "VCPKG found: $ENV{VCPKG_ROOT}") + message(STATUS "Using VCPKG integration") +endif() -target_link_libraries( yolo_mark ${OpenCV_LIBS} -L/usr/lib64 -ldl ) +project(Yolo_mark VERSION ${Yolo_mark_VERSION}) + +enable_language(CXX) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules/" ${CMAKE_MODULE_PATH}) + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Install prefix" FORCE) +endif() +set(INSTALL_BIN_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Path where exe will be installed") + +find_package(OpenCV REQUIRED) + +if(TARGET opencv_world) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_world") +else() + if(TARGET opencv_video) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_video") + endif() + if(TARGET opencv_videoio) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_videoio") + endif() + if(TARGET opencv_highgui) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_highgui") + endif() + if(TARGET opencv_imgproc) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_imgproc") + endif() + if(TARGET opencv_imgcodecs) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_imgcodecs") + endif() + if(TARGET opencv_core) + list(APPEND OpenCV_LINKED_COMPONENTS "opencv_core") + endif() +endif() + +set(Yolo_mark_SOURCES "${CMAKE_CURRENT_LIST_DIR}/src/main.cpp") + +add_executable(yolo_mark ${Yolo_mark_SOURCES}) +target_link_libraries(yolo_mark ${OpenCV_LINKED_COMPONENTS}) +install(TARGETS yolo_mark DESTINATION "${INSTALL_BIN_DIR}") diff --git a/README.md b/README.md index 7be25129..557f4a33 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,73 @@ # Yolo_mark -**Windows** & **Linux** GUI for marking bounded boxes of objects in images for training Yolo v3 and v2 -* To compile on **Windows** open `yolo_mark.sln` in MSVS2013/2015, compile it **x64 & Release** and run the file: `x64/Release/yolo_mark.cmd`. Change paths in `yolo_mark.sln` to the OpenCV 2.x/3.x installed on your computer: +GUI for marking bounding boxes of objects in images for training Yolo - * (right click on project) -> properties -> C/C++ -> General -> Additional Include Directories: `C:\opencv_3.0\opencv\build\include;` - - * (right click on project) -> properties -> Linker -> General -> Additional Library Directories: `C:\opencv_3.0\opencv\build\x64\vc14\lib;` +* To build the tool, you need a compiler, cmake and OpenCV. Then run: -* To compile on **Linux** type in console 3 commands: - ``` - cmake . - make - ./linux_mark.sh - ``` +```bash +cmake . +make +./run.sh (or run.cmd on Windows) +``` -Supported both: OpenCV 2.x and OpenCV 3.x +There are also build scripts (`build.sh` and `build.ps1`) to ease building on *nix systems and on windows. --------- +To use for labeling your custom images: -1. To test, simply run - * **on Windows:** `x64/Release/yolo_mark.cmd` - * **on Linux:** `./linux_mark.sh` +* delete all files from directory `data/img` +* put your `.jpg`-images to this directory `data/img` +* change numer of classes (objects for detection) in file [`data/obj.data`](data/obj.data#L1) +* put names of objects, one for each line in file [`data/obj.names`](data/obj.names) +* run file: `yolo_mark.cmd` -2. To use for labeling your custom images: +To train the net for your custom objects, you should change 2 lines in file `yolo-obj.cfg`: - * delete all files from directory `x64/Release/data/img` - * put your `.jpg`-images to this directory `x64/Release/data/img` - * change numer of classes (objects for detection) in file `x64/Release/data/obj.data`: https://github.com/AlexeyAB/Yolo_mark/blob/master/x64/Release/data/obj.data#L1 - * put names of objects, one for each line in file `x64/Release/data/obj.names`: https://github.com/AlexeyAB/Yolo_mark/blob/master/x64/Release/data/obj.names - * run file: `x64\Release\yolo_mark.cmd` +* [set number of classes (objects)](yolo-obj.cfg#L230) +* set `filter`-value + * [For Yolo-v2 `(classes + 5)*5`](yolo-obj.cfg#L224) + * For Yolo-v3 `(classes + 5)*3` +* Download [pre-trained weights](http://pjreddie.com/media/files/darknet19_448.conv.23) for the convolutional layers +* Put files: `yolo-obj.cfg`, `data/train.txt`, `data/obj.names`, `data/obj.data`, `darknet19_448.conv.23` and directory `data/img` near with executable `darknet`-file, and start training: `darknet detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23` -3. To training for your custom objects, you should change 2 lines in file `x64/Release/yolo-obj.cfg`: +For a detailed description, see [darknet repo](https://github.com/AlexeyAB/darknet#how-to-train-to-detect-your-custom-objects) - * set number of classes (objects): https://github.com/AlexeyAB/Yolo_mark/blob/master/x64/Release/yolo-obj.cfg#L230 - * set `filter`-value - * For Yolov2 `(classes + 5)*5`: https://github.com/AlexeyAB/Yolo_mark/blob/master/x64/Release/yolo-obj.cfg#L224 - * For Yolov3 `(classes + 5)*3` +## How to get frames from video files - 3.1 Download pre-trained weights for the convolutional layers (76 MB): http://pjreddie.com/media/files/darknet19_448.conv.23 - - 3.2 Put files: `yolo-obj.cfg`, `data/train.txt`, `data/obj.names`, `data/obj.data`, `darknet19_448.conv.23` and directory `data/img` near with executable `darknet`-file, and start training: `darknet detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23` +To get frames from video files (save each N frame, in example N=10), you can use this command: -For a detailed description, see: https://github.com/AlexeyAB/darknet#how-to-train-to-detect-your-custom-objects - ----- - -#### How to get frames from videofile: - -To get frames from videofile (save each N frame, in example N=10), you can use this command: * on Windows: `yolo_mark.exe data/img cap_video test.mp4 10` * on Linux: `./yolo_mark x64/Release/data/img cap_video test.mp4 10` -Directory `data/img` should be created before this. Also on Windows, the file `opencv_ffmpeg340_64.dll` from `opencv\build\bin` should be placed near with `yolo_mark.exe`. +Directory `data/img` should be created before this. + +As a result, many frames will be collected in the directory `data/img`. Then you can label them manually using such command -As a result, many frames will be collected in the directory `data/img`. Then you can label them manually using such command: * on Windows: `yolo_mark.exe data/img data/train.txt data/obj.names` * on Linux: `./yolo_mark x64/Release/data/img x64/Release/data/train.txt x64/Release/data/obj.names` ----- - -#### Here are: +## Instruction manual -* /x64/Release/ - * `yolo_mark.cmd` - example hot to use yolo mark: `yolo_mark.exe data/img data/train.txt data/obj.names` - * `train_obj.cmd` - example how to train yolo for your custom objects (put this file near with darknet.exe): `darknet.exe detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23` - * `yolo-obj.cfg` - example of yoloV3-neural-network for 2 object -* /x64/Release/data/ - * `obj.names` - example of list with object names - * `obj.data` - example with configuration for training Yolo v3 - * `train.txt` - example with list of image filenames for training Yolo v3 - -* /x64/Release/data/img/`air4.txt` - example with coordinates of objects on image `air4.jpg` with aircrafts (class=0) +### Mouse control -![Image of Yolo_mark](https://habrastorage.org/files/229/f06/277/229f06277fcc49279342b7edfabbb47a.jpg) - -### Instruction manual - -#### Mouse control - -Button | Description | ---- | --- | +Button | Description +--- | --- Left | Draw box Right | Move box -#### Keyboard Shortcuts - -Shortcut | Description | ---- | --- | - | Next image | - | Previous image | -r | Delete selected box (mouse hovered) | -c | Clear all marks on the current image | -p | Copy previous mark | -o | Track objects | -ESC | Close application | -n | One object per image | -0-9 | Object id | -m | Show coords | -w | Line width | -k | Hide object name | -h | Help | - +### Keyboard Shortcuts + +Shortcut | Description +--- | --- + | Next image + | Previous image +r | Delete selected box (mouse hovered) +c | Clear all marks on the current image +p | Copy previous mark +o | Track objects +ESC | Close application +n | One object per image +0-9 | Object id +m | Show coords +w | Line width +k | Hide object name +h | Help diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..06bc11a8 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,57 @@ +image: Visual Studio 2017 +clone_folder: c:\projects\Yolo_mark +cache: C:\Tools\vcpkg\installed\ + +environment: + WORKSPACE: C:\projects + matrix: + - platform: Cygwin64 + COMPILER: cygwin + CYGWIN_NOWINPATH: yes + CYGSH: C:\cygwin64\bin\bash -c + - platform: Win64 + COMPILER: vs + configuration: Release + VCPKG_ROOT: C:\Tools\vcpkg + VCPKG_DEFAULT_TRIPLET: x64-windows + +install: + - if [%COMPILER%]==[vs] cinst cmake ninja + - if [%COMPILER%]==[vs] set "PATH=C:\Program Files\CMake\bin;%PATH%" + - if [%COMPILER%]==[vs] call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 + - if [%COMPILER%]==[cygwin] SET "PATH=C:\cygwin64\usr\local\bin;C:\cygwin64\bin;C:\cygwin64\usr\bin;%PATH%" + - if [%COMPILER%]==[cygwin] SET PATH=%PATH:C:\Program Files\Git\usr\bin;=% + - git submodule -q update --init --recursive + - cd %WORKSPACE%\ + - cd %WORKSPACE%\ + - mkdir cygwin-downloads + - ps: if($env:COMPILER -eq "cygwin") { Invoke-WebRequest https://cygwin.com/setup-x86_64.exe -OutFile $env:WORKSPACE\cygwin-setup.exe } + - if [%COMPILER%]==[cygwin] %WORKSPACE%\cygwin-setup.exe --quiet-mode --no-shortcuts --no-startmenu --no-desktop --upgrade-also --root C:\cygwin64 --local-package-dir %WORKSPACE%\cygwin-downloads --packages gcc-g++,cmake,libopencv-devel,libncurses-devel + - ps: if($env:COMPILER -eq "cygwin") { Invoke-WebRequest https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz -OutFile $env:WORKSPACE\cmake-3.14.0.tar.gz } + - if [%COMPILER%]==[cygwin] %CYGSH% 'tar zxvf cmake-3.14.0.tar.gz' + - if [%COMPILER%]==[cygwin] cd %WORKSPACE%\cmake-3.14.0 + - if [%COMPILER%]==[cygwin] %CYGSH% 'cmake .' + - if [%COMPILER%]==[cygwin] %CYGSH% 'make -j8' + - if [%COMPILER%]==[cygwin] %CYGSH% 'make install' + - if [%COMPILER%]==[cygwin] cd %WORKSPACE% + - if [%COMPILER%]==[vs] cd %VCPKG_ROOT% + - if [%COMPILER%]==[vs] git pull + - if [%COMPILER%]==[vs] .\bootstrap-vcpkg.bat + - if [%COMPILER%]==[vs] echo set(VCPKG_BUILD_TYPE release) >> triplets\%VCPKG_DEFAULT_TRIPLET%.cmake + - if [%COMPILER%]==[vs] .\vcpkg.exe install opencv[contrib] --recurse + - cd %WORKSPACE%\Yolo_mark\ + - mkdir build_debug && cd build_debug + - if [%COMPILER%]==[cygwin] %CYGSH% 'cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE="Debug"' + - cd .. + - mkdir build_release && cd build_release + - if [%COMPILER%]==[cygwin] %CYGSH% 'cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE="Release"' + - if [%COMPILER%]==[vs] if [%configuration%]==[Release] cmake -G "Visual Studio 15 2017" -T "host=x64" -A "x64" "-DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake" "-DVCPKG_TARGET_TRIPLET=%VCPKG_DEFAULT_TRIPLET%" -DCMAKE_BUILD_TYPE="Release" .. + - cd .. + +build_script: + - if [%COMPILER%]==[cygwin] cd build_debug && %CYGSH% 'cmake --build . --target install -- -j8' && cd .. + - if [%COMPILER%]==[cygwin] cd build_release && %CYGSH% 'cmake --build . --target install -- -j8' && cd .. + - if [%COMPILER%]==[vs] if [%configuration%]==[Release] cd build_release && cmake --build . --config Release --parallel 8 --target install && cd .. + +artifacts: + - path: '*.exe' diff --git a/build.ps1 b/build.ps1 new file mode 100755 index 00000000..b5defa9d --- /dev/null +++ b/build.ps1 @@ -0,0 +1,163 @@ +#!/usr/bin/env pwsh + +$number_of_build_workers=8 +$use_vcpkg=$true + +function getProgramFiles32bit() { + $out = ${env:PROGRAMFILES(X86)} + if ($null -eq $out) { + $out = ${env:PROGRAMFILES} + } + + if ($null -eq $out) { + throw "Could not find [Program Files 32-bit]" + } + + return $out +} + +function getLatestVisualStudioWithDesktopWorkloadPath() { + $programFiles = getProgramFiles32bit + $vswhereExe = "$programFiles\Microsoft Visual Studio\Installer\vswhere.exe" + if (Test-Path $vswhereExe) { + $output = & $vswhereExe -products * -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -format xml + [xml]$asXml = $output + foreach ($instance in $asXml.instances.instance) { + $installationPath = $instance.InstallationPath -replace "\\$" # Remove potential trailing backslash + } + if (!$installationPath) { + Write-Host "Warning: no full Visual Studio setup has been found, extending search to include also partial installations" -ForegroundColor Yellow + $output = & $vswhereExe -products * -latest -format xml + [xml]$asXml = $output + foreach ($instance in $asXml.instances.instance) { + $installationPath = $instance.InstallationPath -replace "\\$" # Remove potential trailing backslash + } + } + if (!$installationPath) { + Throw "Could not locate any installation of Visual Studio" + } + } + else { + Throw "Could not locate vswhere at $vswhereExe" + } + return $installationPath +} + + +function getLatestVisualStudioWithDesktopWorkloadVersion() { + $programFiles = getProgramFiles32bit + $vswhereExe = "$programFiles\Microsoft Visual Studio\Installer\vswhere.exe" + if (Test-Path $vswhereExe) { + $output = & $vswhereExe -products * -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -format xml + [xml]$asXml = $output + foreach ($instance in $asXml.instances.instance) { + $installationVersion = $instance.InstallationVersion + } + if (!$installationVersion) { + Write-Host "Warning: no full Visual Studio setup has been found, extending search to include also partial installations" -ForegroundColor Yellow + $output = & $vswhereExe -products * -latest -format xml + [xml]$asXml = $output + foreach ($instance in $asXml.instances.instance) { + $installationVersion = $instance.installationVersion + } + } + if (!$installationVersion) { + Throw "Could not locate any installation of Visual Studio" + } + } + else { + Throw "Could not locate vswhere at $vswhereExe" + } + return $installationVersion +} + + +if ((Test-Path env:VCPKG_ROOT) -and $use_vcpkg) { + $vcpkg_path = "$env:VCPKG_ROOT" + Write-Host "Found vcpkg in VCPKG_ROOT: $vcpkg_path" +} +elseif ((Test-Path "${env:WORKSPACE}\vcpkg") -and $use_vcpkg) { + $vcpkg_path = "${env:WORKSPACE}\vcpkg" + Write-Host "Found vcpkg in WORKSPACE\vcpkg: $vcpkg_path" +} +else { + Write-Host "Unable to find VCPKG`n" -ForegroundColor Yellow +} + +if ($null -eq $env:VCPKG_DEFAULT_TRIPLET -and $use_vcpkg) { + Write-Host "No default triplet has been set-up for vcpkg. Defaulting to x64-windows" -ForegroundColor Yellow + $vcpkg_triplet = "x64-windows" +} +elseif ($use_vcpkg) { + $vcpkg_triplet = $env:VCPKG_DEFAULT_TRIPLET +} + +if ($vcpkg_triplet -Match "x86" -and $use_vcpkg) { + Throw "Yolo_mark is supported only in x64 builds!" +} + +if ($null -eq (Get-Command "cl.exe" -ErrorAction SilentlyContinue)) { + $vsfound = getLatestVisualStudioWithDesktopWorkloadPath + Write-Host "Found VS in ${vsfound}" + Push-Location "${vsfound}\Common7\Tools" + cmd.exe /c "VsDevCmd.bat -arch=x64 & set" | + ForEach-Object { + if ($_ -match "=") { + $v = $_.split("="); Set-Item -force -path "ENV:\$($v[0])" -value "$($v[1])" + } + } + Pop-Location + Write-Host "Visual Studio Command Prompt variables set" -ForegroundColor Yellow +} + +$tokens = getLatestVisualStudioWithDesktopWorkloadVersion +$tokens = $tokens.split('.') +if ($tokens[0] -eq "14") { + $generator = "Visual Studio 14 2015" +} +elseif ($tokens[0] -eq "15") { + $generator = "Visual Studio 15 2017" +} +elseif ($tokens[0] -eq "16") { + $generator = "Visual Studio 16 2019" +} +else { + throw "Unknown Visual Studio version, unsupported configuration" +} +Write-Host "Setting up environment to use CMake generator: $generator" -ForegroundColor Yellow + +if ($null -eq (Get-Command "nvcc.exe" -ErrorAction SilentlyContinue)) { + if (Test-Path env:CUDA_PATH) { + $env:PATH += ";${env:CUDA_PATH}\bin" + Write-Host "Found cuda in ${env:CUDA_PATH}" -ForegroundColor Yellow + } + else { + Write-Host "Unable to find CUDA, if necessary please install it or define a CUDA_PATH env variable pointing to the install folder" -ForegroundColor Yellow + } +} + +if (Test-Path env:CUDA_PATH) { + if (-Not(Test-Path env:CUDA_TOOLKIT_ROOT_DIR)) { + $env:CUDA_TOOLKIT_ROOT_DIR = "${env:CUDA_PATH}" + Write-Host "Added missing env variable CUDA_TOOLKIT_ROOT_DIR" -ForegroundColor Yellow + } +} + + +## DEBUG +#New-Item -Path .\build_win_debug -ItemType directory -Force +#Set-Location build_win_debug +#cmake -G "$generator" -T "host=x64" -A "x64" "-DCMAKE_TOOLCHAIN_FILE=$vcpkg_path\scripts\buildsystems\vcpkg.cmake" "-DVCPKG_TARGET_TRIPLET=$vcpkg_triplet" #"-DCMAKE_BUILD_TYPE=Debug" $additional_build_setup .. +#cmake --build . --config Debug --target install +##cmake --build . --config Debug --parallel ${number_of_build_workers} --target install #valid only for CMake 3.12+ +#Copy-Item Debug\*.dll .. +#Set-Location .. + +# RELEASE +New-Item -Path .\build_win_release -ItemType directory -Force +Set-Location build_win_release +cmake -G "$generator" -T "host=x64" -A "x64" "-DCMAKE_TOOLCHAIN_FILE=$vcpkg_path\scripts\buildsystems\vcpkg.cmake" "-DVCPKG_TARGET_TRIPLET=$vcpkg_triplet" "-DCMAKE_BUILD_TYPE=Release" $additional_build_setup .. +cmake --build . --config Release --target install +#cmake --build . --config Release --parallel ${number_of_build_workers} --target install #valid only for CMake 3.12+ +Copy-Item Release\*.dll .. +Set-Location .. diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..81d54d65 --- /dev/null +++ b/build.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +number_of_build_workers=8 +bypass_vcpkg=true + +if [[ "$OSTYPE" == "darwin"* ]]; then + if [[ "$1" == "gcc" ]]; then + export CC="/usr/local/bin/gcc-9" + export CXX="/usr/local/bin/g++-9" + fi + vcpkg_triplet="x64-osx" +else + vcpkg_triplet="x64-linux" +fi + +if [[ ! -z "${VCPKG_ROOT}" ]] && [ -d ${VCPKG_ROOT} ] && [ ! "$bypass_vcpkg" = true ] +then + vcpkg_path="${VCPKG_ROOT}" + vcpkg_define="-DCMAKE_TOOLCHAIN_FILE=${vcpkg_path}/scripts/buildsystems/vcpkg.cmake" + vcpkg_triplet_define="-DVCPKG_TARGET_TRIPLET=$vcpkg_triplet" + echo "Found vcpkg in VCPKG_ROOT: ${vcpkg_path}" +elif [[ ! -z "${WORKSPACE}" ]] && [ -d ${WORKSPACE}/vcpkg ] && [ ! "$bypass_vcpkg" = true ] +then + vcpkg_path="${WORKSPACE}/vcpkg" + vcpkg_define="-DCMAKE_TOOLCHAIN_FILE=${vcpkg_path}/scripts/buildsystems/vcpkg.cmake" + vcpkg_triplet_define="-DVCPKG_TARGET_TRIPLET=$vcpkg_triplet" + echo "Found vcpkg in WORKSPACE/vcpkg: ${vcpkg_path}" +elif [ ! "$bypass_vcpkg" = true ] +then + (>&2 echo "yolo_mark is unsupported without vcpkg, use at your own risk!") +fi + +## DEBUG +#mkdir -p build_debug +#cd build_debug +#cmake .. -DCMAKE_BUILD_TYPE=Debug ${vcpkg_define} ${vcpkg_triplet_define} ${additional_defines} ${additional_build_setup} +#cmake --build . --target install -- -j${number_of_build_workers} +##cmake --build . --target install --parallel ${number_of_build_workers} #valid only for CMake 3.12+ +#cd .. + +# RELEASE +mkdir -p build_release +cd build_release +cmake .. -DCMAKE_BUILD_TYPE=Release ${vcpkg_define} ${vcpkg_triplet_define} ${additional_defines} ${additional_build_setup} +cmake --build . --target install -- -j${number_of_build_workers} +#cmake --build . --target install --parallel ${number_of_build_workers} #valid only for CMake 3.12+ +cd .. + +# In case you want to discard changes from examples, it's useful to run this command: +# git update-index --assume-unchanged data/img/air1.jpg data/img/air1.txt data/img/air2.jpg data/img/air2.txt data/img/air3.jpg data/img/air3.txt data/img/air4.jpg data/img/air4.txt data/img/air5.jpg data/img/air5.txt data/img/air6.jpg data/img/air6.txt data/img/bird1.jpg data/img/bird1.txt data/img/bird2.jpg data/img/bird2.txt data/img/bird3.jpg data/img/bird3.txt data/img/bird4.jpg data/img/bird4.txt data/obj.data data/obj.names data/train.txt + diff --git a/x64/Release/data/img/air1.jpg b/data/img/air1.jpg similarity index 100% rename from x64/Release/data/img/air1.jpg rename to data/img/air1.jpg diff --git a/x64/Release/data/img/air1.txt b/data/img/air1.txt similarity index 100% rename from x64/Release/data/img/air1.txt rename to data/img/air1.txt diff --git a/x64/Release/data/img/air2.jpg b/data/img/air2.jpg similarity index 100% rename from x64/Release/data/img/air2.jpg rename to data/img/air2.jpg diff --git a/x64/Release/data/img/air2.txt b/data/img/air2.txt similarity index 100% rename from x64/Release/data/img/air2.txt rename to data/img/air2.txt diff --git a/x64/Release/data/img/air3.jpg b/data/img/air3.jpg similarity index 100% rename from x64/Release/data/img/air3.jpg rename to data/img/air3.jpg diff --git a/x64/Release/data/img/air3.txt b/data/img/air3.txt similarity index 100% rename from x64/Release/data/img/air3.txt rename to data/img/air3.txt diff --git a/x64/Release/data/img/air4.jpg b/data/img/air4.jpg similarity index 100% rename from x64/Release/data/img/air4.jpg rename to data/img/air4.jpg diff --git a/x64/Release/data/img/air4.txt b/data/img/air4.txt similarity index 100% rename from x64/Release/data/img/air4.txt rename to data/img/air4.txt diff --git a/x64/Release/data/img/air5.jpg b/data/img/air5.jpg similarity index 100% rename from x64/Release/data/img/air5.jpg rename to data/img/air5.jpg diff --git a/x64/Release/data/img/air5.txt b/data/img/air5.txt similarity index 100% rename from x64/Release/data/img/air5.txt rename to data/img/air5.txt diff --git a/x64/Release/data/img/air6.jpg b/data/img/air6.jpg similarity index 100% rename from x64/Release/data/img/air6.jpg rename to data/img/air6.jpg diff --git a/x64/Release/data/img/air6.txt b/data/img/air6.txt similarity index 100% rename from x64/Release/data/img/air6.txt rename to data/img/air6.txt diff --git a/x64/Release/data/img/bird1.jpg b/data/img/bird1.jpg similarity index 100% rename from x64/Release/data/img/bird1.jpg rename to data/img/bird1.jpg diff --git a/x64/Release/data/img/bird1.txt b/data/img/bird1.txt similarity index 100% rename from x64/Release/data/img/bird1.txt rename to data/img/bird1.txt diff --git a/x64/Release/data/img/bird2.jpg b/data/img/bird2.jpg similarity index 100% rename from x64/Release/data/img/bird2.jpg rename to data/img/bird2.jpg diff --git a/x64/Release/data/img/bird2.txt b/data/img/bird2.txt similarity index 100% rename from x64/Release/data/img/bird2.txt rename to data/img/bird2.txt diff --git a/x64/Release/data/img/bird3.jpg b/data/img/bird3.jpg similarity index 100% rename from x64/Release/data/img/bird3.jpg rename to data/img/bird3.jpg diff --git a/x64/Release/data/img/bird3.txt b/data/img/bird3.txt similarity index 100% rename from x64/Release/data/img/bird3.txt rename to data/img/bird3.txt diff --git a/x64/Release/data/img/bird4.jpg b/data/img/bird4.jpg similarity index 100% rename from x64/Release/data/img/bird4.jpg rename to data/img/bird4.jpg diff --git a/x64/Release/data/img/bird4.txt b/data/img/bird4.txt similarity index 100% rename from x64/Release/data/img/bird4.txt rename to data/img/bird4.txt diff --git a/x64/Release/data/obj.data b/data/obj.data similarity index 100% rename from x64/Release/data/obj.data rename to data/obj.data diff --git a/x64/Release/data/obj.names b/data/obj.names similarity index 100% rename from x64/Release/data/obj.names rename to data/obj.names diff --git a/x64/Release/data/train.txt b/data/train.txt similarity index 100% rename from x64/Release/data/train.txt rename to data/train.txt diff --git a/linux_mark.sh b/linux_mark.sh deleted file mode 100644 index 7b560195..00000000 --- a/linux_mark.sh +++ /dev/null @@ -1,7 +0,0 @@ -echo Example how to start marking bouded boxes for training set Yolo v2 - - -./yolo_mark x64/Release/data/img x64/Release/data/train.txt x64/Release/data/obj.names - - -pause diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 53ec2dc6..00000000 --- a/main.cpp +++ /dev/null @@ -1,1014 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include // C++11: async(); feature<>; -#include -#include -#include // std::ofstream -#include // std::unique - -#include // C++ -#include -#include -#include -//#include -#include - -#ifdef _DEBUG -#define LIB_SUFFIX "d.lib" -#else -#define LIB_SUFFIX ".lib" -#endif // DEBUG - -#ifndef CV_VERSION_EPOCH -#include "opencv2/videoio/videoio.hpp" -#define OPENCV_VERSION CVAUX_STR(CV_VERSION_MAJOR)"" CVAUX_STR(CV_VERSION_MINOR)"" CVAUX_STR(CV_VERSION_REVISION) -#pragma comment(lib, "opencv_world" OPENCV_VERSION LIB_SUFFIX) -#else -#define OPENCV_VERSION CVAUX_STR(CV_VERSION_EPOCH)"" CVAUX_STR(CV_VERSION_MAJOR)"" CVAUX_STR(CV_VERSION_MINOR) -#pragma comment(lib, "opencv_core" OPENCV_VERSION LIB_SUFFIX) -#pragma comment(lib, "opencv_imgproc" OPENCV_VERSION LIB_SUFFIX) -#pragma comment(lib, "opencv_highgui" OPENCV_VERSION LIB_SUFFIX) -#endif - -#ifndef CV_FILLED -#define CV_FILLED cv::FILLED -#endif - - -using namespace cv; - - -// label coordinates -struct coord_t { - cv::Rect_ abs_rect; - int id; -}; - -class Tracker_optflow { -public: - const int flow_error; - - Tracker_optflow(int win_size = 15, int max_level = 3, int iterations = 8000, int _flow_error = -1) : - flow_error((_flow_error > 0) ? _flow_error : (win_size * 4)) - { - sync_PyrLKOpticalFlow = cv::SparsePyrLKOpticalFlow::create(); - sync_PyrLKOpticalFlow->setWinSize(cv::Size(win_size, win_size)); // 9, 15, 21, 31 - sync_PyrLKOpticalFlow->setMaxLevel(max_level); // +- 3 pt - - } - - // just to avoid extra allocations - cv::Mat dst_grey; - cv::Mat prev_pts_flow, cur_pts_flow; - cv::Mat status, err; - - cv::Mat src_grey; // used in both functions - cv::Ptr sync_PyrLKOpticalFlow; - - std::vector cur_bbox_vec; - std::vector good_bbox_vec_flags; - - void update_cur_bbox_vec(std::vector _cur_bbox_vec) - { - cur_bbox_vec = _cur_bbox_vec; - good_bbox_vec_flags = std::vector(cur_bbox_vec.size(), true); - cv::Mat prev_pts, cur_pts_flow; - - for (auto &i : cur_bbox_vec) { - float x_center = (i.abs_rect.x + i.abs_rect.width / 2.0F); - float y_center = (i.abs_rect.y + i.abs_rect.height / 2.0F); - prev_pts.push_back(cv::Point2f(x_center, y_center)); - } - - if (prev_pts.rows == 0) - prev_pts_flow = cv::Mat(); - else - cv::transpose(prev_pts, prev_pts_flow); - } - - - void update_tracking_flow(cv::Mat new_src_mat, std::vector _cur_bbox_vec) - { - if (new_src_mat.channels() == 1) { - src_grey = new_src_mat.clone(); - } - else if (new_src_mat.channels() == 3) { - cv::cvtColor(new_src_mat, src_grey, cv::COLOR_BGR2GRAY, 1); - } - else if (new_src_mat.channels() == 4) { - cv::cvtColor(new_src_mat, src_grey, cv::COLOR_BGRA2GRAY, 1); - } - else { - std::cerr << " Warning: new_src_mat.channels() is not: 1, 3 or 4. It is = " << new_src_mat.channels() << " \n"; - return; - } - update_cur_bbox_vec(_cur_bbox_vec); - } - - - std::vector tracking_flow(cv::Mat new_dst_mat, bool check_error = true) - { - if (sync_PyrLKOpticalFlow.empty()) { - std::cout << "sync_PyrLKOpticalFlow isn't initialized \n"; - return cur_bbox_vec; - } - - cv::cvtColor(new_dst_mat, dst_grey, cv::COLOR_BGR2GRAY, 1); - - if (src_grey.rows != dst_grey.rows || src_grey.cols != dst_grey.cols) { - src_grey = dst_grey.clone(); - //std::cerr << " Warning: src_grey.rows != dst_grey.rows || src_grey.cols != dst_grey.cols \n"; - return cur_bbox_vec; - } - - if (prev_pts_flow.cols < 1) { - return cur_bbox_vec; - } - - ////sync_PyrLKOpticalFlow_gpu.sparse(src_grey_gpu, dst_grey_gpu, prev_pts_flow_gpu, cur_pts_flow_gpu, status_gpu, &err_gpu); // OpenCV 2.4.x - sync_PyrLKOpticalFlow->calc(src_grey, dst_grey, prev_pts_flow, cur_pts_flow, status, err); // OpenCV 3.x - - dst_grey.copyTo(src_grey); - - std::vector result_bbox_vec; - - if (err.rows == cur_bbox_vec.size() && status.rows == cur_bbox_vec.size()) - { - for (size_t i = 0; i < cur_bbox_vec.size(); ++i) - { - cv::Point2f cur_key_pt = cur_pts_flow.at(0, i); - cv::Point2f prev_key_pt = prev_pts_flow.at(0, i); - - float moved_x = cur_key_pt.x - prev_key_pt.x; - float moved_y = cur_key_pt.y - prev_key_pt.y; - - if (abs(moved_x) < 100 && abs(moved_y) < 100 && good_bbox_vec_flags[i]) - if (err.at(0, i) < flow_error && status.at(0, i) != 0 && - ((float)cur_bbox_vec[i].abs_rect.x + moved_x) > 0 && ((float)cur_bbox_vec[i].abs_rect.y + moved_y) > 0) - { - cur_bbox_vec[i].abs_rect.x += moved_x;// +0.5; - cur_bbox_vec[i].abs_rect.y += moved_y;// +0.5; - result_bbox_vec.push_back(cur_bbox_vec[i]); - } - else good_bbox_vec_flags[i] = false; - else good_bbox_vec_flags[i] = false; - - //if(!check_error && !good_bbox_vec_flags[i]) result_bbox_vec.push_back(cur_bbox_vec[i]); - } - } - - prev_pts_flow = cur_pts_flow.clone(); - - return result_bbox_vec; - } - -}; - -std::atomic right_button_click; -std::atomic move_rect_id; -std::atomic move_rect; -std::atomic clear_marks; -std::atomic copy_previous_marks(false); -std::atomic tracker_copy_previous_marks(false); - -std::atomic show_help; -std::atomic exit_flag(false); - -std::atomic mark_line_width(2); // default mark line width is 2 pixels. -const int MAX_MARK_LINE_WIDTH = 3; -std::atomic show_mark_class(true); -std::atomic delete_selected(false); - -std::atomic x_start, y_start; -std::atomic x_end, y_end; -std::atomic x_size, y_size; -std::atomic draw_select, selected, undo; - -std::atomic add_id_img; -Rect prev_img_rect(0, 0, 50, 100); -Rect next_img_rect(1280 - 50, 0, 50, 100); - - -void callback_mouse_click(int event, int x, int y, int flags, void* user_data) -{ - if (event == cv::EVENT_LBUTTONDBLCLK) - { - std::cout << "cv::EVENT_LBUTTONDBLCLK \n"; - } - else if (event == cv::EVENT_LBUTTONDOWN) - { - draw_select = true; - selected = false; - x_start = x; - y_start = y; - - if (prev_img_rect.contains(Point2i(x, y))) add_id_img = -1; - else if (next_img_rect.contains(Point2i(x, y))) add_id_img = 1; - else add_id_img = 0; - //std::cout << "cv::EVENT_LBUTTONDOWN \n"; - } - else if (event == cv::EVENT_LBUTTONUP) - { - x_size = abs(x - x_start); - y_size = abs(y - y_start); - x_end = max(x, 0); - y_end = max(y, 0); - draw_select = false; - selected = true; - //std::cout << "cv::EVENT_LBUTTONUP \n"; - } - else if (event == cv::EVENT_RBUTTONDOWN) - { - right_button_click = true; - - x_start = x; - y_start = y; - std::cout << "cv::EVENT_RBUTTONDOWN \n"; - } - else if (event == cv::EVENT_RBUTTONUP) - { - right_button_click = false; - move_rect = true; - } - if (event == cv::EVENT_RBUTTONDBLCLK) - { - std::cout << "cv::EVENT_RBUTTONDBLCLK \n"; - } - else if (event == cv::EVENT_MOUSEMOVE) - { - x_end = max(x, 0); - y_end = max(y, 0); - } -} - -class comma : public std::numpunct { -public: - comma() : std::numpunct() {} -protected: - char do_decimal_point() const { return '.'; } -}; - - -int main(int argc, char *argv[]) -{ - - try - { - std::locale loccomma(std::locale::classic(), new comma); - std::locale::global(loccomma); - - std::string images_path = "./"; - - if (argc >= 2) { - images_path = std::string(argv[1]); // path to images, train and synset - } - else { - std::cout << "Usage: [path_to_images] [train.txt] [obj.names] \n"; - return 0; - } - - std::string train_filename = images_path + "train.txt"; - std::string synset_filename = images_path + "obj.names"; - - if (argc >= 3) { - train_filename = std::string(argv[2]); // file containing: list of images - } - - if (argc >= 4) { - synset_filename = std::string(argv[3]); // file containing: object names - } - - // optical flow tracker - Tracker_optflow tracker_optflow; - cv::Mat optflow_img; - - // capture frames from video file - 1 frame per 3 seconds of video - if (argc >= 4 && (train_filename == "cap_video" || train_filename == "cap_video_backward")) { - const std::string videofile = synset_filename; - cv::VideoCapture cap(videofile); -#ifndef CV_VERSION_EPOCH // OpenCV 3.x - const int fps = cap.get(cv::CAP_PROP_FPS); -#else // OpenCV 2.x - const int fps = cap.get(CV_CAP_PROP_FPS); -#endif - int frame_counter = 0, image_counter = 0; - int backward = (train_filename == "cap_video_backward") ? 1 : 0; - if (backward) image_counter = 99999999; // 99M - float save_each_frames = 50; - if (argc >= 5) save_each_frames = std::stoul(std::string(argv[4])); - - int pos_filename = 0; - if ((1 + videofile.find_last_of("\\")) < videofile.length()) pos_filename = 1 + videofile.find_last_of("\\"); - if ((1 + videofile.find_last_of("/")) < videofile.length()) pos_filename = std::max(pos_filename, 1 + (int)videofile.find_last_of("/")); - std::string const filename = videofile.substr(pos_filename); - std::string const filename_without_ext = filename.substr(0, filename.find_last_of(".")); - - for (cv::Mat frame; cap >> frame, cap.isOpened() && !frame.empty();) { - cv::imshow("video cap to frames", frame); -#ifndef CV_VERSION_EPOCH - int pressed_key = cv::waitKeyEx(20); // OpenCV 3.x -#else - int pressed_key = cv::waitKey(20); // OpenCV 2.x -#endif - if (pressed_key == 27 || pressed_key == 1048603) break; // ESC - exit (OpenCV 2.x / 3.x) - if (frame_counter++ >= save_each_frames) { // save frame for each 3 second - frame_counter = 0; - std::stringstream image_counter_ss; - image_counter_ss << std::setw(8) << std::setfill('0') << image_counter; - if (backward) image_counter--; - else image_counter++; - std::string img_name = images_path + "/" + filename_without_ext + "_" + image_counter_ss.str() + ".jpg"; - std::cout << "saved " << img_name << std::endl; - cv::imwrite(img_name, frame); - } - } - exit(0); - } - - bool show_mouse_coords = false; - std::vector filenames_in_folder; - //glob(images_path, filenames_in_folder); // void glob(String pattern, std::vector& result, bool recursive = false); - cv::String images_path_cv = images_path; - std::vector filenames_in_folder_cv; - glob(images_path_cv, filenames_in_folder_cv); // void glob(String pattern, std::vector& result, bool recursive = false); - for (auto &i : filenames_in_folder_cv) - filenames_in_folder.push_back(i); - - std::vector jpg_filenames_path; - std::vector jpg_filenames; - std::vector jpg_filenames_without_ext; - std::vector image_ext; - std::vector txt_filenames; - std::vector jpg_in_train; - std::vector synset_txt; - - // image-paths to txt-paths - for (auto &i : filenames_in_folder) - { - int pos_filename = 0; - if ((1 + i.find_last_of("\\")) < i.length()) pos_filename = 1 + i.find_last_of("\\"); - if ((1 + i.find_last_of("/")) < i.length()) pos_filename = std::max(pos_filename, 1 + (int)i.find_last_of("/")); - - - std::string const filename = i.substr(pos_filename); - std::string const ext = i.substr(i.find_last_of(".") + 1); - std::string const filename_without_ext = filename.substr(0, filename.find_last_of(".")); - - if (ext == "jpg" || ext == "JPG" || - ext == "jpeg" || ext == "JPEG" || - ext == "bmp" || ext == "BMP" || - ext == "png" || ext == "PNG" || - ext == "ppm" || ext == "PPM") - { - jpg_filenames_without_ext.push_back(filename_without_ext); - image_ext.push_back(ext); - jpg_filenames.push_back(filename); - jpg_filenames_path.push_back(i); - } - if (ext == "txt") { - txt_filenames.push_back(filename_without_ext); - } - } - std::sort(jpg_filenames.begin(), jpg_filenames.end()); - std::sort(jpg_filenames_path.begin(), jpg_filenames_path.end()); - std::sort(txt_filenames.begin(), txt_filenames.end()); - - if (jpg_filenames.size() == 0) { - std::cout << "Error: Image files not found by path: " << images_path << std::endl; - return 0; - } - - // check whether there are files with the same names (but different extensions) - { - auto sorted_names_without_ext = jpg_filenames_without_ext; - std::sort(sorted_names_without_ext.begin(), sorted_names_without_ext.end()); - for (size_t i = 1; i < sorted_names_without_ext.size(); ++i) { - if (sorted_names_without_ext[i - 1] == sorted_names_without_ext[i]) { - std::cout << "Error: Can't create " << sorted_names_without_ext[i] << - ".txt file for several images with different extensions but with the same filename: " - << sorted_names_without_ext[i] << std::endl; - // print duplicate images - for (size_t k = 0; k < jpg_filenames_without_ext.size(); ++k) { - if (jpg_filenames_without_ext[k] == sorted_names_without_ext[i]) { - std::cout << jpg_filenames_without_ext[k] << "." << image_ext[k] << std::endl; - } - } - return 0; - } - } - } - - // intersect jpg & txt - std::vector intersect_filenames(jpg_filenames.size()); - std::vector difference_filenames(jpg_filenames.size()); - std::vector intersect_ext; - std::vector difference_ext; - - auto dif_it_end = std::set_difference(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), - txt_filenames.begin(), txt_filenames.end(), - difference_filenames.begin()); - difference_filenames.resize(dif_it_end - difference_filenames.begin()); - - auto inter_it_end = std::set_intersection(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), - txt_filenames.begin(), txt_filenames.end(), - intersect_filenames.begin()); - intersect_filenames.resize(inter_it_end - intersect_filenames.begin()); - - // get intersect extensions for intersect_filenames - for (auto &i : intersect_filenames) { - size_t ext_index = find(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), i) - jpg_filenames_without_ext.begin(); - intersect_ext.push_back(image_ext[ext_index]); - } - - // get difference extensions for intersect_filenames - for (auto &i : difference_filenames) { - size_t ext_index = find(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), i) - jpg_filenames_without_ext.begin(); - difference_ext.push_back(image_ext[ext_index]); - } - - txt_filenames.clear(); - for (auto &i : intersect_filenames) { - txt_filenames.push_back(i + ".txt"); - } - - int image_list_count = max(1, (int)jpg_filenames_path.size() - 1); - - // store train.txt - std::ofstream ofs_train(train_filename, std::ios::out | std::ios::trunc); - if (!ofs_train.is_open()) { - throw(std::runtime_error("Can't open file: " + train_filename)); - } - - for (size_t i = 0; i < intersect_filenames.size(); ++i) { - ofs_train << images_path << "/" << intersect_filenames[i] << "." << intersect_ext[i] << std::endl; - } - ofs_train.flush(); - std::cout << "File opened for output: " << train_filename << std::endl; - - - // load synset.txt - { - std::ifstream ifs(synset_filename); - if (!ifs.is_open()) { - throw(std::runtime_error("Can't open file: " + synset_filename)); - } - - for (std::string line; getline(ifs, line);) - synset_txt.push_back(line); - } - std::cout << "File loaded: " << synset_filename << std::endl; - - Mat preview(Size(100, 100), CV_8UC3); - Mat full_image(Size(1280, 720), CV_8UC3); - Mat frame(Size(full_image.cols, full_image.rows + preview.rows), CV_8UC3); - - Rect full_rect_dst(Point2i(0, preview.rows), Size(frame.cols, frame.rows - preview.rows)); - Mat full_image_roi = frame(full_rect_dst); - - size_t const preview_number = frame.cols / preview.cols; - - - // labels on the current image - std::vector current_coord_vec; - Size current_img_size; - - - std::string const window_name = "Marking images"; - namedWindow(window_name, WINDOW_NORMAL); - resizeWindow(window_name, 1280, 720); - imshow(window_name, frame); - moveWindow(window_name, 0, 0); - setMouseCallback(window_name, callback_mouse_click); - - bool next_by_click = false; - bool marks_changed = false; - - int old_trackbar_value = -1, trackbar_value = 0; - std::string const trackbar_name = "image num"; - int tb_res = createTrackbar(trackbar_name, window_name, &trackbar_value, image_list_count); - - int old_current_obj_id = -1, current_obj_id = 0; - std::string const trackbar_name_2 = "object id"; - int const max_object_id = (synset_txt.size() > 0) ? synset_txt.size() : 20; - int tb_res_2 = createTrackbar(trackbar_name_2, window_name, ¤t_obj_id, max_object_id); - - - do { - //trackbar_value = min(max(0, trackbar_value), (int)jpg_filenames_path.size() - 1); - - // selected new image - if (old_trackbar_value != trackbar_value || exit_flag) - { - trackbar_value = min(max(0, trackbar_value), (int)jpg_filenames_path.size() - 1); - setTrackbarPos(trackbar_name, window_name, trackbar_value); - frame(Rect(0, 0, frame.cols, preview.rows)) = Scalar::all(0); - move_rect_id = -1; - - // save current coords - if (old_trackbar_value >= 0) // && current_coord_vec.size() > 0) // Yolo v2 can processes background-image without objects - { - try - { - std::string const jpg_filename = jpg_filenames[old_trackbar_value]; - std::string const filename_without_ext = jpg_filename.substr(0, jpg_filename.find_last_of(".")); - std::string const txt_filename = filename_without_ext + ".txt"; - std::string const txt_filename_path = images_path + "/" + txt_filename; - - std::cout << "txt_filename_path = " << txt_filename_path << std::endl; - - std::ofstream ofs(txt_filename_path, std::ios::out | std::ios::trunc); - ofs << std::fixed; - - // store coords to [image name].txt - for (auto &i : current_coord_vec) - { - float const relative_center_x = (float)(i.abs_rect.x + i.abs_rect.width / 2) / full_image_roi.cols; - float const relative_center_y = (float)(i.abs_rect.y + i.abs_rect.height / 2) / full_image_roi.rows; - float const relative_width = (float)i.abs_rect.width / full_image_roi.cols; - float const relative_height = (float)i.abs_rect.height / full_image_roi.rows; - - if (relative_width <= 0) continue; - if (relative_height <= 0) continue; - if (relative_center_x <= 0) continue; - if (relative_center_y <= 0) continue; - - ofs << i.id << " " << - relative_center_x << " " << relative_center_y << " " << - relative_width << " " << relative_height << std::endl; - } - - // store [path/image name.jpg] to train.txt - auto it = std::find(difference_filenames.begin(), difference_filenames.end(), filename_without_ext); - if (it != difference_filenames.end()) - { - ofs_train << images_path << "/" << jpg_filename << std::endl; - ofs_train.flush(); - - size_t new_size = std::remove(difference_filenames.begin(), difference_filenames.end(), filename_without_ext) - - difference_filenames.begin(); - difference_filenames.resize(new_size); - } - } - catch (...) { std::cout << " Exception when try to write txt-file \n"; } - } - - // show preview images - for (size_t i = 0; i < preview_number && (i + trackbar_value) < jpg_filenames_path.size(); ++i) - { - Mat img = imread(jpg_filenames_path[trackbar_value + i]); - // check if the image has been loaded successful to prevent crash - if (img.cols == 0) - { - continue; - } - resize(img, preview, preview.size()); - int const x_shift = i*preview.cols + prev_img_rect.width; - Rect rect_dst(Point2i(x_shift, 0), preview.size()); - Mat dst_roi = frame(rect_dst); - preview.copyTo(dst_roi); - //rectangle(frame, rect_dst, Scalar(200, 150, 200), 2); - putText(dst_roi, jpg_filenames[trackbar_value + i], Point2i(0, 10), FONT_HERSHEY_COMPLEX_SMALL, 0.5, Scalar::all(255)); - - if (i == 0) - { - optflow_img = img; - resize(img, full_image, full_rect_dst.size()); - full_image.copyTo(full_image_roi); - current_img_size = img.size(); - - try { - std::string const jpg_filename = jpg_filenames[trackbar_value]; - std::string const txt_filename = jpg_filename.substr(0, jpg_filename.find_last_of(".")) + ".txt"; - //std::cout << (images_path + "/" + txt_filename) << std::endl; - std::ifstream ifs(images_path + "/" + txt_filename); - if (copy_previous_marks) copy_previous_marks = false; - else if (tracker_copy_previous_marks) { - tracker_copy_previous_marks = false; - current_coord_vec = tracker_optflow.tracking_flow(img, false); - } - else current_coord_vec.clear(); - - for (std::string line; getline(ifs, line);) - { - std::stringstream ss(line); - coord_t coord; - coord.id = -1; - ss >> coord.id; - if (coord.id < 0) continue; - float relative_coord[4] = { -1, -1, -1, -1 }; // rel_center_x, rel_center_y, rel_width, rel_height - for (size_t i = 0; i < 4; i++) if(!(ss >> relative_coord[i])) continue; - for (size_t i = 0; i < 4; i++) if (relative_coord[i] < 0) continue; - coord.abs_rect.x = (relative_coord[0] - relative_coord[2] / 2) * (float)full_image_roi.cols; - coord.abs_rect.y = (relative_coord[1] - relative_coord[3] / 2) * (float)full_image_roi.rows; - coord.abs_rect.width = relative_coord[2] * (float)full_image_roi.cols; - coord.abs_rect.height = relative_coord[3] * (float)full_image_roi.rows; - - current_coord_vec.push_back(coord); - } - } - catch (...) { std::cout << " Exception when try to read txt-file \n"; } - } - - std::string const jpg_filename = jpg_filenames[trackbar_value + i]; - std::string const filename_without_ext = jpg_filename.substr(0, jpg_filename.find_last_of(".")); - // green check-mark on the preview image if there is a lebel txt-file for this image - if (!std::binary_search(difference_filenames.begin(), difference_filenames.end(), filename_without_ext)) - { - line(dst_roi, Point2i(80, 88), Point2i(85, 93), Scalar(20, 70, 20), 5); - line(dst_roi, Point2i(85, 93), Point2i(93, 85), Scalar(20, 70, 20), 5); - - line(dst_roi, Point2i(80, 88), Point2i(85, 93), Scalar(50, 200, 100), 2); - line(dst_roi, Point2i(85, 93), Point2i(93, 85), Scalar(50, 200, 100), 2); - } - - } - std::cout << " trackbar_value = " << trackbar_value << std::endl; - - old_trackbar_value = trackbar_value; - - marks_changed = false; - - rectangle(frame, prev_img_rect, Scalar(100, 100, 100), CV_FILLED); - rectangle(frame, next_img_rect, Scalar(100, 100, 100), CV_FILLED); - } - - trackbar_value = min(max(0, trackbar_value), (int)jpg_filenames_path.size() - 1); - - // highlight prev img - for (size_t i = 0; i < preview_number && (i + trackbar_value) < jpg_filenames_path.size(); ++i) - { - int const x_shift = i*preview.cols + prev_img_rect.width; - Rect rect_dst(Point2i(x_shift, 0), Size(preview.cols - 2, preview.rows)); - Scalar color(100, 70, 100); - if (i == 0) color = Scalar(250, 120, 150); - if (y_end < preview.rows && i == (x_end - prev_img_rect.width) / preview.cols) color = Scalar(250, 200, 200); - rectangle(frame, rect_dst, color, 2); - } - - if (undo) { - undo = false; - if(current_coord_vec.size() > 0) { - full_image.copyTo(full_image_roi); - current_coord_vec.pop_back(); - } - } - - // marking is completed (left mouse button is OFF) - if (selected) - { - selected = false; - full_image.copyTo(full_image_roi); - - if (y_end < preview.rows && x_end > prev_img_rect.width && x_end < (full_image.cols - prev_img_rect.width) && - y_start < preview.rows) - { - int const i = (x_end - prev_img_rect.width) / preview.cols; - trackbar_value += i; - } - else if (y_end >= preview.rows) - { - if (next_by_click) { - ++trackbar_value; - current_coord_vec.clear(); - } - - Rect selected_rect( - Point2i((int)min(x_start, x_end), (int)min(y_start, y_end)), - Size(x_size, y_size)); - - selected_rect &= full_rect_dst; - selected_rect.y -= (int)prev_img_rect.height; - - coord_t coord; - coord.abs_rect = selected_rect; - coord.id = current_obj_id; - current_coord_vec.push_back(coord); - - marks_changed = true; - } - } - - std::string current_synset_name; - if (current_obj_id < synset_txt.size()) current_synset_name = " - " + synset_txt[current_obj_id]; - - // show X and Y coords of mouse - if (show_mouse_coords) { - full_image.copyTo(full_image_roi); - int const x_inside = std::min((int)x_end, full_image_roi.cols); - int const y_inside = std::min(std::max(0, y_end - (int)prev_img_rect.height), full_image_roi.rows); - float const relative_center_x = (float)(x_inside) / full_image_roi.cols; - float const relative_center_y = (float)(y_inside) / full_image_roi.rows; - int const abs_x = relative_center_x*current_img_size.width; - int const abs_y = relative_center_y*current_img_size.height; - char buff[100]; - snprintf(buff, 100, "Abs: %d x %d Rel: %.3f x %.3f", abs_x, abs_y, relative_center_x, relative_center_y); - //putText(full_image_roi, buff, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(50, 10, 10), 3); - putText(full_image_roi, buff, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(100, 50, 50), 2); - putText(full_image_roi, buff, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(220, 120, 120), 1); - } - else - { - full_image.copyTo(full_image_roi); - //std::string text = "Show mouse coordinates - press M"; - //putText(full_image_roi, text, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(100, 50, 50), 2); - //putText(full_image_roi, text, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(220, 120, 120), 1); - } - - // marking is in progress (left mouse button is ON) - if (draw_select) - { - if (add_id_img != 0) trackbar_value += add_id_img; - - if (y_start >= preview.rows) - { - //full_image.copyTo(full_image_roi); - Rect selected_rect( - Point2i(max(0, (int)min(x_start, x_end)), max(preview.rows, (int)min(y_start, y_end))), - Point2i(max(x_start, x_end), max(y_start, y_end))); - rectangle(frame, selected_rect, Scalar(150, 200, 150)); - - if (show_mark_class) - { - putText(frame, std::to_string(current_obj_id) + current_synset_name, - selected_rect.tl() + Point2i(2, 22), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(150, 200, 150), 2); - } - } - } - - // Draw crosshair - { - const int offset = preview.rows; // Vertical offset - - // Only draw crosshair, if mouse is over image area - if (y_end >= offset) - { - const bool bit_high = true; - const bool bit_low = false; - const int mouse_offset = 25; - const int ver_min = draw_select ? std::min(x_end - mouse_offset, x_start - mouse_offset) : x_end - mouse_offset; - const int ver_max = draw_select ? std::max(x_end + mouse_offset, x_start + mouse_offset) : x_end + mouse_offset; - const int hor_min = draw_select ? std::min(y_end - mouse_offset, y_start - mouse_offset) : y_end - mouse_offset; - const int hor_max = draw_select ? std::max(y_end + mouse_offset, y_start + mouse_offset) : y_end + mouse_offset; - - // Draw crosshair onto empty canvas (draws high bits on low-bit-canvas) - cv::Mat crosshair_mask(frame.size(), CV_8UC1, cv::Scalar(bit_low)); - cv::line(crosshair_mask, cv::Point(0, y_end), cv::Point(ver_min, y_end), cv::Scalar(bit_high)); // Horizontal, left to mouse - cv::line(crosshair_mask, cv::Point(ver_max, y_end), cv::Point(crosshair_mask.size().width, y_end), cv::Scalar(bit_high)); // Horizontal, mouse to right - cv::line(crosshair_mask, cv::Point(x_end, offset), cv::Point(x_end, std::max(offset, hor_min)), cv::Scalar(bit_high)); // Vertical, top to mouse - cv::line(crosshair_mask, cv::Point(x_end, hor_max), cv::Point(x_end, crosshair_mask.size().height), cv::Scalar(bit_high)); // Vertical, mouse to bottom - - // Draw crosshair onto frame copy - cv::Mat crosshair_frame(frame.size(), frame.type()); - frame.copyTo(crosshair_frame); - cv::bitwise_not(crosshair_frame, crosshair_frame, crosshair_mask); - - // Fade-in frame copy with crosshair into original frame (for alpha) - const double alpha = 0.7; - cv::addWeighted(crosshair_frame, alpha, frame, 1 - alpha, 0.0, frame); - } - } - - // remove all labels from this image - if (clear_marks == true) - { - clear_marks = false; - marks_changed = true; - full_image.copyTo(full_image_roi); - current_coord_vec.clear(); - } - - - if (old_current_obj_id != current_obj_id) - { - full_image.copyTo(full_image_roi); - old_current_obj_id = current_obj_id; - setTrackbarPos(trackbar_name_2, window_name, current_obj_id); - } - - int selected_id = -1; - // draw all labels - //for (auto &i : current_coord_vec) - for(size_t k = 0; k < current_coord_vec.size(); ++k) - { - auto &i = current_coord_vec.at(k); - std::string synset_name; - if (i.id < synset_txt.size()) synset_name = " - " + synset_txt[i.id]; - - int offset = i.id * 25; - int red = (offset + 0) % 255 * ((i.id + 2) % 3); - int green = (offset + 70) % 255 * ((i.id + 1) % 3); - int blue = (offset + 140) % 255 * ((i.id + 0) % 3); - Scalar color_rect(red, green, blue); // Scalar color_rect(100, 200, 100); - - // selected rect - if (i.abs_rect.x < x_end && (i.abs_rect.x + i.abs_rect.width) > x_end && - (i.abs_rect.y + preview.rows) < y_end && (i.abs_rect.y + i.abs_rect.height + preview.rows) > y_end) - { - if (selected_id < 0) { - color_rect = Scalar(100, 200, 300); - selected_id = k; - rectangle(full_image_roi, i.abs_rect, color_rect, mark_line_width*2); - } - } - - if (show_mark_class) - { - putText(full_image_roi, std::to_string(i.id) + synset_name, - i.abs_rect.tl() + Point2f(2, 22), FONT_HERSHEY_SIMPLEX, 0.8, color_rect, 2); - } - - rectangle(full_image_roi, i.abs_rect, color_rect, mark_line_width); - } - - // remove selected rect - if (delete_selected) { - delete_selected = false; - if (selected_id >= 0) current_coord_vec.erase(current_coord_vec.begin() + selected_id); - } - - // show moving rect - if (right_button_click == true) - { - if (move_rect_id < 0) move_rect_id = selected_id; - - int x_delta = x_end - x_start; - int y_delta = y_end - y_start; - auto rect = current_coord_vec[move_rect_id].abs_rect; - rect.x += x_delta; - rect.y += y_delta; - - Scalar color_rect = Scalar(300, 200, 100); - rectangle(full_image_roi, rect, color_rect, mark_line_width); - } - - // complete moving label rect - if (move_rect && move_rect_id >= 0) { - int x_delta = x_end - x_start; - int y_delta = y_end - y_start; - current_coord_vec[move_rect_id].abs_rect.x += x_delta; - current_coord_vec[move_rect_id].abs_rect.y += y_delta; - move_rect = false; - move_rect_id = -1; - } - - - if (next_by_click) { - putText(full_image_roi, "Mode: 1 mark per image (next by click)", - Point2i(850, 20), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(50, 170, 100), 2); - } - - - { - std::string const obj_str = "Object id: " + std::to_string(current_obj_id) + current_synset_name; - - putText(full_image_roi, obj_str, Point2i(0, 21), FONT_HERSHEY_DUPLEX, 0.8, Scalar(10, 50, 10), 3); - putText(full_image_roi, obj_str, Point2i(0, 21), FONT_HERSHEY_DUPLEX, 0.8, Scalar(20, 120, 60), 2); - putText(full_image_roi, obj_str, Point2i(0, 21), FONT_HERSHEY_DUPLEX, 0.8, Scalar(50, 200, 100), 1); - } - - if (show_help) - { - putText(full_image_roi, - "<- prev_img -> next_img c - clear_marks n - one_object_per_img 0-9 - obj_id m - show coords ESC - exit", - Point2i(0, 45), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(50, 10, 10), 2); - putText(full_image_roi, - "w - line width k - hide obj_name p - copy previous o - track objects r - delete selected R-mouse - move box", // h - disable help", - Point2i(0, 80), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(50, 10, 10), 2); - } - else - { - putText(full_image_roi, - "h - show help", - Point2i(0, 45), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(50, 10, 10), 2); - } - - - // arrows - { - Scalar prev_arrow_color(200, 150, 100); - Scalar next_arrow_color = prev_arrow_color; - if (prev_img_rect.contains(Point2i(x_end, y_end))) prev_arrow_color = Scalar(220, 190, 170); - if (next_img_rect.contains(Point2i(x_end, y_end))) next_arrow_color = Scalar(220, 190, 170); - - std::vector prev_triangle_pts = { Point(5, 50), Point(40, 90), Point(40, 10), Point(5, 50) }; - Mat prev_roi = frame(prev_img_rect); - line(prev_roi, prev_triangle_pts[0], prev_triangle_pts[1], prev_arrow_color, 5); - line(prev_roi, prev_triangle_pts[1], prev_triangle_pts[2], prev_arrow_color, 5); - line(prev_roi, prev_triangle_pts[2], prev_triangle_pts[3], prev_arrow_color, 5); - line(prev_roi, prev_triangle_pts[3], prev_triangle_pts[0], prev_arrow_color, 5); - - std::vector next_triangle_pts = { Point(10, 10), Point(10, 90), Point(45, 50), Point(10, 10) }; - Mat next_roi = frame(next_img_rect); - line(next_roi, next_triangle_pts[0], next_triangle_pts[1], next_arrow_color, 5); - line(next_roi, next_triangle_pts[1], next_triangle_pts[2], next_arrow_color, 5); - line(next_roi, next_triangle_pts[2], next_triangle_pts[3], next_arrow_color, 5); - line(next_roi, next_triangle_pts[3], next_triangle_pts[0], next_arrow_color, 5); - } - - imshow(window_name, frame); - -#ifndef CV_VERSION_EPOCH - int pressed_key = cv::waitKeyEx(20); // OpenCV 3.x -#else - int pressed_key = cv::waitKey(20); // OpenCV 2.x -#endif - - if (pressed_key >= 0) - for (int i = 0; i < 5; ++i) cv::waitKey(1); - - if (exit_flag) break; // exit after saving - if (pressed_key == 27 || pressed_key == 1048603) exit_flag = true;// break; // ESC - save & exit - - if (pressed_key >= '0' && pressed_key <= '9') current_obj_id = pressed_key - '0'; // 0 - 9 - if (pressed_key >= 1048624 && pressed_key <= 1048633) current_obj_id = pressed_key - 1048624; // 0 - 9 - - switch (pressed_key) - { - //case 'z': // z - //case 1048698: // z - // undo = true; - // break; - - case 'p': // p - case 1048688: // p - copy_previous_marks = 1; - ++trackbar_value; - break; - - case 'o': // o - case 1048687: // o - tracker_copy_previous_marks = 1; - ++trackbar_value; - break; - - case 32: // SPACE - case 1048608: // SPACE - ++trackbar_value; - break; - - case 2424832: // <- - case 65361: // <- - case 91: // [ - --trackbar_value; - break; - case 2555904: // -> - case 65363: // -> - case 93: // ] - ++trackbar_value; - break; - case 'c': // c - case 1048675: // c - clear_marks = true; - break; - case 'm': // m - case 1048685: // m - show_mouse_coords = !show_mouse_coords; - full_image.copyTo(full_image_roi); - break; - case 'n': // n - case 1048686: // n - next_by_click = !next_by_click; - full_image.copyTo(full_image_roi); - break; - case 'w': // w - case 1048695: // w - mark_line_width = mark_line_width % MAX_MARK_LINE_WIDTH + 1; - break; - case 'h': // h - case 1048680: // h - show_help = !show_help; - break; - case 'k': - case 1048683: - show_mark_class = !show_mark_class; - break; - case 'r': // r - case 1048690: // r - delete_selected = true; - break; - default: - ; - } - - if(tracker_copy_previous_marks) - tracker_optflow.update_tracking_flow(optflow_img, current_coord_vec); - - //if (pressed_key >= 0) std::cout << "pressed_key = " << (int)pressed_key << std::endl; - - } while (true); - - } - catch (std::exception &e) { - std::cout << "exception: " << e.what() << std::endl; - } - catch (...) { - std::cout << "unknown exception \n"; - } - - return 0; -} diff --git a/run.cmd b/run.cmd new file mode 100755 index 00000000..4d2b645b --- /dev/null +++ b/run.cmd @@ -0,0 +1,3 @@ +echo How to start marking bounging boxes to create training sets for Yolo + +yolo_mark.exe data/img data/train.txt data/obj.names diff --git a/run.sh b/run.sh new file mode 100755 index 00000000..693d4869 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "How to start marking bounding boxes to create training sets for Yolo" + +./yolo_mark data/img data/train.txt data/obj.names diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..ca527dc7 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,962 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//#include +#include + +#ifndef CV_VERSION_EPOCH +#include "opencv2/videoio/videoio.hpp" +#define OPENCV_VERSION CVAUX_STR(CV_VERSION_MAJOR)"" CVAUX_STR(CV_VERSION_MINOR)"" CVAUX_STR(CV_VERSION_REVISION) OCV_D +#else +#define OPENCV_VERSION CVAUX_STR(CV_VERSION_EPOCH)"" CVAUX_STR(CV_VERSION_MAJOR)"" CVAUX_STR(CV_VERSION_MINOR) OCV_D +#endif + +// label coordinates +struct coord_t { + cv::Rect_ abs_rect; + int id; +}; + +class Tracker_optflow { + public: + const int flow_error; + + Tracker_optflow(int win_size = 15, int max_level = 3, int iterations = 8000, int _flow_error = -1) + : flow_error((_flow_error > 0) ? _flow_error : (win_size * 4)) + { + sync_PyrLKOpticalFlow = cv::SparsePyrLKOpticalFlow::create(); + sync_PyrLKOpticalFlow->setWinSize(cv::Size(win_size, win_size)); // 9, 15, 21, 31 + sync_PyrLKOpticalFlow->setMaxLevel(max_level); // +- 3 pt + } + + // just to avoid extra allocations + cv::Mat dst_grey; + cv::Mat prev_pts_flow, cur_pts_flow; + cv::Mat status, err; + + cv::Mat src_grey; // used in both functions + cv::Ptr sync_PyrLKOpticalFlow; + + std::vector cur_bbox_vec; + std::vector good_bbox_vec_flags; + + void update_cur_bbox_vec(std::vector _cur_bbox_vec) + { + cur_bbox_vec = _cur_bbox_vec; + good_bbox_vec_flags = std::vector(cur_bbox_vec.size(), true); + cv::Mat prev_pts, cur_pts_flow; + + for (auto& i : cur_bbox_vec) { + float x_center = (i.abs_rect.x + i.abs_rect.width / 2.0F); + float y_center = (i.abs_rect.y + i.abs_rect.height / 2.0F); + prev_pts.push_back(cv::Point2f(x_center, y_center)); + } + + if (prev_pts.rows == 0) + prev_pts_flow = cv::Mat(); + else + cv::transpose(prev_pts, prev_pts_flow); + } + + void update_tracking_flow(cv::Mat new_src_mat, std::vector _cur_bbox_vec) + { + if (new_src_mat.channels() == 1) { + src_grey = new_src_mat.clone(); + } else if (new_src_mat.channels() == 3) { + cv::cvtColor(new_src_mat, src_grey, cv::COLOR_BGR2GRAY, 1); + } else if (new_src_mat.channels() == 4) { + cv::cvtColor(new_src_mat, src_grey, cv::COLOR_BGRA2GRAY, 1); + } else { + std::cerr << " Warning: new_src_mat.channels() is not: 1, 3 or 4. It is = " << new_src_mat.channels() << " \n"; + return; + } + update_cur_bbox_vec(_cur_bbox_vec); + } + + std::vector tracking_flow(cv::Mat new_dst_mat, bool check_error = true) + { + if (sync_PyrLKOpticalFlow.empty()) { + std::cout << "sync_PyrLKOpticalFlow isn't initialized \n"; + return cur_bbox_vec; + } + + cv::cvtColor(new_dst_mat, dst_grey, cv::COLOR_BGR2GRAY, 1); + + if (src_grey.rows != dst_grey.rows || src_grey.cols != dst_grey.cols) { + src_grey = dst_grey.clone(); + //std::cerr << " Warning: src_grey.rows != dst_grey.rows || src_grey.cols != dst_grey.cols \n"; + return cur_bbox_vec; + } + + if (prev_pts_flow.cols < 1) { + return cur_bbox_vec; + } + + ////sync_PyrLKOpticalFlow_gpu.sparse(src_grey_gpu, dst_grey_gpu, prev_pts_flow_gpu, cur_pts_flow_gpu, status_gpu, &err_gpu); // OpenCV 2.4.x + sync_PyrLKOpticalFlow->calc(src_grey, dst_grey, prev_pts_flow, cur_pts_flow, status, err); // OpenCV 3.x + + dst_grey.copyTo(src_grey); + + std::vector result_bbox_vec; + + if (err.rows == cur_bbox_vec.size() && status.rows == cur_bbox_vec.size()) { + for (size_t i = 0; i < cur_bbox_vec.size(); ++i) { + cv::Point2f cur_key_pt = cur_pts_flow.at(0, i); + cv::Point2f prev_key_pt = prev_pts_flow.at(0, i); + + float moved_x = cur_key_pt.x - prev_key_pt.x; + float moved_y = cur_key_pt.y - prev_key_pt.y; + + if (abs(moved_x) < 100 && abs(moved_y) < 100 && good_bbox_vec_flags[i]) + if (err.at(0, i) < flow_error && status.at(0, i) != 0 && ((float)cur_bbox_vec[i].abs_rect.x + moved_x) > 0 && ((float)cur_bbox_vec[i].abs_rect.y + moved_y) > 0) { + cur_bbox_vec[i].abs_rect.x += moved_x; // +0.5; + cur_bbox_vec[i].abs_rect.y += moved_y; // +0.5; + result_bbox_vec.push_back(cur_bbox_vec[i]); + } else + good_bbox_vec_flags[i] = false; + else + good_bbox_vec_flags[i] = false; + + //if(!check_error && !good_bbox_vec_flags[i]) result_bbox_vec.push_back(cur_bbox_vec[i]); + } + } + + prev_pts_flow = cur_pts_flow.clone(); + + return result_bbox_vec; + } +}; + +std::atomic right_button_click; +std::atomic move_rect_id; +std::atomic move_rect; +std::atomic clear_marks; +std::atomic copy_previous_marks(false); +std::atomic tracker_copy_previous_marks(false); + +std::atomic show_help; +std::atomic exit_flag(false); + +std::atomic mark_line_width(2); // default mark line width is 2 pixels. +const int MAX_MARK_LINE_WIDTH = 3; +std::atomic show_mark_class(true); +std::atomic delete_selected(false); + +std::atomic x_start, y_start; +std::atomic x_end, y_end; +std::atomic x_size, y_size; +std::atomic draw_select, selected, undo; + +std::atomic add_id_img; +cv::Rect prev_img_rect(0, 0, 50, 100); +cv::Rect next_img_rect(1280 - 50, 0, 50, 100); + +void callback_mouse_click(int event, int x, int y, int flags, void* user_data) +{ + if (event == cv::EVENT_LBUTTONDBLCLK) { + std::cout << "cv::EVENT_LBUTTONDBLCLK \n"; + } else if (event == cv::EVENT_LBUTTONDOWN) { + draw_select = true; + selected = false; + x_start = x; + y_start = y; + + if (prev_img_rect.contains(cv::Point2i(x, y))) + add_id_img = -1; + else if (next_img_rect.contains(cv::Point2i(x, y))) + add_id_img = 1; + else + add_id_img = 0; + //std::cout << "cv::EVENT_LBUTTONDOWN \n"; + } else if (event == cv::EVENT_LBUTTONUP) { + x_size = std::abs(x - x_start); + y_size = std::abs(y - y_start); + x_end = std::max(x, 0); + y_end = std::max(y, 0); + draw_select = false; + selected = true; + //std::cout << "cv::EVENT_LBUTTONUP \n"; + } else if (event == cv::EVENT_RBUTTONDOWN) { + right_button_click = true; + + x_start = x; + y_start = y; + std::cout << "cv::EVENT_RBUTTONDOWN \n"; + } else if (event == cv::EVENT_RBUTTONUP) { + right_button_click = false; + move_rect = true; + } + if (event == cv::EVENT_RBUTTONDBLCLK) { + std::cout << "cv::EVENT_RBUTTONDBLCLK \n"; + } else if (event == cv::EVENT_MOUSEMOVE) { + x_end = std::max(x, 0); + y_end = std::max(y, 0); + } +} + +class comma : public std::numpunct { + public: + comma() + : std::numpunct() + { + } + + protected: + char do_decimal_point() const { return '.'; } +}; + +int main(int argc, char* argv[]) +{ + + try { + std::locale loccomma(std::locale::classic(), new comma); + std::locale::global(loccomma); + + std::string images_path = "./"; + + if (argc >= 2) { + images_path = std::string(argv[1]); // path to images, train and synset + } else { + std::cout << "Usage: [path_to_images] [train.txt] [obj.names] \n"; + return 0; + } + + std::string train_filename = images_path + "train.txt"; + std::string synset_filename = images_path + "obj.names"; + + if (argc >= 3) { + train_filename = std::string(argv[2]); // file containing: list of images + } + + if (argc >= 4) { + synset_filename = std::string(argv[3]); // file containing: object names + } + + // optical flow tracker + Tracker_optflow tracker_optflow; + cv::Mat optflow_img; + + // capture frames from video file - 1 frame per 3 seconds of video + if (argc >= 4 && (train_filename == "cap_video" || train_filename == "cap_video_backward")) { + const std::string videofile = synset_filename; + cv::VideoCapture cap(videofile); +#ifndef CV_VERSION_EPOCH // OpenCV 3.x + const int fps = cap.get(cv::CAP_PROP_FPS); +#else // OpenCV 2.x + const int fps = cap.get(CV_CAP_PROP_FPS); +#endif + int frame_counter = 0, image_counter = 0; + int backward = (train_filename == "cap_video_backward") ? 1 : 0; + if (backward) + image_counter = 99999999; // 99M + float save_each_frames = 50; + if (argc >= 5) + save_each_frames = std::stoul(std::string(argv[4])); + + int pos_filename = 0; + if ((1 + videofile.find_last_of("\\")) < videofile.length()) + pos_filename = 1 + videofile.find_last_of("\\"); + if ((1 + videofile.find_last_of("/")) < videofile.length()) + pos_filename = std::max(pos_filename, 1 + (int)videofile.find_last_of("/")); + std::string const filename = videofile.substr(pos_filename); + std::string const filename_without_ext = filename.substr(0, filename.find_last_of(".")); + + for (cv::Mat frame; cap >> frame, cap.isOpened() && !frame.empty();) { + cv::imshow("video cap to frames", frame); +#ifndef CV_VERSION_EPOCH + int pressed_key = cv::waitKeyEx(20); // OpenCV 3.x +#else + int pressed_key = cv::waitKey(20); // OpenCV 2.x +#endif + if (pressed_key == 27 || pressed_key == 1048603) + break; // ESC - exit (OpenCV 2.x / 3.x) + if (frame_counter++ >= save_each_frames) { // save frame for each 3 second + frame_counter = 0; + std::stringstream image_counter_ss; + image_counter_ss << std::setw(8) << std::setfill('0') << image_counter; + if (backward) + image_counter--; + else + image_counter++; + std::string img_name = images_path + "/" + filename_without_ext + "_" + image_counter_ss.str() + ".jpg"; + std::cout << "saved " << img_name << std::endl; + cv::imwrite(img_name, frame); + } + } + exit(0); + } + + bool show_mouse_coords = false; + std::vector filenames_in_folder; + //glob(images_path, filenames_in_folder); // void glob(String pattern, std::vector& result, bool recursive = false); + cv::String images_path_cv = images_path; + std::vector filenames_in_folder_cv; + cv::glob(images_path_cv, filenames_in_folder_cv); // void glob(String pattern, std::vector& result, bool recursive = false); + for (auto& i : filenames_in_folder_cv) + filenames_in_folder.push_back(i); + + std::vector jpg_filenames_path; + std::vector jpg_filenames; + std::vector jpg_filenames_without_ext; + std::vector image_ext; + std::vector txt_filenames; + std::vector jpg_in_train; + std::vector synset_txt; + + // image-paths to txt-paths + for (auto& i : filenames_in_folder) { + int pos_filename = 0; + if ((1 + i.find_last_of("\\")) < i.length()) + pos_filename = 1 + i.find_last_of("\\"); + if ((1 + i.find_last_of("/")) < i.length()) + pos_filename = std::max(pos_filename, 1 + (int)i.find_last_of("/")); + + std::string const filename = i.substr(pos_filename); + std::string const ext = i.substr(i.find_last_of(".") + 1); + std::string const filename_without_ext = filename.substr(0, filename.find_last_of(".")); + + if (ext == "jpg" || ext == "JPG" || ext == "jpeg" || ext == "JPEG" || ext == "bmp" || ext == "BMP" || ext == "png" || ext == "PNG" || ext == "ppm" || ext == "PPM") { + jpg_filenames_without_ext.push_back(filename_without_ext); + image_ext.push_back(ext); + jpg_filenames.push_back(filename); + jpg_filenames_path.push_back(i); + } + if (ext == "txt") { + txt_filenames.push_back(filename_without_ext); + } + } + std::sort(jpg_filenames.begin(), jpg_filenames.end()); + std::sort(jpg_filenames_path.begin(), jpg_filenames_path.end()); + std::sort(txt_filenames.begin(), txt_filenames.end()); + + if (jpg_filenames.size() == 0) { + std::cout << "Error: Image files not found by path: " << images_path << std::endl; + return 0; + } + + // check whether there are files with the same names (but different extensions) + { + auto sorted_names_without_ext = jpg_filenames_without_ext; + std::sort(sorted_names_without_ext.begin(), sorted_names_without_ext.end()); + for (size_t i = 1; i < sorted_names_without_ext.size(); ++i) { + if (sorted_names_without_ext[i - 1] == sorted_names_without_ext[i]) { + std::cout << "Error: Can't create " << sorted_names_without_ext[i] << ".txt file for several images with different extensions but with the same filename: " + << sorted_names_without_ext[i] << std::endl; + // print duplicate images + for (size_t k = 0; k < jpg_filenames_without_ext.size(); ++k) { + if (jpg_filenames_without_ext[k] == sorted_names_without_ext[i]) { + std::cout << jpg_filenames_without_ext[k] << "." << image_ext[k] << std::endl; + } + } + return 0; + } + } + } + + // intersect jpg & txt + std::vector intersect_filenames(jpg_filenames.size()); + std::vector difference_filenames(jpg_filenames.size()); + std::vector intersect_ext; + std::vector difference_ext; + + auto dif_it_end = std::set_difference(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), + txt_filenames.begin(), txt_filenames.end(), + difference_filenames.begin()); + difference_filenames.resize(dif_it_end - difference_filenames.begin()); + + auto inter_it_end = std::set_intersection(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), + txt_filenames.begin(), txt_filenames.end(), + intersect_filenames.begin()); + intersect_filenames.resize(inter_it_end - intersect_filenames.begin()); + + // get intersect extensions for intersect_filenames + for (auto& i : intersect_filenames) { + size_t ext_index = find(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), i) - jpg_filenames_without_ext.begin(); + intersect_ext.push_back(image_ext[ext_index]); + } + + // get difference extensions for intersect_filenames + for (auto& i : difference_filenames) { + size_t ext_index = find(jpg_filenames_without_ext.begin(), jpg_filenames_without_ext.end(), i) - jpg_filenames_without_ext.begin(); + difference_ext.push_back(image_ext[ext_index]); + } + + txt_filenames.clear(); + for (auto& i : intersect_filenames) { + txt_filenames.push_back(i + ".txt"); + } + + int image_list_count = std::max(1, (int)jpg_filenames_path.size() - 1); + + // store train.txt + std::ofstream ofs_train(train_filename, std::ios::out | std::ios::trunc); + if (!ofs_train.is_open()) { + throw(std::runtime_error("Can't open file: " + train_filename)); + } + + for (size_t i = 0; i < intersect_filenames.size(); ++i) { + ofs_train << images_path << "/" << intersect_filenames[i] << "." << intersect_ext[i] << std::endl; + } + ofs_train.flush(); + std::cout << "File opened for output: " << train_filename << std::endl; + + // load synset.txt + { + std::ifstream ifs(synset_filename); + if (!ifs.is_open()) { + throw(std::runtime_error("Can't open file: " + synset_filename)); + } + + for (std::string line; getline(ifs, line);) + synset_txt.push_back(line); + } + std::cout << "File loaded: " << synset_filename << std::endl; + + cv::Mat preview(cv::Size(100, 100), CV_8UC3); + cv::Mat full_image(cv::Size(1280, 720), CV_8UC3); + cv::Mat frame(cv::Size(full_image.cols, full_image.rows + preview.rows), CV_8UC3); + + cv::Rect full_rect_dst(cv::Point2i(0, preview.rows), cv::Size(frame.cols, frame.rows - preview.rows)); + cv::Mat full_image_roi = frame(full_rect_dst); + + size_t const preview_number = frame.cols / preview.cols; + + // labels on the current image + std::vector current_coord_vec; + cv::Size current_img_size; + + std::string const window_name = "Marking images"; + cv::namedWindow(window_name, cv::WINDOW_NORMAL); + cv::resizeWindow(window_name, 1280, 720); + cv::imshow(window_name, frame); + cv::moveWindow(window_name, 0, 0); + cv::setMouseCallback(window_name, callback_mouse_click); + + bool next_by_click = false; + bool marks_changed = false; + + int old_trackbar_value = -1, trackbar_value = 0; + std::string const trackbar_name = "image num"; + int tb_res = cv::createTrackbar(trackbar_name, window_name, &trackbar_value, image_list_count); + + int old_current_obj_id = -1, current_obj_id = 0; + std::string const trackbar_name_2 = "object id"; + int const max_object_id = (synset_txt.size() > 0) ? synset_txt.size() : 20; + int tb_res_2 = cv::createTrackbar(trackbar_name_2, window_name, ¤t_obj_id, max_object_id); + + do { + //trackbar_value = min(max(0, trackbar_value), (int)jpg_filenames_path.size() - 1); + + // selected new image + if (old_trackbar_value != trackbar_value || exit_flag) { + trackbar_value = std::min(std::max(0, trackbar_value), (int)jpg_filenames_path.size() - 1); + cv::setTrackbarPos(trackbar_name, window_name, trackbar_value); + frame(cv::Rect(0, 0, frame.cols, preview.rows)) = cv::Scalar::all(0); + move_rect_id = -1; + + // save current coords + if (old_trackbar_value >= 0) // && current_coord_vec.size() > 0) // Yolo v2 can processes background-image without objects + { + try { + std::string const jpg_filename = jpg_filenames[old_trackbar_value]; + std::string const filename_without_ext = jpg_filename.substr(0, jpg_filename.find_last_of(".")); + std::string const txt_filename = filename_without_ext + ".txt"; + std::string const txt_filename_path = images_path + "/" + txt_filename; + + std::cout << "txt_filename_path = " << txt_filename_path << std::endl; + + std::ofstream ofs(txt_filename_path, std::ios::out | std::ios::trunc); + ofs << std::fixed; + + // store coords to [image name].txt + for (auto& i : current_coord_vec) { + float const relative_center_x = (float)(i.abs_rect.x + i.abs_rect.width / 2) / full_image_roi.cols; + float const relative_center_y = (float)(i.abs_rect.y + i.abs_rect.height / 2) / full_image_roi.rows; + float const relative_width = (float)i.abs_rect.width / full_image_roi.cols; + float const relative_height = (float)i.abs_rect.height / full_image_roi.rows; + + if (relative_width <= 0) + continue; + if (relative_height <= 0) + continue; + if (relative_center_x <= 0) + continue; + if (relative_center_y <= 0) + continue; + + ofs << i.id << " " << relative_center_x << " " << relative_center_y << " " << relative_width << " " << relative_height << std::endl; + } + + // store [path/image name.jpg] to train.txt + auto it = std::find(difference_filenames.begin(), difference_filenames.end(), filename_without_ext); + if (it != difference_filenames.end()) { + ofs_train << images_path << "/" << jpg_filename << std::endl; + ofs_train.flush(); + + size_t new_size = std::remove(difference_filenames.begin(), difference_filenames.end(), filename_without_ext) - difference_filenames.begin(); + difference_filenames.resize(new_size); + } + } catch (...) { + std::cout << " Exception when try to write txt-file \n"; + } + } + + // show preview images + for (size_t i = 0; i < preview_number && (i + trackbar_value) < jpg_filenames_path.size(); ++i) { + cv::Mat img = cv::imread(jpg_filenames_path[trackbar_value + i]); + // check if the image has been loaded successful to prevent crash + if (img.cols == 0) { + continue; + } + resize(img, preview, preview.size()); + int const x_shift = i * preview.cols + prev_img_rect.width; + cv::Rect rect_dst(cv::Point2i(x_shift, 0), preview.size()); + cv::Mat dst_roi = frame(rect_dst); + preview.copyTo(dst_roi); + //rectangle(frame, rect_dst, Scalar(200, 150, 200), 2); + cv::putText(dst_roi, jpg_filenames[trackbar_value + i], cv::Point2i(0, 10), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar::all(255)); + + if (i == 0) { + optflow_img = img; + resize(img, full_image, full_rect_dst.size()); + full_image.copyTo(full_image_roi); + current_img_size = img.size(); + + try { + std::string const jpg_filename = jpg_filenames[trackbar_value]; + std::string const txt_filename = jpg_filename.substr(0, jpg_filename.find_last_of(".")) + ".txt"; + //std::cout << (images_path + "/" + txt_filename) << std::endl; + std::ifstream ifs(images_path + "/" + txt_filename); + if (copy_previous_marks) + copy_previous_marks = false; + else if (tracker_copy_previous_marks) { + tracker_copy_previous_marks = false; + current_coord_vec = tracker_optflow.tracking_flow(img, false); + } else + current_coord_vec.clear(); + + for (std::string line; getline(ifs, line);) { + std::stringstream ss(line); + coord_t coord; + coord.id = -1; + ss >> coord.id; + if (coord.id < 0) + continue; + float relative_coord[4] = { -1, -1, -1, -1 }; // rel_center_x, rel_center_y, rel_width, rel_height + for (size_t i = 0; i < 4; i++) + if (!(ss >> relative_coord[i])) + continue; + for (size_t i = 0; i < 4; i++) + if (relative_coord[i] < 0) + continue; + coord.abs_rect.x = (relative_coord[0] - relative_coord[2] / 2) * (float)full_image_roi.cols; + coord.abs_rect.y = (relative_coord[1] - relative_coord[3] / 2) * (float)full_image_roi.rows; + coord.abs_rect.width = relative_coord[2] * (float)full_image_roi.cols; + coord.abs_rect.height = relative_coord[3] * (float)full_image_roi.rows; + + current_coord_vec.push_back(coord); + } + } catch (...) { + std::cout << " Exception when try to read txt-file \n"; + } + } + + std::string const jpg_filename = jpg_filenames[trackbar_value + i]; + std::string const filename_without_ext = jpg_filename.substr(0, jpg_filename.find_last_of(".")); + // green check-mark on the preview image if there is a lebel txt-file for this image + if (!std::binary_search(difference_filenames.begin(), difference_filenames.end(), filename_without_ext)) { + line(dst_roi, cv::Point2i(80, 88), cv::Point2i(85, 93), cv::Scalar(20, 70, 20), 5); + line(dst_roi, cv::Point2i(85, 93), cv::Point2i(93, 85), cv::Scalar(20, 70, 20), 5); + + line(dst_roi, cv::Point2i(80, 88), cv::Point2i(85, 93), cv::Scalar(50, 200, 100), 2); + line(dst_roi, cv::Point2i(85, 93), cv::Point2i(93, 85), cv::Scalar(50, 200, 100), 2); + } + } + std::cout << " trackbar_value = " << trackbar_value << std::endl; + + old_trackbar_value = trackbar_value; + + marks_changed = false; + +#ifndef CV_VERSION_EPOCH // OpenCV 3.x + rectangle(frame, prev_img_rect, cv::Scalar(100, 100, 100), cv::FILLED); + rectangle(frame, next_img_rect, cv::Scalar(100, 100, 100), cv::FILLED); +#else // OpenCV 2.x + rectangle(frame, prev_img_rect, cv::Scalar(100, 100, 100), CV_FILLED); + rectangle(frame, next_img_rect, cv::Scalar(100, 100, 100), CV_FILLED); +#endif + } + + trackbar_value = std::min(std::max(0, trackbar_value), (int)jpg_filenames_path.size() - 1); + + // highlight prev img + for (size_t i = 0; i < preview_number && (i + trackbar_value) < jpg_filenames_path.size(); ++i) { + int const x_shift = i * preview.cols + prev_img_rect.width; + cv::Rect rect_dst(cv::Point2i(x_shift, 0), cv::Size(preview.cols - 2, preview.rows)); + cv::Scalar color(100, 70, 100); + if (i == 0) + color = cv::Scalar(250, 120, 150); + if (y_end < preview.rows && i == (x_end - prev_img_rect.width) / preview.cols) + color = cv::Scalar(250, 200, 200); + rectangle(frame, rect_dst, color, 2); + } + + if (undo) { + undo = false; + if (current_coord_vec.size() > 0) { + full_image.copyTo(full_image_roi); + current_coord_vec.pop_back(); + } + } + + // marking is completed (left mouse button is OFF) + if (selected) { + selected = false; + full_image.copyTo(full_image_roi); + + if (y_end < preview.rows && x_end > prev_img_rect.width && x_end < (full_image.cols - prev_img_rect.width) && y_start < preview.rows) { + int const i = (x_end - prev_img_rect.width) / preview.cols; + trackbar_value += i; + } else if (y_end >= preview.rows) { + if (next_by_click) { + ++trackbar_value; + current_coord_vec.clear(); + } + + cv::Rect selected_rect( + cv::Point2i((int)min(x_start, x_end), (int)min(y_start, y_end)), + cv::Size(x_size, y_size)); + + selected_rect &= full_rect_dst; + selected_rect.y -= (int)prev_img_rect.height; + + coord_t coord; + coord.abs_rect = selected_rect; + coord.id = current_obj_id; + current_coord_vec.push_back(coord); + + marks_changed = true; + } + } + + std::string current_synset_name; + if (current_obj_id < synset_txt.size()) + current_synset_name = " - " + synset_txt[current_obj_id]; + + // show X and Y coords of mouse + if (show_mouse_coords) { + full_image.copyTo(full_image_roi); + int const x_inside = std::min((int)x_end, full_image_roi.cols); + int const y_inside = std::min(std::max(0, y_end - (int)prev_img_rect.height), full_image_roi.rows); + float const relative_center_x = (float)(x_inside) / full_image_roi.cols; + float const relative_center_y = (float)(y_inside) / full_image_roi.rows; + int const abs_x = relative_center_x * current_img_size.width; + int const abs_y = relative_center_y * current_img_size.height; + char buff[100]; + snprintf(buff, 100, "Abs: %d x %d Rel: %.3f x %.3f", abs_x, abs_y, relative_center_x, relative_center_y); + //putText(full_image_roi, buff, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(50, 10, 10), 3); + putText(full_image_roi, buff, cv::Point2i(800, 20), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(100, 50, 50), 2); + putText(full_image_roi, buff, cv::Point2i(800, 20), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(220, 120, 120), 1); + } else { + full_image.copyTo(full_image_roi); + //std::string text = "Show mouse coordinates - press M"; + //putText(full_image_roi, text, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(100, 50, 50), 2); + //putText(full_image_roi, text, Point2i(800, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(220, 120, 120), 1); + } + + // marking is in progress (left mouse button is ON) + if (draw_select) { + if (add_id_img != 0) + trackbar_value += add_id_img; + + if (y_start >= preview.rows) { + //full_image.copyTo(full_image_roi); + cv::Rect selected_rect( + cv::Point2i(std::max(0, (int)min(x_start, x_end)), std::max(preview.rows, (int)std::min(y_start, y_end))), + cv::Point2i(std::max(x_start, x_end), std::max(y_start, y_end))); + rectangle(frame, selected_rect, cv::Scalar(150, 200, 150)); + + if (show_mark_class) { + putText(frame, std::to_string(current_obj_id) + current_synset_name, + selected_rect.tl() + cv::Point2i(2, 22), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(150, 200, 150), 2); + } + } + } + + // Draw crosshair + { + const int offset = preview.rows; // Vertical offset + + // Only draw crosshair, if mouse is over image area + if (y_end >= offset) { + const bool bit_high = true; + const bool bit_low = false; + const int mouse_offset = 25; + const int ver_min = draw_select ? std::min(x_end - mouse_offset, x_start - mouse_offset) : x_end - mouse_offset; + const int ver_max = draw_select ? std::max(x_end + mouse_offset, x_start + mouse_offset) : x_end + mouse_offset; + const int hor_min = draw_select ? std::min(y_end - mouse_offset, y_start - mouse_offset) : y_end - mouse_offset; + const int hor_max = draw_select ? std::max(y_end + mouse_offset, y_start + mouse_offset) : y_end + mouse_offset; + + // Draw crosshair onto empty canvas (draws high bits on low-bit-canvas) + cv::Mat crosshair_mask(frame.size(), CV_8UC1, cv::Scalar(bit_low)); + cv::line(crosshair_mask, cv::Point(0, y_end), cv::Point(ver_min, y_end), cv::Scalar(bit_high)); // Horizontal, left to mouse + cv::line(crosshair_mask, cv::Point(ver_max, y_end), cv::Point(crosshair_mask.size().width, y_end), cv::Scalar(bit_high)); // Horizontal, mouse to right + cv::line(crosshair_mask, cv::Point(x_end, offset), cv::Point(x_end, std::max(offset, hor_min)), cv::Scalar(bit_high)); // Vertical, top to mouse + cv::line(crosshair_mask, cv::Point(x_end, hor_max), cv::Point(x_end, crosshair_mask.size().height), cv::Scalar(bit_high)); // Vertical, mouse to bottom + + // Draw crosshair onto frame copy + cv::Mat crosshair_frame(frame.size(), frame.type()); + frame.copyTo(crosshair_frame); + cv::bitwise_not(crosshair_frame, crosshair_frame, crosshair_mask); + + // Fade-in frame copy with crosshair into original frame (for alpha) + const double alpha = 0.7; + cv::addWeighted(crosshair_frame, alpha, frame, 1 - alpha, 0.0, frame); + } + } + + // remove all labels from this image + if (clear_marks == true) { + clear_marks = false; + marks_changed = true; + full_image.copyTo(full_image_roi); + current_coord_vec.clear(); + } + + if (old_current_obj_id != current_obj_id) { + full_image.copyTo(full_image_roi); + old_current_obj_id = current_obj_id; + cv::setTrackbarPos(trackbar_name_2, window_name, current_obj_id); + } + + int selected_id = -1; + // draw all labels + //for (auto &i : current_coord_vec) + for (size_t k = 0; k < current_coord_vec.size(); ++k) { + auto& i = current_coord_vec.at(k); + std::string synset_name; + if (i.id < synset_txt.size()) + synset_name = " - " + synset_txt[i.id]; + + int offset = i.id * 25; + int red = (offset + 0) % 255 * ((i.id + 2) % 3); + int green = (offset + 70) % 255 * ((i.id + 1) % 3); + int blue = (offset + 140) % 255 * ((i.id + 0) % 3); + cv::Scalar color_rect(red, green, blue); // Scalar color_rect(100, 200, 100); + + // selected rect + if (i.abs_rect.x < x_end && (i.abs_rect.x + i.abs_rect.width) > x_end && (i.abs_rect.y + preview.rows) < y_end && (i.abs_rect.y + i.abs_rect.height + preview.rows) > y_end) { + if (selected_id < 0) { + color_rect = cv::Scalar(100, 200, 300); + selected_id = k; + rectangle(full_image_roi, i.abs_rect, color_rect, mark_line_width * 2); + } + } + + if (show_mark_class) { + putText(full_image_roi, std::to_string(i.id) + synset_name, + i.abs_rect.tl() + cv::Point2f(2, 22), cv::FONT_HERSHEY_SIMPLEX, 0.8, color_rect, 2); + } + + rectangle(full_image_roi, i.abs_rect, color_rect, mark_line_width); + } + + // remove selected rect + if (delete_selected) { + delete_selected = false; + if (selected_id >= 0) + current_coord_vec.erase(current_coord_vec.begin() + selected_id); + } + + // show moving rect + if (right_button_click == true) { + if (move_rect_id < 0) + move_rect_id = selected_id; + + int x_delta = x_end - x_start; + int y_delta = y_end - y_start; + auto rect = current_coord_vec[move_rect_id].abs_rect; + rect.x += x_delta; + rect.y += y_delta; + + cv::Scalar color_rect = cv::Scalar(300, 200, 100); + rectangle(full_image_roi, rect, color_rect, mark_line_width); + } + + // complete moving label rect + if (move_rect && move_rect_id >= 0) { + int x_delta = x_end - x_start; + int y_delta = y_end - y_start; + current_coord_vec[move_rect_id].abs_rect.x += x_delta; + current_coord_vec[move_rect_id].abs_rect.y += y_delta; + move_rect = false; + move_rect_id = -1; + } + + if (next_by_click) { + putText(full_image_roi, "Mode: 1 mark per image (next by click)", + cv::Point2i(850, 20), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(50, 170, 100), 2); + } + + { + std::string const obj_str = "Object id: " + std::to_string(current_obj_id) + current_synset_name; + + putText(full_image_roi, obj_str, cv::Point2i(0, 21), cv::FONT_HERSHEY_DUPLEX, 0.8, cv::Scalar(10, 50, 10), 3); + putText(full_image_roi, obj_str, cv::Point2i(0, 21), cv::FONT_HERSHEY_DUPLEX, 0.8, cv::Scalar(20, 120, 60), 2); + putText(full_image_roi, obj_str, cv::Point2i(0, 21), cv::FONT_HERSHEY_DUPLEX, 0.8, cv::Scalar(50, 200, 100), 1); + } + + if (show_help) { + putText(full_image_roi, + "<- prev_img -> next_img c - clear_marks n - one_object_per_img 0-9 - obj_id m - show coords ESC - exit", + cv::Point2i(0, 45), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(50, 10, 10), 2); + putText(full_image_roi, + "w - line width k - hide obj_name p - copy previous o - track objects r - delete selected R-mouse - move box", // h - disable help", + cv::Point2i(0, 80), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(50, 10, 10), 2); + } else { + putText(full_image_roi, + "h - show help", + cv::Point2i(0, 45), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(50, 10, 10), 2); + } + + // arrows + { + cv::Scalar prev_arrow_color(200, 150, 100); + cv::Scalar next_arrow_color = prev_arrow_color; + if (prev_img_rect.contains(cv::Point2i(x_end, y_end))) + prev_arrow_color = cv::Scalar(220, 190, 170); + if (next_img_rect.contains(cv::Point2i(x_end, y_end))) + next_arrow_color = cv::Scalar(220, 190, 170); + + std::vector prev_triangle_pts = { cv::Point(5, 50), cv::Point(40, 90), cv::Point(40, 10), cv::Point(5, 50) }; + cv::Mat prev_roi = frame(prev_img_rect); + line(prev_roi, prev_triangle_pts[0], prev_triangle_pts[1], prev_arrow_color, 5); + line(prev_roi, prev_triangle_pts[1], prev_triangle_pts[2], prev_arrow_color, 5); + line(prev_roi, prev_triangle_pts[2], prev_triangle_pts[3], prev_arrow_color, 5); + line(prev_roi, prev_triangle_pts[3], prev_triangle_pts[0], prev_arrow_color, 5); + + std::vector next_triangle_pts = { cv::Point(10, 10), cv::Point(10, 90), cv::Point(45, 50), cv::Point(10, 10) }; + cv::Mat next_roi = frame(next_img_rect); + line(next_roi, next_triangle_pts[0], next_triangle_pts[1], next_arrow_color, 5); + line(next_roi, next_triangle_pts[1], next_triangle_pts[2], next_arrow_color, 5); + line(next_roi, next_triangle_pts[2], next_triangle_pts[3], next_arrow_color, 5); + line(next_roi, next_triangle_pts[3], next_triangle_pts[0], next_arrow_color, 5); + } + + imshow(window_name, frame); + +#ifndef CV_VERSION_EPOCH + int pressed_key = cv::waitKeyEx(20); // OpenCV 3.x +#else + int pressed_key = cv::waitKey(20); // OpenCV 2.x +#endif + + if (pressed_key >= 0) + for (int i = 0; i < 5; ++i) + cv::waitKey(1); + + if (exit_flag) + break; // exit after saving + if (pressed_key == 27 || pressed_key == 1048603) + exit_flag = true; // break; // ESC - save & exit + + if (pressed_key >= '0' && pressed_key <= '9') + current_obj_id = pressed_key - '0'; // 0 - 9 + if (pressed_key >= 1048624 && pressed_key <= 1048633) + current_obj_id = pressed_key - 1048624; // 0 - 9 + + switch (pressed_key) { + //case 'z': // z + //case 1048698: // z + // undo = true; + // break; + + case 'p': // p + case 1048688: // p + copy_previous_marks = 1; + ++trackbar_value; + break; + + case 'o': // o + case 1048687: // o + tracker_copy_previous_marks = 1; + ++trackbar_value; + break; + + case 32: // SPACE + case 1048608: // SPACE + ++trackbar_value; + break; + + case 2424832: // <- + case 65361: // <- + case 91: // [ + --trackbar_value; + break; + case 2555904: // -> + case 65363: // -> + case 93: // ] + ++trackbar_value; + break; + case 'c': // c + case 1048675: // c + clear_marks = true; + break; + case 'm': // m + case 1048685: // m + show_mouse_coords = !show_mouse_coords; + full_image.copyTo(full_image_roi); + break; + case 'n': // n + case 1048686: // n + next_by_click = !next_by_click; + full_image.copyTo(full_image_roi); + break; + case 'w': // w + case 1048695: // w + mark_line_width = mark_line_width % MAX_MARK_LINE_WIDTH + 1; + break; + case 'h': // h + case 1048680: // h + show_help = !show_help; + break; + case 'k': + case 1048683: + show_mark_class = !show_mark_class; + break; + case 'r': // r + case 1048690: // r + delete_selected = true; + break; + default:; + } + + if (tracker_copy_previous_marks) + tracker_optflow.update_tracking_flow(optflow_img, current_coord_vec); + + //if (pressed_key >= 0) std::cout << "pressed_key = " << (int)pressed_key << std::endl; + + } while (true); + + } catch (std::exception& e) { + std::cout << "exception: " << e.what() << std::endl; + } catch (...) { + std::cout << "unknown exception \n"; + } + + return 0; +} diff --git a/train.cmd b/train.cmd new file mode 100755 index 00000000..87c06619 --- /dev/null +++ b/train.cmd @@ -0,0 +1,3 @@ +echo How to start training Darknet Yolo objects (air, bird) + +darknet.exe detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23 diff --git a/x64/Release/train_obj.cmd b/x64/Release/train_obj.cmd deleted file mode 100644 index e5bf36c3..00000000 --- a/x64/Release/train_obj.cmd +++ /dev/null @@ -1,7 +0,0 @@ -echo Example how to start training Darknet Yolo v2 for 2 objects (air, bird) - - -darknet.exe detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23 - - -pause \ No newline at end of file diff --git a/x64/Release/yolo_mark.cmd b/x64/Release/yolo_mark.cmd deleted file mode 100644 index e412cf28..00000000 --- a/x64/Release/yolo_mark.cmd +++ /dev/null @@ -1,7 +0,0 @@ -echo Example how to start marking bouded boxes for training set Yolo v2 - - -yolo_mark.exe data/img data/train.txt data/obj.names - - -pause \ No newline at end of file diff --git a/x64/Release/yolo-obj.cfg b/yolo-obj.cfg similarity index 100% rename from x64/Release/yolo-obj.cfg rename to yolo-obj.cfg diff --git a/yolo_mark.sln b/yolo_mark.sln deleted file mode 100644 index e36777d6..00000000 --- a/yolo_mark.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yolo_mark", "yolo_mark.vcxproj", "{71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Debug|x64.ActiveCfg = Debug|x64 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Debug|x64.Build.0 = Debug|x64 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Debug|x86.ActiveCfg = Debug|Win32 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Debug|x86.Build.0 = Debug|Win32 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Release|x64.ActiveCfg = Release|x64 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Release|x64.Build.0 = Release|x64 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Release|x86.ActiveCfg = Release|Win32 - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/yolo_mark.vcxproj b/yolo_mark.vcxproj deleted file mode 100644 index 187a3fb3..00000000 --- a/yolo_mark.vcxproj +++ /dev/null @@ -1,130 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {71E25DC4-69FF-4FE2-9DDA-0517AEF2AEAD} - OpenCV_mark_imgs - yolo_mark - - - - Application - true - v140 - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - Level3 - Disabled - true - - - true - - - - - Level3 - Disabled - true - $(OPENCV_DIR)\include;C:\opencv_3.0\opencv\build\include - - - true - $(OPENCV_DIR)\x64\vc15\lib;$(OPENCV_DIR)\x64\vc14\lib;C:\opencv_3.0\opencv\build\x64\vc14\lib;C:\opencv_2.4.13\opencv\build\x64\vc12\lib;%(AdditionalLibraryDirectories) - - - - - - Level3 - MaxSpeed - true - true - true - Async - - - true - true - true - - - - - Level3 - MaxSpeed - true - true - true - $(OPENCV_DIR)\include;C:\opencv_3.0\opencv\build\include - - - true - true - true - $(OutDir)$(TargetName)$(TargetExt) - $(OPENCV_DIR)\x64\vc15\lib;$(OPENCV_DIR)\x64\vc14\lib;C:\opencv_3.0\opencv\build\x64\vc14\lib;C:\opencv_2.4.13\opencv\build\x64\vc12\lib;%(AdditionalLibraryDirectories) - - - - - - - - - \ No newline at end of file