commit 7afc14d0ebc8a1d7af16e6acd30bfb6840da6792 Author: cailean Date: Sun Jun 1 12:29:37 2025 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4dff236 --- /dev/null +++ b/.gitignore @@ -0,0 +1,89 @@ +#.gitignore is a file which makes git ignore files which should +# not go into version control in the first place +# Questions? See +# http://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Ignoring-Files +# http://git-scm.com/docs/gitignore + +########################### +# ignore generated binaries +# but not the data folder +########################### + +/bin/* +!/bin/data/ + +######### +# general +######### + +[Bb]uild/ +[Oo]bj/ +*.o +[Dd]ebug*/ +[Rr]elease*/ +*.mode* +*.app/ +*.pyc +.svn/ +*.log + +######################## +# IDE files which should +# be ignored +######################## + +# XCode +*.pbxuser +*.perspective +*.perspectivev3 +*.mode1v3 +*.mode2v3 +# XCode 4 +*.xccheckout +xcuserdata/ + +# Visual Studio +*.sdf +*.opensdf +*.suo +*.pdb +*.ilk +*.aps +ipch/ + +# Eclipse +.metadata +local.properties +.externalToolBuilders + +# Android Studio +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries + +################## +# operating system +################## + +# Linux +*~ +# KDE +.directory +.AppleDouble + +# OSX +.DS_Store +*.swp +*~.nib +# Thumbnails +._* + +# Windows +# Image file caches +Thumbs.db +# Folder config file +Desktop.ini + +# Android +.csettings \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..54d2a1f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,260 @@ +{ + "configurations": [ + { + "browse": { + "databaseFilename": "${workspaceRoot}/.vscode/browse.db", + "limitSymbolsToIncludedHeaders": true, + "path": [ + "/usr/local/include", + "/System/Library/Frameworks", + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/", + "${workspaceFolder}/src/**", + "${OF_INCLUDE}", + "${OF_LIBS_INCLUDE}", + "${PROJECT_ADDON_INCLUDES}", + "${PROJECT_EXTRA_INCLUDES}" + ] + }, + "cStandard": "c17", + "compilerPath": "/usr/bin/gcc", + "configurationProvider": "ms-vscode.makefile-tools", + "cppStandard": "c++17", + "includePath": [ + "/usr/local/include", + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/", + "${OF_INCLUDE}", + "${OF_LIBS_INCLUDE}", + "${workspaceFolder}/src/**", + "${PROJECT_ADDON_INCLUDES}", + "${PROJECT_EXTRA_INCLUDES}" + ], + "intelliSenseMode": "${default}", + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ], + "mergeConfigurations": true, + "name": "Mac" + }, + { + "browse": { + "databaseFilename": "", + "limitSymbolsToIncludedHeaders": true, + "path": [ + "/usr/local/include", + "/usr/include", + "${workspaceFolder}/src/**", + "${OF_INCLUDE}", + "${OF_LIBS_INCLUDE}", + "${PROJECT_ADDON_INCLUDES}", + "${PROJECT_EXTRA_INCLUDES}" + ] + }, + "cStandard": "c17", + "compilerPath": "/usr/bin/gcc", + "configurationProvider": "ms-vscode.makefile-tools", + "cppStandard": "c++17", + "includePath": [ + "/usr/include", + "/usr/local/include", + "/usr/include/c++/*", + "/usr/lib/gcc/x86_64-linux-gnu/*", + "/usr/lib/x86_64-linux-gnu/glib-2.0/include", + "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "${OF_INCLUDE}", + "${OF_LIBS_INCLUDE}", + "${workspaceRoot}/../../../libs/kiss/include", + "/usr/include/x86_64-linux-gnu/c++/*", + "/usr/include/pulse", + "/usr/include/cairo", + "/usr/include/gstreamer-1.0", + "/usr/include/glib-2.0", + "${workspaceRoot}", + "${workspaceFolder}/src/**", + "${PROJECT_ADDON_INCLUDES}", + "${PROJECT_EXTRA_INCLUDES}" + ], + "intelliSenseMode": "${default}", + "mergeConfigurations": true, + "name": "Linux" + }, + { + "browse": { + "databaseFilename": "", + "limitSymbolsToIncludedHeaders": true, + "path": [ + "${workspaceFolder}/src/**", + "${OF_INCLUDE}", + "${OF_LIBS_INCLUDE}", + "${PROJECT_ADDON_INCLUDES}", + "${PROJECT_EXTRA_INCLUDES}" + ] + }, + "cStandard": "c17", + "compilerPath": "C:/msys64/mingw64/bin/g++.exe", + "configurationProvider": "ms-vscode.makefile-tools", + "cppStandard": "c++17", + "includePath": [ + "C:/msys64/mingw64/include/c++/**", + "C:/msys64/mingw64/i686-w64-mingw64/include", + "${OF_INCLUDE}", + "${OF_LIBS_INCLUDE}", + "${workspaceFolder}/src/**", + "${PROJECT_ADDON_INCLUDES}", + "${PROJECT_EXTRA_INCLUDES}" + ], + "intelliSenseMode": "clang-x64", + "mergeConfigurations": true, + "name": "Win32" + } + ], + "env": { + "OF_INCLUDE": [ + "${OF_LIBS_ROOT}/openFrameworks/**" + ], + "OF_LIBS_INCLUDE": [ + "${OF_LIBS_ROOT}/cairo/include/cairo", + "${OF_LIBS_ROOT}/curl/include", + "${OF_LIBS_ROOT}/fmod/include", + "${OF_LIBS_ROOT}/FreeImage/include", + "${OF_LIBS_ROOT}/freetype/include", + "${OF_LIBS_ROOT}/glew/include", + "${OF_LIBS_ROOT}/glfw/include", + "${OF_LIBS_ROOT}/glm/include", + "${OF_LIBS_ROOT}/json/include", + "${OF_LIBS_ROOT}/pugixml/include", + "${OF_LIBS_ROOT}/rtAudio/include", + "${OF_LIBS_ROOT}/tess2/include", + "${OF_LIBS_ROOT}/uriparser/include", + "${OF_LIBS_ROOT}/utf8/include" + ], + "OF_LIBS_ROOT": "${OF_ROOT}/libs", + "OF_ROOT": "${workspaceFolder}/../../..", + "PROJECT_ADDON_INCLUDES": [], + "PROJECT_EXTRA_INCLUDES": [ + "${workspaceRoot}/..\\..\\..\\addons\\ofxGui\\src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxNetwork\\src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOnnxRuntime\\libs\\onnxruntime\\include", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOnnxRuntime\\src", + "${workspaceRoot}/src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxGui\\src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxNetwork\\src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\etc", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\aruco", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\bioinspired", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\calib3d", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\cuda", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\cuda\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\hal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\opencl", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\opencl\\runtime", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\opencl\\runtime\\autogenerated", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\parallel", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\parallel\\backend", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\core\\utils", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\dnn", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\dnn\\utils", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\features2d", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\features2d\\hal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\flann", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\fuzzy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\img_hash", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\imgcodecs", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\imgcodecs\\legacy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\imgproc", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\imgproc\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\imgproc\\hal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\line_descriptor", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\mcc", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\objdetect", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\optflow", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\phase_unwrapping", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\reg", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\saliency", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\signal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\stereo", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\structured_light", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\surface_matching", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\tracking", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\video", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\video\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\video\\legacy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\xfeatures2d", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\include\\opencv4\\opencv2\\ximgproc", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\calib3d", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\cuda", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\cuda\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\hal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\llapi", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\opencl", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\opencl\\runtime", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\opencl\\runtime\\autogenerated", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\openvx", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\parallel", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\parallel\\backend", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\private", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\core\\utils", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\dnn", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\dnn\\utils", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\features2d", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\features2d\\hal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\flann", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\cpu", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\fluid", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\gpu", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\infer", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\oak", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\ocl", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\own", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\plaidml", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\python", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\render", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\s11n", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\streaming", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\streaming\\gstreamer", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\streaming\\onevpl", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\gapi\\util", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\highgui", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\imgcodecs", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\imgcodecs\\legacy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\imgproc", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\imgproc\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\imgproc\\hal", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\ml", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\objdetect", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\photo", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\photo\\legacy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\stitching", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\stitching\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\ts", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\video", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\video\\detail", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\video\\legacy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\videoio", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\videoio\\doc", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\include\\opencv2\\videoio\\legacy", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\lib", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\libs\\opencv\\license", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOpenCv\\src", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOnnxRuntime\\libs\\onnxruntime\\include", + "${workspaceRoot}/..\\..\\..\\addons\\ofxOnnxRuntime\\src" + ] + }, + "version": 4 +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e5b6ac7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-vscode.makefile-tools", + "ms-vscode.cpptools" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e535136 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,72 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C++ Launch", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceRoot}", + "environment": [], + "externalConsole": false, + "linux": { + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "program": "${workspaceRoot}/bin/${workspaceFolderBasename}_debug" + }, + "osx": { + "MIMode": "lldb", + "program": "${workspaceRoot}/bin/${workspaceFolderBasename}_debug.app/Contents/MacOS/${workspaceFolderBasename}_debug" + }, + "windows": { + "MIMode": "gdb", + "miDebuggerPath": "gdb.exe", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "program": "${workspaceRoot}/bin/${workspaceFolderBasename}_debug" + } + }, + { + "name": "C++ Attach", + "type": "cppdbg", + "request": "attach", + "program": "enter program name, for example ${workspaceRoot}/a.out", + "processId": "${command:pickProcess}", + "linux": { + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + "osx": { + "MIMode": "lldb" + }, + "windows": { + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a222128 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,92 @@ +{ + + "version": "2.0.0", + "shell":{ + "task": true + }, + "windows" : { + "options": { + "shell": { + "executable": "C:\\msys64\\msys2_shell.cmd", + "args": [ + "-defterm", + "-mingw64", + "-no-start", + "-here", + "-shell bash -c" + ] + } + } + }, + "problemMatcher": { + "owner": "cpp", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + + "group": { + "kind": "build", + }, + + "presentation": { + "reveal": "always", + "panel": "shared" + }, + + "tasks": [ + { + "type": "shell", + "label": "Build RELEASE", + "command": "make -j -s 2>&1" + // "windows" : { + // "command" : "msbuild" + // } + }, + { + "type": "shell", + "label": "Run RELEASE", + "command": "make RunRelease >&1", + "windows" : { + "command" : "bin/*.exe" + } + }, + { + "label": "Build and Run Release", + "dependsOn": ["Build RELEASE", "Run RELEASE"], + "dependsOrder": "sequence", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "label": "Build DEBUG", + "command": "make Debug -j -s 2>&1 || exit 1" + }, + + { + "type": "shell", + "label": "Clean DEBUG", + "command": "make CleanDebug" + }, + + { + "type": "shell", + "label": "Clean RELEASE", + "command": "make CleanRelease" + }, + { + "type": "shell", + "label": "Clean ALL", + "command": "make clean" + } + ] +} diff --git a/MacbethCV.code-workspace b/MacbethCV.code-workspace new file mode 100644 index 0000000..44385fe --- /dev/null +++ b/MacbethCV.code-workspace @@ -0,0 +1,143 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "${workspaceRoot}/../../../../libs/openFrameworks" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxGui" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxNetwork" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxOpenCv" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxOnnxRuntime" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxGui" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxNetwork" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxOpenCv" + }, + { + "path": "${workspaceRoot}/../..\\..\\..\\addons\\ofxOnnxRuntime" + } + ], + "openFrameworksProjectGeneratorVersion": "0.103.0", + "settings": { + "files.associations": { + "algorithm": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "coroutine": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "filesystem": "cpp", + "format": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "random": "cpp", + "ranges": "cpp", + "ratio": "cpp", + "regex": "cpp", + "semaphore": "cpp", + "set": "cpp", + "shared_mutex": "cpp", + "source_location": "cpp", + "span": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "strstream": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "valarray": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "string_view": "cpp", + "stdfloat": "cpp", + "text_encoding": "cpp" + } + } +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d2f8482 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +# Attempt to load a config.make file. +# If none is found, project defaults in config.project.make will be used. +ifneq ($(wildcard config.make),) + include config.make +endif + +# make sure the the OF_ROOT location is defined +ifndef OF_ROOT + OF_ROOT=../../.. +endif + +# call the project makefile! +include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/addons.make b/addons.make new file mode 100644 index 0000000..d45058d --- /dev/null +++ b/addons.make @@ -0,0 +1,4 @@ +ofxGui +ofxNetwork +ofxOpenCv +ofxOnnxRuntime diff --git a/bin/data/.gitkeep b/bin/data/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/bin/data/.gitkeep @@ -0,0 +1 @@ + diff --git a/bin/data/compressed-test-video.mp4 b/bin/data/compressed-test-video.mp4 new file mode 100644 index 0000000..16909ae Binary files /dev/null and b/bin/data/compressed-test-video.mp4 differ diff --git a/bin/data/fer.onnx b/bin/data/fer.onnx new file mode 100644 index 0000000..cdd6a60 Binary files /dev/null and b/bin/data/fer.onnx differ diff --git a/bin/data/test-video.mp4 b/bin/data/test-video.mp4 new file mode 100644 index 0000000..ec49752 Binary files /dev/null and b/bin/data/test-video.mp4 differ diff --git a/bin/data/yolov5s-face.onnx b/bin/data/yolov5s-face.onnx new file mode 100644 index 0000000..f926ca3 Binary files /dev/null and b/bin/data/yolov5s-face.onnx differ diff --git a/config.make b/config.make new file mode 100644 index 0000000..1b2fc8f --- /dev/null +++ b/config.make @@ -0,0 +1,145 @@ +################################################################################ +# CONFIGURE PROJECT MAKEFILE (optional) +# This file is where we make project specific configurations. +################################################################################ + +################################################################################ +# OF ROOT +# The location of your root openFrameworks installation +# (default) OF_ROOT = ../../.. +################################################################################ +OF_ROOT = ../../.. + +################################################################################ +# PROJECT ROOT +# The location of the project - a starting place for searching for files +# (default) PROJECT_ROOT = . (this directory) +# +################################################################################ +# PROJECT_ROOT = . + +################################################################################ +# PROJECT SPECIFIC CHECKS +# This is a project defined section to create internal makefile flags to +# conditionally enable or disable the addition of various features within +# this makefile. For instance, if you want to make changes based on whether +# GTK is installed, one might test that here and create a variable to check. +################################################################################ +# None + +################################################################################ +# PROJECT EXTERNAL SOURCE PATHS +# These are fully qualified paths that are not within the PROJECT_ROOT folder. +# Like source folders in the PROJECT_ROOT, these paths are subject to +# exlclusion via the PROJECT_EXLCUSIONS list. +# +# (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXTERNAL_SOURCE_PATHS = + +################################################################################ +# PROJECT EXCLUSIONS +# These makefiles assume that all folders in your current project directory +# and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations +# to look for source code. The any folders or files that match any of the +# items in the PROJECT_EXCLUSIONS list below will be ignored. +# +# Each item in the PROJECT_EXCLUSIONS list will be treated as a complete +# string unless teh user adds a wildcard (%) operator to match subdirectories. +# GNU make only allows one wildcard for matching. The second wildcard (%) is +# treated literally. +# +# (default) PROJECT_EXCLUSIONS = (blank) +# +# Will automatically exclude the following: +# +# $(PROJECT_ROOT)/bin% +# $(PROJECT_ROOT)/obj% +# $(PROJECT_ROOT)/%.xcodeproj +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXCLUSIONS = + +################################################################################ +# PROJECT LINKER FLAGS +# These flags will be sent to the linker when compiling the executable. +# +# (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ + +# Currently, shared libraries that are needed are copied to the +# $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to +# add a runtime path to search for those shared libraries, since they aren't +# incorporated directly into the final executable application binary. +# TODO: should this be a default setting? +# PROJECT_LDFLAGS=-Wl,-rpath=./libs + +################################################################################ +# PROJECT DEFINES +# Create a space-delimited list of DEFINES. The list will be converted into +# CFLAGS with the "-D" flag later in the makefile. +# +# (default) PROJECT_DEFINES = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_DEFINES = + +################################################################################ +# PROJECT CFLAGS +# This is a list of fully qualified CFLAGS required when compiling for this +# project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS +# defined in your platform specific core configuration files. These flags are +# presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below. +# +# (default) PROJECT_CFLAGS = (blank) +# +# Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in +# your platform specific configuration file will be applied by default and +# further flags here may not be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CFLAGS = + +################################################################################ +# PROJECT OPTIMIZATION CFLAGS +# These are lists of CFLAGS that are target-specific. While any flags could +# be conditionally added, they are usually limited to optimization flags. +# These flags are added BEFORE the PROJECT_CFLAGS. +# +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank) +# +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank) +# +# Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the +# PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration +# file will be applied by default and further optimization flags here may not +# be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE = +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG = + +################################################################################ +# PROJECT COMPILERS +# Custom compilers can be set for CC and CXX +# (default) PROJECT_CXX = (blank) +# (default) PROJECT_CC = (blank) +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CXX = +# PROJECT_CC = + +# vscode template + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0df6a15 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,17 @@ +#include "ofMain.h" +#include "ofApp.h" + +//======================================================================== +int main( ){ + + //Use ofGLFWWindowSettings for more options like multi-monitor fullscreen + ofGLWindowSettings settings; + settings.setSize(1920, 1080); + settings.windowMode = OF_WINDOW; //can also be OF_FULLSCREEN + + auto window = ofCreateWindow(settings); + + ofRunApp(window, std::make_shared()); + ofRunMainLoop(); + +} diff --git a/src/ofApp.cpp b/src/ofApp.cpp new file mode 100644 index 0000000..13d4ae9 --- /dev/null +++ b/src/ofApp.cpp @@ -0,0 +1,46 @@ +#include "ofApp.h" + +//-------------------------------------------------------------- +void ofApp::setup(){ + ofSetFrameRate(60); + + videoPlayer.load(videoPath); + videoPlayer.setLoopState(OF_LOOP_NORMAL); + videoPlayer.play(); + + inputImage.allocate(640, 640, OF_IMAGE_COLOR); // Allocate image, so we don't get any issues when processing on the thread + videoFrame.allocate(1920, 1080, OF_IMAGE_COLOR); + + onnx.setup(&inputImage); // setup onnx -> will need to pass in a pointer to the two fbos? +} + +//-------------------------------------------------------------- +void ofApp::update(){ + videoPlayer.update(); + if(videoPlayer.isFrameNew()) { + ofPixels & p = videoPlayer.getPixels(); + videoFrame.setFromPixels(p); + } + onnx.update(videoFrame); +} + +//-------------------------------------------------------------- +void ofApp::draw(){ + //videoPlayer.draw(0, 0, 1920, 1080); + onnx.draw(); +} + +//-------------------------------------------------------------- +void ofApp::exit(){ + +} + +//-------------------------------------------------------------- +void ofApp::keyPressed(int key){ + +} + +//-------------------------------------------------------------- +void ofApp::keyReleased(int key){ + +} \ No newline at end of file diff --git a/src/ofApp.h b/src/ofApp.h new file mode 100644 index 0000000..39098e4 --- /dev/null +++ b/src/ofApp.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ofMain.h" +#include "onxProcess.h" + +class ofApp : public ofBaseApp{ + + public: + void setup() override; + void update() override; + void draw() override; + void exit() override; + + void keyPressed(int key) override; + void keyReleased(int key) override; + + ofVideoPlayer videoPlayer; + std::string videoPath = "compressed-test-video.mp4"; + onxProcess onnx; + + ofImage inputImage; + ofImage videoFrame; +}; diff --git a/src/ofYolo.cpp b/src/ofYolo.cpp new file mode 100644 index 0000000..8ce3e4d --- /dev/null +++ b/src/ofYolo.cpp @@ -0,0 +1,175 @@ +#include "ofYolo.h" + +// Takes output tensor data, processes that data, and returns an array of BoxfWithLandmarks (detected_faces) +void ofYolo::ParseOutput(float* &output_tensors, std::vector &sorted_faces, unsigned int num_anchors) { + + std::vector detected_faces; + + for (unsigned int i = 0; i < num_anchors; ++i) { + const float *row_ptr = output_tensors + i * 16; // Each row contains 16 values + float obj_conf = row_ptr[4]; // Objectness confidence + + if (obj_conf < 0.5) continue; // Filter out low-confidence detections + + // Extract bounding box, confidence, and landmarks + float cx = row_ptr[0]; + float cy = row_ptr[1]; + float w = row_ptr[2]; + float h = row_ptr[3]; + float cls_conf = row_ptr[15]; // Face confidence score + + if (cls_conf < 0.5) continue; // Filter by class confidence + + BoxfWithLandmarks face; + face.box.x1 = cx - w / 2; + face.box.y1 = cy - h / 2; + face.box.x2 = cx + w / 2; + face.box.y2 = cy + h / 2; + face.box.score = cls_conf; + face.box.label_text = "face"; + + // Extract landmarks + for (int j = 0; j < 10; j += 2) { + face.landmarks.points.push_back(cv::Point2f(row_ptr[5 + j], row_ptr[5 + j + 1])); + } + + detected_faces.push_back(face); // Store the detected face with landmarks + } + + // Apply NMS to detected faces list to remove any overlapping bounding boxes. + NonMaximumSuppression(detected_faces, sorted_faces, 0.5); + + // Sort faces based on confidence value + SortDetectedFaces(sorted_faces); +} + +// Simple helper for drawing boxes given x1, y1, x2, y2 coordinates. +void ofYolo::DrawBox(std::vector &detected_faces){ + for (const auto &face : detected_faces) { + ofPushStyle(); + ofNoFill(); + ofSetColor(ofColor::cyan); + ofDrawRectangle(face.box.x1, face.box.y1, ((face.box.x2) - (face.box.x1)), face.box.y2 - face.box.y1); + ofPopStyle(); + } +} + +// Simple helper to draw boxes at the center of the detected face. +void ofYolo::DrawCenter(std::vector &detected_faces){ + ofNoFill(); + glm::vec2 position = detected_faces[0].box.center; + ofDrawCircle(position.x + ofGetWindowWidth() / 2, position.y, 5); + +} + +// Applies NMS to an array of BoxfWithLandmarks.face.boxes, to remove any overlapping bounding boxes. +void ofYolo::NonMaximumSuppression(std::vector &input_faces, std::vector &output_faces, float iou_threshold) +{ + // Sort the boxes by their confidence scores (highest to lowest) + std::sort(input_faces.begin(), input_faces.end(), + [](const BoxfWithLandmarks &a, const BoxfWithLandmarks &b) { + return a.box.score > b.box.score; + }); + + std::vector suppressed(input_faces.size(), 0); // Suppression mask + + // Iterate through the boxes + for (size_t i = 0; i < input_faces.size(); ++i) { + if (suppressed[i]) continue; // Skip already suppressed boxes + + // Add this box to the output + output_faces.push_back(input_faces[i]); + + for (size_t j = i + 1; j < input_faces.size(); ++j) { + if (suppressed[j]) continue; + + // Calculate IoU between box i and box j + float iou = input_faces[i].box.iou_of(input_faces[j].box); + + // Suppress box j if IoU is greater than the threshold + if (iou > iou_threshold) { + suppressed[j] = 1; + } + } + } +} + +// Scales the coordinates of detected faces from the model output dimensions -> the original input image dimensions. +void ofYolo::ConvertBoxCoordsToOriginalSize(std::vector &detected_faces, size_t original_width, size_t original_height){ + float width_scale = static_cast(original_width) / modelSize; + float height_scale = static_cast(original_height) / modelSize; + + for (auto &face : detected_faces) { + // Convert bounding box coordinates + face.box.x1 *= width_scale; + face.box.y1 *= height_scale; + face.box.x2 *= width_scale; + face.box.y2 *= height_scale; + face.box.UpdateCenter(); + + // Expansion factor + float expansion_factor = 1.0f; + + // Find current dimensions + float current_width = face.box.x2 - face.box.x1; + float current_height = face.box.y2 - face.box.y1; + + // Use width as the base for the 1:1.5 ratio + float base_width = max(current_width, current_height); + float new_width = base_width; + float new_height = base_width * 1.5f; // Make height 1.5 times the width + + // Apply expansion + new_width *= expansion_factor; + new_height *= expansion_factor; + + // Recalculate coordinates maintaining the center + face.box.x1 = face.box.center.x - (new_width / 2.0f); + face.box.x2 = face.box.center.x + (new_width / 2.0f); + face.box.y1 = face.box.center.y - (new_height / 2.0f); + face.box.y2 = face.box.center.y + (new_height / 2.0f); + } + SortDetectedFaces(detected_faces); +} + +void ofYolo::CropFaceToImage(ofImage &inputImage, BoxfWithLandmarks &face, ofImage &colorImage, int imageSize){ + + //colorImage.resetROI(); + + // Calculate the coordinates and dimensions of the face box + float x1 = face.box.x1; + float y1 = face.box.y1; + float x2 = face.box.x2; + float y2 = face.box.y2; + + // Ensure coordinates are within the input image bounds + x1 = ofClamp(x1, 0.0f, (float)inputImage.getWidth()); + y1 = ofClamp(y1, 0.0f, (float)inputImage.getHeight()); + x2 = ofClamp(x2, 0.0f, (float)inputImage.getWidth()); + y2 = ofClamp(y2, 0.0f, (float)inputImage.getHeight()); + + // Calculate width and height of the cropped area + float cropWidth = x2 - x1; + float cropHeight = y2 - y1; + + // Create cropped section, defined by the box coords + ofFbo tempFbo; + tempFbo.allocate(cropWidth, cropHeight, GL_RGB); + + tempFbo.begin(); + ofClear(0); + inputImage.getTexture().drawSubsection(0, 0, cropWidth, cropHeight, x1, y1); + tempFbo.end(); + + ofPixels pix; + tempFbo.readToPixels(pix); + face.img.setFromPixels(pix); + face.img.resize(imageSize, imageSize); +} + +void ofYolo::SortDetectedFaces(std::vector &detectedFaces){ + std::sort(detectedFaces.begin(), detectedFaces.end(), + [](const BoxfWithLandmarks &a, const BoxfWithLandmarks &b) { + return a.box.center.x > b.box.center.x; // Sort in descending order + }); +} \ No newline at end of file diff --git a/src/ofYolo.h b/src/ofYolo.h new file mode 100644 index 0000000..6983b4f --- /dev/null +++ b/src/ofYolo.h @@ -0,0 +1,130 @@ +#ifndef YOLO +#define YOLO + +#include "ofMain.h" +#include "ofxOpenCv.h" +#include "ofxOnnxRuntime.h" +#include + + + +/* +Struct for storing information about detetced faces. + */ +struct Emotion { + float anger; + float disgust; + float fear; + float happiness; + float neutral; + float sadness; + float surprise; + + void applySoftmax() { + float values[7] = {anger, disgust, fear, happiness, neutral, sadness, surprise}; + + // Find max value for numerical stability + float maxVal = values[0]; + for(int i = 1; i < 7; i++) { + maxVal = std::max(maxVal, values[i]); + } + + // Calculate exp(x - max) and sum + float sum = 0.0f; + for(int i = 0; i < 7; i++) { + values[i] = std::exp(values[i] - maxVal); + sum += values[i]; + } + + // Normalize + anger = values[0] / sum; + disgust = values[1] / sum; + fear = values[2] / sum; + happiness = values[3] / sum; + neutral = values[4] / sum; + sadness = values[5] / sum; + surprise = values[6] / sum; + } + + std::string getDominantEmotion() const { + float values[7] = {anger, disgust, fear, happiness, neutral, sadness, surprise}; + const char* names[7] = {"anger", "disgust", "fear", "happiness", "neutral", "sadness", "surprise"}; + + int maxIndex = 0; + float maxValue = values[0]; + + for(int i = 1; i < 7; i++) { + if(values[i] > maxValue) { + maxValue = values[i]; + maxIndex = i; + } + } + + return std::string(names[maxIndex]); + } + +}; + +struct Boxf { + float x1, y1, x2, y2; // Coordinates of the bounding box + float score; // Confidence score + glm::vec2 center; + int label; // Class label (e.g., "face") + std::string label_text; + + // Calculate Intersection over Union (IoU) with another box + float iou_of(const Boxf &other) const { + float intersection_x1 = std::max(x1, other.x1); + float intersection_y1 = std::max(y1, other.y1); + float intersection_x2 = std::min(x2, other.x2); + float intersection_y2 = std::min(y2, other.y2); + + float intersection_area = std::max(0.0f, intersection_x2 - intersection_x1) * + std::max(0.0f, intersection_y2 - intersection_y1); + + float this_area = (x2 - x1) * (y2 - y1); + float other_area = (other.x2 - other.x1) * (other.y2 - other.y1); + + float union_area = this_area + other_area - intersection_area; + + return intersection_area / union_area; + } + + void UpdateCenter(){ + center.x = (x1 + x2) / 2; + center.y = (y1 + y2) / 2; + } +}; + +struct Landmarks { + std::vector points; // Facial landmarks points (e.g., eyes, nose, mouth) + bool flag = false; // Indicator if landmarks are available +}; + +struct BoxfWithLandmarks { + Boxf box; // Bounding box for the face + Landmarks landmarks; // Landmark points for the face + bool flag = false; // Indicator if this detection is valid + ofImage img; + Emotion emotion; +}; + + + + +class ofYolo{ + public: + ofYolo(){}; + void ParseOutput(float* &out_ptr, std::vector &sorted_faces, unsigned int num_anchors); + void DrawBox(std::vector &detected_faces); + void DrawCenter(std::vector &detected_faces); + void NonMaximumSuppression(std::vector &input_faces, std::vector &output_faces, float iou_threshold); + void ConvertBoxCoordsToOriginalSize(std::vector &detected_faces, size_t original_width, size_t original_height); + void CropFaceToImage(ofImage &inputImage, BoxfWithLandmarks &face, ofImage &colorImage, int imageSize); + void SortDetectedFaces(std::vector &detectedFaces); + private: + // Input dimenions of the model -- used for coordinate scaling. + size_t modelSize = 640; +}; + +#endif \ No newline at end of file diff --git a/src/onxProcess.cpp b/src/onxProcess.cpp new file mode 100644 index 0000000..094db2f --- /dev/null +++ b/src/onxProcess.cpp @@ -0,0 +1,145 @@ +#include "onxProcess.h" + +void onxProcess::setup(ofImage* img) { + // Setup code here + this->inputImage = img; + modelInput.emplace_back(inputImage); + + // Need to allocate ofImage objects & point to them + for(size_t i = 0; i < batchSizeFer; i++) { + ofImage t; + t.allocate(modelInputDimensionsFer, modelInputDimensionsFer, OF_IMAGE_COLOR); + inputImagesFer.push_back(t); + } + + for(auto & i : inputImagesFer) { + modelInputFer.emplace_back(&i); + } + + std::cout << "ONNX Runtime version: " << ORT_API_VERSION << std::endl; + onxFaceDetection.setup(modelPath, ofxOnnxRuntime::BaseSetting{ ofxOnnxRuntime::INFER_CUDA, 0, ofxOnnxRuntime::FLOAT32, ofxOnnxRuntime::FLOAT32, modelInputWidth, modelInputHeight, true}, 1, false, false); + onxFaceDetection.setInputs(modelInput); + + onxFER.setup(modelPathFer, ofxOnnxRuntime::BaseSetting{ ofxOnnxRuntime::INFER_CUDA, 0, ofxOnnxRuntime::FLOAT32, ofxOnnxRuntime::FLOAT32, modelInputDimensionsFer, modelInputDimensionsFer, true}, batchSizeFer, false, false); + onxFER.setInputs(modelInputFer); + + fdThread.setup(&onxFaceDetection); + fdThread.start(); +} + +void onxProcess::update(ofImage& frame) { + // Update frame (the current video frame) + videoFrame = frame; + + // Getthe number of anchors and output tensor -> processes that with some helper functions given an array to fill wil detected faces + if(fdThread.checkInferenceComplete()) { + // Update input image when thread is not busy + ofPixels & p = videoFrame.getPixels(); + inputImage[0].setFromPixels(p); + + auto output = fdThread.getResult(); + auto outputShape = output->front().GetTensorTypeAndShapeInfo().GetShape(); + unsigned int numAnchors = outputShape[1]; + float* tensor = output->front().GetTensorMutableData(); + + detectedFaces.clear(); + yolo.ParseOutput(tensor, detectedFaces, numAnchors); + yolo.ConvertBoxCoordsToOriginalSize(detectedFaces, 1920, 1080); + + fdThread.resetInferenceFlag(); + fdThread.shouldRunInference = true; + } + + cropFaces(); + + updateEmotions(); + + try{ + fdThread.update(); + } catch (_exception e) { + ofLogError() << "Model Thread did not run ) :"; + } +} + +void onxProcess::cropFaces() { + inputImagesFerTemp.clear(); + + // Populate a list of images with cropped faces. Set for model input! + // Makes sure I do not exceed batchsize, even if more faces are detected + ofImage temp; + for(size_t i = 0; i < detectedFaces.size() && i < inputImagesFer.size(); i++) { + // Adds the cropped image to the boxf object + yolo.CropFaceToImage(videoFrame, detectedFaces[i], temp, modelInputDimensionsFer); + inputImagesFer[i].setFromPixels(detectedFaces[i].img.getPixels()); + } +} + +void onxProcess::updateEmotions() { + //Run model! + auto out = onxFER.run(); + float* tensor = out->front().GetTensorMutableData(); + + // Makes sure I do not exceed batchsize, even if more faces are detected + for(size_t i = 0; i < detectedFaces.size() && i < inputImagesFer.size(); i++) { + Emotion emotion; + emotion.anger = tensor[i * 7]; + emotion.disgust = tensor[i * 7 + 1]; + emotion.fear = tensor[i * 7 + 2]; + emotion.happiness = tensor[i * 7 + 3]; + emotion.neutral = tensor[i * 7 + 4]; + emotion.sadness = tensor[i * 7 + 5]; + emotion.surprise = tensor[i * 7 + 6]; + + // Normalises values between 0-1 + emotion.applySoftmax(); + + detectedFaces[i].emotion = emotion; + } +} + +void onxProcess::draw() { + // Drawing code here + //videoFrame.draw(0, 0); + //yolo.DrawBox(detectedFaces); + + int faceSize = modelInputDimensionsFer; + int facesPerRow = ofGetWindowWidth() / faceSize; + + for (size_t i = 0; i < modelInputFer.size(); i++) { + int col = i % facesPerRow; + int row = i / facesPerRow; + + int x = col * faceSize; + int y = 640 + (row * faceSize); + + modelInputFer[i]->draw(x, y); + } +} + +void onxProcess::drawGrid() { + ofImage temp; + int faceSize = 384; + int facesPerRow = ofGetWindowWidth() / faceSize; + int totalRows = (detectedFaces.size() + facesPerRow - 1) / facesPerRow; + + for (int row = 0; row < totalRows; ++row) { + // Calculate number of faces in this row + int startIdx = row * facesPerRow; + int endIdx = std::min(startIdx + facesPerRow, (int)detectedFaces.size()); + int facesInThisRow = endIdx - startIdx; + + // Calculate horizontal offset to center faces in this row + int rowWidth = facesInThisRow * faceSize; + int xOffset = (ofGetWindowWidth() - rowWidth) / 2; + + for (int i = 0; i < facesInThisRow; ++i) { + int idx = startIdx + i; + yolo.CropFaceToImage(videoFrame, detectedFaces[idx], temp, faceSize); + + int x = xOffset + i * faceSize; + int y = row * faceSize; + + temp.draw(x, y); + } + } +} \ No newline at end of file diff --git a/src/onxProcess.h b/src/onxProcess.h new file mode 100644 index 0000000..05f0fff --- /dev/null +++ b/src/onxProcess.h @@ -0,0 +1,44 @@ +#pragma once + +#include "ofMain.h" +#include "ofxOnnxRuntime.h" +#include "ofxOnnxRuntimeThread.h" +#include "ofYolo.h" + +class onxProcess { +public: + void setup(ofImage* img); + void update(ofImage& frame); + void cropFaces(); + void updateEmotions(); + void draw(); + void drawGrid(); + + ofxOnnxRuntime::BaseHandler onxFaceDetection; + ofxOnnxRuntime::OnnxThread fdThread; + + ofxOnnxRuntime::BaseHandler onxFER; + ofxOnnxRuntime::OnnxThread ferThread; + + ofYolo yolo; + + std::vector modelInput; + std::vector modelInputFer; + ofImage* inputImage; + + std::vector inputImagesFer; + std::vector inputImagesFerTemp; + + int modelInputWidth = 640; + int modelInputHeight = 640; + + int modelInputDimensionsFer = 260; + int batchSizeFer = 2; + + std::string modelPath = "yolov5s-face.onnx"; + std::string modelPathFer = "fer.onnx"; + + std::vector detectedFaces; + + ofImage videoFrame; +}; \ No newline at end of file