diff --git a/Application/.vs/config/applicationhost.config b/Application/.vs/config/applicationhost.config new file mode 100644 index 0000000000..f7669cc6b0 --- /dev/null +++ b/Application/.vs/config/applicationhost.config @@ -0,0 +1,1022 @@ + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Application/Rulr.sln b/Application/Rulr.sln index 4496d3bc1d..d8a9ee7242 100644 --- a/Application/Rulr.sln +++ b/Application/Rulr.sln @@ -12,6 +12,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Rulr", "Rulr.vcxproj", "{08 {C135C231-F751-482D-9E4C-C29473E3DD98} = {C135C231-F751-482D-9E4C-C29473E3DD98} {CBFC3D47-1E57-4291-90B6-C344B836B8DA} = {CBFC3D47-1E57-4291-90B6-C344B836B8DA} {4172965B-8375-4DF7-B213-A242DC92BC90} = {4172965B-8375-4DF7-B213-A242DC92BC90} + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52} = {85FF8660-9C77-43A1-B8EE-F2B3182A6C52} {056CE582-26C0-4041-8B4A-EE2B53E77208} = {056CE582-26C0-4041-8B4A-EE2B53E77208} {09FAF69E-1054-477B-8CF6-B5349771197C} = {09FAF69E-1054-477B-8CF6-B5349771197C} {45CE5AA6-9DFD-497A-8CAF-1743A8075F5A} = {45CE5AA6-9DFD-497A-8CAF-1743A8075F5A} @@ -165,6 +166,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_BrightnessAssignment EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_Calibrate", "..\Plugin_Calibrate\Plugin_Calibrate.vcxproj", "{C135C231-F751-482D-9E4C-C29473E3DD98}" + ProjectSection(ProjectDependencies) = postProject + {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} = {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SLS", "SLS", "{B5D96B9A-620B-49D9-8A5D-B30C62FC76E4}" EndProject @@ -180,6 +184,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_LSS", "..\Plugin_LSS {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} = {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Experiments", "Experiments", "{B93C7CAE-7E6C-4B10-80F2-FB823EAF130D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_Experiments", "..\Plugin_Experiments\Plugin_Experiments.vcxproj", "{85FF8660-9C77-43A1-B8EE-F2B3182A6C52}" + ProjectSection(ProjectDependencies) = postProject + {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} = {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -231,9 +242,7 @@ Global {3E918808-ADEE-4B04-9042-25F795280BAD}.Release|x64.ActiveCfg = Release|x64 {3E918808-ADEE-4B04-9042-25F795280BAD}.Release|x64.Build.0 = Release|x64 {CBFC3D47-1E57-4291-90B6-C344B836B8DA}.Debug|x64.ActiveCfg = Release|x64 - {CBFC3D47-1E57-4291-90B6-C344B836B8DA}.Debug|x64.Build.0 = Release|x64 {CBFC3D47-1E57-4291-90B6-C344B836B8DA}.Release|x64.ActiveCfg = Release|x64 - {CBFC3D47-1E57-4291-90B6-C344B836B8DA}.Release|x64.Build.0 = Release|x64 {5B11C632-4E4E-4F86-914D-61F2F1FA6C0B}.Debug|x64.ActiveCfg = Release|x64 {5B11C632-4E4E-4F86-914D-61F2F1FA6C0B}.Debug|x64.Build.0 = Release|x64 {5B11C632-4E4E-4F86-914D-61F2F1FA6C0B}.Release|x64.ActiveCfg = Release|x64 @@ -247,31 +256,23 @@ Global {4D3BCFDD-E65D-4247-B303-E0839CAEC6A6}.Release|x64.ActiveCfg = Release|x64 {4D3BCFDD-E65D-4247-B303-E0839CAEC6A6}.Release|x64.Build.0 = Release|x64 {5688EFFC-E727-4032-9CC3-2697085BEC57}.Debug|x64.ActiveCfg = Release|x64 - {5688EFFC-E727-4032-9CC3-2697085BEC57}.Debug|x64.Build.0 = Release|x64 {5688EFFC-E727-4032-9CC3-2697085BEC57}.Release|x64.ActiveCfg = Release|x64 - {5688EFFC-E727-4032-9CC3-2697085BEC57}.Release|x64.Build.0 = Release|x64 {C087079E-2A0E-4B85-A479-C28E2D01A048}.Debug|x64.ActiveCfg = Release|x64 {C087079E-2A0E-4B85-A479-C28E2D01A048}.Debug|x64.Build.0 = Release|x64 {C087079E-2A0E-4B85-A479-C28E2D01A048}.Release|x64.ActiveCfg = Release|x64 {C087079E-2A0E-4B85-A479-C28E2D01A048}.Release|x64.Build.0 = Release|x64 {4172965B-8375-4DF7-B213-A242DC92BC90}.Debug|x64.ActiveCfg = Release|x64 - {4172965B-8375-4DF7-B213-A242DC92BC90}.Debug|x64.Build.0 = Release|x64 {4172965B-8375-4DF7-B213-A242DC92BC90}.Release|x64.ActiveCfg = Release|x64 - {4172965B-8375-4DF7-B213-A242DC92BC90}.Release|x64.Build.0 = Release|x64 {DFE9EE67-8247-43D6-A957-813EFAD6111E}.Debug|x64.ActiveCfg = Release|x64 {DFE9EE67-8247-43D6-A957-813EFAD6111E}.Release|x64.ActiveCfg = Release|x64 {09FAF69E-1054-477B-8CF6-B5349771197C}.Debug|x64.ActiveCfg = Release|x64 - {09FAF69E-1054-477B-8CF6-B5349771197C}.Debug|x64.Build.0 = Release|x64 {09FAF69E-1054-477B-8CF6-B5349771197C}.Release|x64.ActiveCfg = Release|x64 - {09FAF69E-1054-477B-8CF6-B5349771197C}.Release|x64.Build.0 = Release|x64 {C400D413-78E4-4F60-B5E2-7965FCAC0EEC}.Debug|x64.ActiveCfg = Release|x64 {C400D413-78E4-4F60-B5E2-7965FCAC0EEC}.Debug|x64.Build.0 = Release|x64 {C400D413-78E4-4F60-B5E2-7965FCAC0EEC}.Release|x64.ActiveCfg = Release|x64 {C400D413-78E4-4F60-B5E2-7965FCAC0EEC}.Release|x64.Build.0 = Release|x64 {F8877612-5358-423D-980F-28A6DBDF252E}.Debug|x64.ActiveCfg = Release|x64 - {F8877612-5358-423D-980F-28A6DBDF252E}.Debug|x64.Build.0 = Release|x64 {F8877612-5358-423D-980F-28A6DBDF252E}.Release|x64.ActiveCfg = Release|x64 - {F8877612-5358-423D-980F-28A6DBDF252E}.Release|x64.Build.0 = Release|x64 {1977CCBA-FAB0-4037-8E67-018DE0022E7C}.Debug|x64.ActiveCfg = Release|x64 {1977CCBA-FAB0-4037-8E67-018DE0022E7C}.Debug|x64.Build.0 = Release|x64 {1977CCBA-FAB0-4037-8E67-018DE0022E7C}.Release|x64.ActiveCfg = Release|x64 @@ -301,9 +302,7 @@ Global {3985EE69-9945-40C4-839B-C21A17D5F151}.Release|x64.ActiveCfg = Release|x64 {3985EE69-9945-40C4-839B-C21A17D5F151}.Release|x64.Build.0 = Release|x64 {E91D390E-75A3-4435-8CE9-B14271A8866A}.Debug|x64.ActiveCfg = Release|x64 - {E91D390E-75A3-4435-8CE9-B14271A8866A}.Debug|x64.Build.0 = Release|x64 {E91D390E-75A3-4435-8CE9-B14271A8866A}.Release|x64.ActiveCfg = Release|x64 - {E91D390E-75A3-4435-8CE9-B14271A8866A}.Release|x64.Build.0 = Release|x64 {B6EF2661-4D10-4DAE-B4CF-BD0A92EA864C}.Debug|x64.ActiveCfg = Release|x64 {B6EF2661-4D10-4DAE-B4CF-BD0A92EA864C}.Debug|x64.Build.0 = Release|x64 {B6EF2661-4D10-4DAE-B4CF-BD0A92EA864C}.Release|x64.ActiveCfg = Release|x64 @@ -313,9 +312,7 @@ Global {82E035B1-8706-4571-9774-53990D679319}.Release|x64.ActiveCfg = Release|x64 {82E035B1-8706-4571-9774-53990D679319}.Release|x64.Build.0 = Release|x64 {CAD38B0E-6390-4999-B51F-E4C130D21A30}.Debug|x64.ActiveCfg = Release|x64 - {CAD38B0E-6390-4999-B51F-E4C130D21A30}.Debug|x64.Build.0 = Release|x64 {CAD38B0E-6390-4999-B51F-E4C130D21A30}.Release|x64.ActiveCfg = Release|x64 - {CAD38B0E-6390-4999-B51F-E4C130D21A30}.Release|x64.Build.0 = Release|x64 {45CE5AA6-9DFD-497A-8CAF-1743A8075F5A}.Debug|x64.ActiveCfg = Release|x64 {45CE5AA6-9DFD-497A-8CAF-1743A8075F5A}.Debug|x64.Build.0 = Release|x64 {45CE5AA6-9DFD-497A-8CAF-1743A8075F5A}.Release|x64.ActiveCfg = Release|x64 @@ -327,29 +324,25 @@ Global {9BF86707-A2E9-4727-B3E1-7D0A3B4CE3E3}.Debug|x64.ActiveCfg = Release|x64 {9BF86707-A2E9-4727-B3E1-7D0A3B4CE3E3}.Release|x64.ActiveCfg = Release|x64 {056CE582-26C0-4041-8B4A-EE2B53E77208}.Debug|x64.ActiveCfg = Release|x64 - {056CE582-26C0-4041-8B4A-EE2B53E77208}.Debug|x64.Build.0 = Release|x64 {056CE582-26C0-4041-8B4A-EE2B53E77208}.Release|x64.ActiveCfg = Release|x64 - {056CE582-26C0-4041-8B4A-EE2B53E77208}.Release|x64.Build.0 = Release|x64 {5C557280-E9E7-4789-83F6-7185A89772BD}.Debug|x64.ActiveCfg = Release|x64 {5C557280-E9E7-4789-83F6-7185A89772BD}.Debug|x64.Build.0 = Release|x64 {5C557280-E9E7-4789-83F6-7185A89772BD}.Release|x64.ActiveCfg = Release|x64 {5C557280-E9E7-4789-83F6-7185A89772BD}.Release|x64.Build.0 = Release|x64 - {5C78C8F2-6D2D-4B46-9D92-E29015F0E66A}.Debug|x64.ActiveCfg = Debug|x64 - {5C78C8F2-6D2D-4B46-9D92-E29015F0E66A}.Debug|x64.Build.0 = Debug|x64 + {5C78C8F2-6D2D-4B46-9D92-E29015F0E66A}.Debug|x64.ActiveCfg = Release|x64 {5C78C8F2-6D2D-4B46-9D92-E29015F0E66A}.Release|x64.ActiveCfg = Release|x64 - {5C78C8F2-6D2D-4B46-9D92-E29015F0E66A}.Release|x64.Build.0 = Release|x64 - {C135C231-F751-482D-9E4C-C29473E3DD98}.Debug|x64.ActiveCfg = Debug|x64 - {C135C231-F751-482D-9E4C-C29473E3DD98}.Debug|x64.Build.0 = Debug|x64 + {C135C231-F751-482D-9E4C-C29473E3DD98}.Debug|x64.ActiveCfg = Release|x64 + {C135C231-F751-482D-9E4C-C29473E3DD98}.Debug|x64.Build.0 = Release|x64 {C135C231-F751-482D-9E4C-C29473E3DD98}.Release|x64.ActiveCfg = Release|x64 {C135C231-F751-482D-9E4C-C29473E3DD98}.Release|x64.Build.0 = Release|x64 - {651FDD8B-6C0C-4A82-89ED-182CFF129D03}.Debug|x64.ActiveCfg = Debug|x64 - {651FDD8B-6C0C-4A82-89ED-182CFF129D03}.Debug|x64.Build.0 = Debug|x64 + {651FDD8B-6C0C-4A82-89ED-182CFF129D03}.Debug|x64.ActiveCfg = Release|x64 {651FDD8B-6C0C-4A82-89ED-182CFF129D03}.Release|x64.ActiveCfg = Release|x64 - {651FDD8B-6C0C-4A82-89ED-182CFF129D03}.Release|x64.Build.0 = Release|x64 - {E26FB7D4-3D82-413D-88F7-8A1B6EE91A01}.Debug|x64.ActiveCfg = Debug|x64 - {E26FB7D4-3D82-413D-88F7-8A1B6EE91A01}.Debug|x64.Build.0 = Debug|x64 + {E26FB7D4-3D82-413D-88F7-8A1B6EE91A01}.Debug|x64.ActiveCfg = Release|x64 {E26FB7D4-3D82-413D-88F7-8A1B6EE91A01}.Release|x64.ActiveCfg = Release|x64 - {E26FB7D4-3D82-413D-88F7-8A1B6EE91A01}.Release|x64.Build.0 = Release|x64 + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52}.Debug|x64.ActiveCfg = Release|x64 + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52}.Debug|x64.Build.0 = Release|x64 + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52}.Release|x64.ActiveCfg = Release|x64 + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -414,5 +407,7 @@ Global {651FDD8B-6C0C-4A82-89ED-182CFF129D03} = {B5D96B9A-620B-49D9-8A5D-B30C62FC76E4} {61A0166A-57BD-43D7-BF73-A42CB13A9F1C} = {148566DA-E924-4085-B479-ACA0D1601C9A} {E26FB7D4-3D82-413D-88F7-8A1B6EE91A01} = {61A0166A-57BD-43D7-BF73-A42CB13A9F1C} + {B93C7CAE-7E6C-4B10-80F2-FB823EAF130D} = {148566DA-E924-4085-B479-ACA0D1601C9A} + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52} = {B93C7CAE-7E6C-4B10-80F2-FB823EAF130D} EndGlobalSection EndGlobal diff --git a/Core/src/ofxRulr.h b/Core/src/ofxRulr.h index 2d87232cb4..91d3fd4b1f 100644 --- a/Core/src/ofxRulr.h +++ b/Core/src/ofxRulr.h @@ -4,7 +4,6 @@ #include "ofxRulr/Version.h" #include "ofxRulr/Nodes/Base.h" -#include "ofxRulr/Nodes/Procedure/Base.h" #include "ofxRulr/Graph/World.h" #include "ofxRulr/Graph/FactoryRegister.h" diff --git a/Core/src/ofxRulr/Utils/CaptureSet.cpp b/Core/src/ofxRulr/Utils/CaptureSet.cpp index 2b2e215743..500b8742e8 100644 --- a/Core/src/ofxRulr/Utils/CaptureSet.cpp +++ b/Core/src/ofxRulr/Utils/CaptureSet.cpp @@ -218,8 +218,14 @@ namespace ofxRulr { //---------- void AbstractCaptureSet::add(shared_ptr capture) { + if (find(this->captures.begin(), this->captures.end(), capture) != this->captures.end()) { + return; + } + auto captureWeak = weak_ptr(capture); capture->onDeletePressed += [captureWeak, this]() { + this->listView->clear(); + this->viewDirty = true; auto capture = captureWeak.lock(); if (capture) { this->remove(capture); @@ -266,9 +272,11 @@ namespace ofxRulr { //---------- void AbstractCaptureSet::clear() { + this->listView->clear(); while (!this->captures.empty()) { this->remove(* this->captures.begin()); } + this->viewDirty = true; } //---------- @@ -393,5 +401,10 @@ namespace ofxRulr { vector> AbstractCaptureSet::getAllCapturesUntyped() const { return this->captures; } + + //---------- + size_t AbstractCaptureSet::size() const { + return this->captures.size(); + } } } \ No newline at end of file diff --git a/Core/src/ofxRulr/Utils/CaptureSet.h b/Core/src/ofxRulr/Utils/CaptureSet.h index 4f11291dfb..2e340f8f4d 100644 --- a/Core/src/ofxRulr/Utils/CaptureSet.h +++ b/Core/src/ofxRulr/Utils/CaptureSet.h @@ -27,12 +27,12 @@ namespace ofxRulr { ofxLiquidEvent onDeletePressed; ofxLiquidEvent onSelectionChanged; ofParameter timestamp{ "Timestamp", chrono::system_clock::now() }; + void rebuildDateStrings(); protected: ofParameter selected{ "Selected", true }; virtual ofxCvGui::ElementPtr getDataDisplay(); void callbackSelectedChanged(bool &); - void rebuildDateStrings(); string timeString; string secondString; string dateString; @@ -60,6 +60,8 @@ namespace ofxRulr { ofxLiquidEvent onChange; ofxLiquidEvent onSelectionChanged; + + size_t size() const; protected: virtual bool getIsMultipleSelectionAllowed() = 0; vector> captures; diff --git a/Nodes/src/ofxRulr/Nodes/Item/View.cpp b/Nodes/src/ofxRulr/Nodes/Item/View.cpp index 6a252ff7ef..6d187ef74e 100644 --- a/Nodes/src/ofxRulr/Nodes/Item/View.cpp +++ b/Nodes/src/ofxRulr/Nodes/Item/View.cpp @@ -378,8 +378,8 @@ namespace ofxRulr { //adapted from https://github.com/Itseez/opencv/blob/master/samples/cpp/calibration.cpp#L170 cv::FileStorage fs(result.filePath, cv::FileStorage::WRITE); - fs << "image_width" << this->getWidth(); - fs << "image_height" << this->getHeight(); + fs << "image_width" << (int) this->getWidth(); + fs << "image_height" << (int) this->getHeight(); fs << "camera_matrix" << this->getCameraMatrix(); fs << "distortion_coefficients" << this->getDistortionCoefficients(); diff --git a/Nodes/src/ofxRulr/Nodes/System/VideoOutput.cpp b/Nodes/src/ofxRulr/Nodes/System/VideoOutput.cpp index e49c684407..a1c962297a 100644 --- a/Nodes/src/ofxRulr/Nodes/System/VideoOutput.cpp +++ b/Nodes/src/ofxRulr/Nodes/System/VideoOutput.cpp @@ -26,7 +26,18 @@ namespace ofxRulr { VideoOutput::Output::Output(int index, GLFWmonitor * monitor) { this->index = index; this->monitor = monitor; - glfwGetMonitorPhysicalSize(monitor, &this->width, &this->height); + + { + auto videoMode = glfwGetVideoMode(monitor); + if (videoMode != NULL) { + this->width = videoMode->width; + this->height = videoMode->height; + } + else { + this->width = 0; + this->height = 0; + } + } this->name = glfwGetMonitorName(monitor); } @@ -532,8 +543,14 @@ namespace ofxRulr { } ofSetColor(255); - const auto textBounds = font.getStringBoundingBox(videoOutput.name, 0, 0); - font.drawString(videoOutput.name, (int)((selectButton->getWidth() - textBounds.width) / 2.0f), (int)((selectButton->getHeight() + textBounds.height) / 2.0f)); + { + stringstream text; + text << videoOutput.name << endl; + text << videoOutput.width << "x" << videoOutput.height; + const auto textBounds = font.getStringBoundingBox(text.str(), 0, 0); + font.drawString(text.str(), (int)((selectButton->getWidth() - textBounds.width) / 2.0f), (int)((selectButton->getHeight() + textBounds.height) / 2.0f)); + } + } ofPopStyle(); }; diff --git a/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp b/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp index d311260228..1e53901dff 100644 --- a/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp +++ b/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp @@ -155,8 +155,8 @@ namespace ofxRulr { } RULR_CATCH_ALL_TO_ERROR - //update the fbo - this->fbo.begin(); + //update the fbo + this->fbo.begin(); { //draw the undistorted image this->undistorted.draw(0, 0); diff --git a/PlatformExamples/VVVV/SplitSymbolNames/main.v4p b/PlatformExamples/VVVV/SplitSymbolNames/main.v4p index 6768beac9e..76657b3b66 100644 --- a/PlatformExamples/VVVV/SplitSymbolNames/main.v4p +++ b/PlatformExamples/VVVV/SplitSymbolNames/main.v4p @@ -1,5 +1,5 @@ - + @@ -7,7 +7,7 @@ - + @@ -470,4 +470,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Plugin_ArUco/Plugin_ArUco.vcxproj b/Plugin_ArUco/Plugin_ArUco.vcxproj index 153bfc7069..78f43357ac 100644 --- a/Plugin_ArUco/Plugin_ArUco.vcxproj +++ b/Plugin_ArUco/Plugin_ArUco.vcxproj @@ -89,6 +89,7 @@ + diff --git a/Plugin_ArUco/Plugin_ArUco.vcxproj.filters b/Plugin_ArUco/Plugin_ArUco.vcxproj.filters index ad29386b3f..f8a204c52b 100644 --- a/Plugin_ArUco/Plugin_ArUco.vcxproj.filters +++ b/Plugin_ArUco/Plugin_ArUco.vcxproj.filters @@ -74,5 +74,8 @@ src\ofxRulr\Nodes\ArUco + + src + \ No newline at end of file diff --git a/Plugin_ArUco/src/Constants_Plugin_ArUco.h b/Plugin_ArUco/src/Constants_Plugin_ArUco.h new file mode 100644 index 0000000000..75fd3217cd --- /dev/null +++ b/Plugin_ArUco/src/Constants_Plugin_ArUco.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef BUILD_Plugin_ArUco +#define PLUGIN_ARUCO_EXPORTS __declspec(dllexport) +#else +#define PLUGIN_ARUCO_EXPORTS +#endif \ No newline at end of file diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.h index e5c403a996..51d80e2013 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.h @@ -1,5 +1,6 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include "ofxRulr/Utils/CaptureSet.h" #include "ofxNonLinearFit.h" @@ -10,7 +11,7 @@ namespace ofxRulr { namespace Nodes { namespace ArUco { - class AlignMarkerMap : public Nodes::Base { + class PLUGIN_ARUCO_EXPORTS AlignMarkerMap : public Nodes::Base { public: enum class Plane : int { X, Y, Z diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp index ffe923f5a7..93af51e6ca 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp @@ -146,6 +146,8 @@ namespace ofxRulr { detectorParams->adaptiveThreshWinSizeMin = 3; detectorParams->adaptiveThreshWinSizeMax = 33; detectorParams->adaptiveThreshWinSizeStep = 5; + detectorParams->errorCorrectionRate = this->parameters.detection.errorCorrectionRate; + if (image.cols > 5000) { detectorParams->minMarkerPerimeterRate = 0.003; } diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.h index 1d53e6bb3d..b28e11be6a 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.h @@ -1,5 +1,6 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Item/AbstractBoard.h" #include #include @@ -7,7 +8,7 @@ namespace ofxRulr { namespace Nodes { namespace ArUco { - class ChArUcoBoard : public Nodes::Item::AbstractBoard { + class PLUGIN_ARUCO_EXPORTS ChArUcoBoard : public Nodes::Item::AbstractBoard { public: struct PaperSize { string name; @@ -34,13 +35,13 @@ namespace ofxRulr { struct : ofParameterGroup { struct : ofParameterGroup { - ofParameter width{ "Width", 12 }; + ofParameter width{ "Width", 11 }; ofParameter height{ "Height", 8 }; PARAM_DECLARE("Size [squares]", width, height); } size; struct : ofParameterGroup { - ofParameter square{ "Square", 0.034875, 0.001, 0.10 }; // noah's board + ofParameter square{ "Square", 0.025, 0.001, 0.10 }; ofParameter marker{ "Marker", 0.0175, 0.001, 0.1 }; PARAM_DECLARE("Length [m]", square, marker); } length; @@ -48,7 +49,8 @@ namespace ofxRulr { struct : ofParameterGroup { ofParameter refineStrategy{ "Refine strategy", true }; - PARAM_DECLARE("Detection", refineStrategy); + ofParameter errorCorrectionRate{ "Error correction rate", 0.6 }; + PARAM_DECLARE("Detection", refineStrategy, errorCorrectionRate); } detection; struct : ofParameterGroup { diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h index e500fe77cc..7a68e3ee5a 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h @@ -1,5 +1,6 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include "ofxRulr/Nodes/Item/Camera.h" #include @@ -9,7 +10,7 @@ namespace ofxRulr { namespace Nodes { namespace ArUco { - class Detector : public Nodes::Base { + class PLUGIN_ARUCO_EXPORTS Detector : public Nodes::Base { public: Detector(); string getTypeName() const override; diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Dictionary.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Dictionary.h index f0e6891916..abad868af9 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Dictionary.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Dictionary.h @@ -1,12 +1,13 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include namespace ofxRulr { namespace Nodes { namespace ArUco { - class Dictionary : public Nodes::Base { + class PLUGIN_ARUCO_EXPORTS Dictionary : public Nodes::Base { public: Dictionary(); string getTypeName() const override; diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.h index 3694dacee3..75de81e700 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.h @@ -1,5 +1,6 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.cpp index efc94354b7..f9a25ea24c 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.cpp @@ -65,6 +65,8 @@ namespace ofxRulr { markerPreview.draw(); } + ofDrawBitmapString(ofToString(marker3D.id), ofxCv::toOf(marker3D.points[0])); + switch (this->parameters.drawLabels.get().get()) { case WhenDrawOnWorldStage::Selected: if (!this->isBeingInspected()) { @@ -101,11 +103,31 @@ namespace ofxRulr { inspector->addButton("Clear", [this]() { this->clear(); }); + + { + inspector->addTitle("Rotate 90", ofxCvGui::Widgets::Title::Level::H3); + inspector->addButton("X", [this]() { + this->rotateMarkerMap(ofVec3f(1, 0, 0), 90); + }); + inspector->addButton("Y", [this]() { + this->rotateMarkerMap(ofVec3f(0, 1, 0), 90); + }); + inspector->addButton("Z", [this]() { + this->rotateMarkerMap(ofVec3f(0, 0, 1), 90); + }); + inspector->addButton("Remove...", [this]() { + auto removeString = ofSystemTextBoxDialog("Marker index"); + if (!removeString.empty()) { + auto index = ofToInt(removeString); + this->removeMarker(index); + } + }); + } } //---------- void MarkerMap::serialize(Json::Value & json) { - if(this->markerMap) { + if (this->markerMap) { auto & jsonMarkerMap = json["markerMap"]; jsonMarkerMap["mInfoType"] = this->markerMap->mInfoType; jsonMarkerMap["dictionary"] = this->markerMap->getDictionary(); @@ -160,6 +182,30 @@ namespace ofxRulr { void MarkerMap::clear() { this->markerMap.reset(); } + + //---------- + void MarkerMap::rotateMarkerMap(const ofVec3f & axis, float angle) { + if (this->markerMap) { + auto transform = ofMatrix4x4::newRotationMatrix(angle, axis); + for (auto & marker : *this->markerMap) { + for (auto & point : marker.points) { + point = ofxCv::toCv(ofxCv::toOf(point) * transform); + } + } + } + } + + //---------- + void MarkerMap::removeMarker(size_t idToRemove) { + if (this->markerMap) { + auto toRemove = remove_if(this->markerMap->begin() + , this->markerMap->end() + , [idToRemove](const aruco::Marker3DInfo & markerInfo) { + return markerInfo.id == idToRemove; + }); + this->markerMap->erase(toRemove, this->markerMap->end()); + } + } } } } \ No newline at end of file diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.h index b5d16249be..489c869bb5 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMap.h @@ -1,12 +1,13 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include namespace ofxRulr { namespace Nodes { namespace ArUco { - class MarkerMap : public Nodes::Base { + class PLUGIN_ARUCO_EXPORTS MarkerMap : public Nodes::Base { public: MarkerMap(); string getTypeName() const override; @@ -20,6 +21,8 @@ namespace ofxRulr { shared_ptr getMarkerMap(); void clear(); + void rotateMarkerMap(const ofVec3f & axis, float angle); + void removeMarker(size_t idToRemove); protected: shared_ptr markerMap; diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.cpp index c96d171cd4..beae07d25e 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.cpp @@ -130,7 +130,8 @@ namespace ofxRulr { , cameraNode->getDistortionCoefficients() , rotation , translation - , true); + , true + , cv::SOLVEPNP_ITERATIVE); } else { cv::solvePnPRansac(worldPoints @@ -139,7 +140,8 @@ namespace ofxRulr { , cameraNode->getDistortionCoefficients() , rotation , translation - , true); + , true + , cv::SOLVEPNP_ITERATIVE); } break; } diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.h index 61e35cc5b2..e1334b9198 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.h @@ -1,12 +1,13 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include namespace ofxRulr { namespace Nodes { namespace ArUco { - class MarkerMapPoseTracker : public Nodes::Base { + class PLUGIN_ARUCO_EXPORTS MarkerMapPoseTracker : public Nodes::Base { public: MAKE_ENUM(Method , (Tracker, solvePnP, RANSAC) diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/OSCRelay.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/OSCRelay.h index 8529949e3b..73cb6c7d9f 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/OSCRelay.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/OSCRelay.h @@ -1,12 +1,13 @@ #pragma once +#include "Constants_Plugin_ArUco.h" #include "ofxRulr/Nodes/Base.h" #include "ofxOsc.h" namespace ofxRulr { namespace Nodes { namespace ArUco { - class OSCRelay : public ofxRulr::Nodes::Base { + class PLUGIN_ARUCO_EXPORTS OSCRelay : public ofxRulr::Nodes::Base { public: OSCRelay(); string getTypeName() const override; diff --git a/Plugin_ArUco/src/pch_Plugin_ArUco.h b/Plugin_ArUco/src/pch_Plugin_ArUco.h index b035b32ed1..0607f73481 100644 --- a/Plugin_ArUco/src/pch_Plugin_ArUco.h +++ b/Plugin_ArUco/src/pch_Plugin_ArUco.h @@ -16,3 +16,5 @@ #include "ofxRulr/Nodes/ArUco/MarkerMap.h" #include "ofxRulr/Nodes/ArUco/MarkerMapPoseTracker.h" #include "ofxRulr/Nodes/ArUco/OSCRelay.h" + +#include "pch_Plugin_ArUco.h" diff --git a/Plugin_Calibrate/Plugin_Calibrate.vcxproj b/Plugin_Calibrate/Plugin_Calibrate.vcxproj index 44d6d305dd..19813f2c4d 100644 --- a/Plugin_Calibrate/Plugin_Calibrate.vcxproj +++ b/Plugin_Calibrate/Plugin_Calibrate.vcxproj @@ -12,7 +12,7 @@ {C135C231-F751-482D-9E4C-C29473E3DD98} - ofxRulr::Nodes::BAM + ofxRulr::Nodes::Calibrate 8.1 Plugin_Calibrate diff --git a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.cpp b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.cpp index e528b0256f..8f4f2817f9 100644 --- a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.cpp +++ b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.cpp @@ -110,6 +110,10 @@ namespace ofxRulr { //draw current corners on top ofxCv::drawCorners(this->currentImagePoints); + for (int i = 0; i < this->currentImagePoints.size(); i++) { + ofDrawBitmapString(ofToString(i), this->currentImagePoints[i]); + } + ofPopStyle(); } ofPopMatrix(); @@ -238,6 +242,72 @@ namespace ofxRulr { return false; } + //---------- + void CameraIntrinsics::addImage(cv::Mat image) { + this->throwIfMissingAConnection(); + this->throwIfMissingAConnection(); + + auto board = this->getInput(); + auto camera = this->getInput(); + + vector imagePoints; + vector objectPoints; + + board->findBoard(image + , toCv(imagePoints) + , toCv(objectPoints) + , this->parameters.capture.findBoardMode + , camera->getCameraMatrix() + , camera->getDistortionCoefficients()); + + { + auto capture = make_shared(); + capture->pointsImageSpace = imagePoints; + capture->pointsObjectSpace = objectPoints; + capture->imageWidth = camera->getWidth(); + capture->imageHeight = camera->getHeight(); + this->captures.add(capture); + } + } + + //---------- + void CameraIntrinsics::addFolder(const std::filesystem::path & path) { + + //get list of files + vector filePaths; + for (std::filesystem::directory_iterator it(path) + ; it != std::filesystem::directory_iterator() + ; ++it) { + filePaths.push_back(it->path().string()); + } + + Utils::ScopedProcess scopedProcess("Adding folder images", true, filePaths.size()); + + //for each file in path + for(auto filePath : filePaths) { + try { + Utils::ScopedProcess fileScopedProcess(filePath, false); + + //load image + auto image = cv::imread(filePath); + + //check it loaded + if (image.empty()) { + continue; + } + else { + //add it to the captures + this->addImage(image); + } + } + RULR_CATCH_ALL_TO({ + RULR_WARNING << e.what(); + }); + } + + scopedProcess.end(); + } + //---------- void CameraIntrinsics::populateInspector(ofxCvGui::InspectArguments & inspectArguments) { auto inspector = inspectArguments.inspector; @@ -278,6 +348,15 @@ namespace ofxRulr { inspector->addSpacer(); + inspector->addButton("Add folder of images", [this]() { + auto result = ofSystemLoadDialog("Folder of calibration images", true); + if (result.bSuccess) { + this->addFolder(std::filesystem::path(result.getPath())); + } + }); + + inspector->addSpacer(); + inspector->addParameterGroup(this->parameters); } @@ -312,6 +391,7 @@ namespace ofxRulr { this->captures.add(capture); } } + //---------- void CameraIntrinsics::findBoard() { this->throwIfMissingAnyConnection(); @@ -346,18 +426,20 @@ namespace ofxRulr { void CameraIntrinsics::calibrate() { this->throwIfMissingAConnection(); - auto allCaptures = this->captures.getAllCaptures(); + auto captures = this->captures.getSelection(); - if (allCaptures.size() < 2) { + if (captures.size() < 2) { throw(ofxRulr::Exception("You need to add at least 2 captures before trying to calibrate (ideally 10)")); } auto camera = this->getInput(); vector> imagePoints; vector> objectPoints; - for (auto & capture : allCaptures) { - imagePoints.push_back(toCv(capture->pointsImageSpace)); - objectPoints.push_back(toCv(capture->pointsObjectSpace)); + for (auto & capture : captures) { + if (capture->pointsImageSpace.size() > 0) { + imagePoints.push_back(toCv(capture->pointsImageSpace)); + objectPoints.push_back(toCv(capture->pointsObjectSpace)); + } } cv::Size cameraResolution(camera->getWidth(), camera->getHeight()); @@ -369,15 +451,16 @@ namespace ofxRulr { this->error = cv::calibrateCamera(objectPoints, imagePoints, cameraResolution, cameraMatrix, distortionCoefficients, Rotations, Translations, flags); camera->setIntrinsics(cameraMatrix, distortionCoefficients); - for (int i = 0; i < allCaptures.size(); i++) { - auto capture = allCaptures[i]; + for (int i = 0; i < captures.size(); i++) { + auto capture = captures[i]; capture->extrsinsics = makeMatrix(Rotations[i], Translations[i]); vector reprojectedImageCoordinates; cv::projectPoints(toCv(capture->pointsObjectSpace), Rotations[i], Translations[i], cameraMatrix, distortionCoefficients, reprojectedImageCoordinates); float reprojectionErrorSquaredSum = 0.0f; - for (int i = 0; i < reprojectedImageCoordinates.size(); i++) { - reprojectionErrorSquaredSum += (toOf(reprojectedImageCoordinates[i]) - capture->pointsImageSpace[i]).lengthSquared(); + + for (int j = 0; j < reprojectedImageCoordinates.size(); j++) { + reprojectionErrorSquaredSum += (toOf(reprojectedImageCoordinates[j]) - capture->pointsImageSpace[j]).lengthSquared(); } capture->reprojectionError = sqrt(reprojectionErrorSquaredSum / (float)reprojectedImageCoordinates.size()); } diff --git a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.h b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.h index 0a5e6d1dfe..df277a641f 100644 --- a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.h +++ b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/CameraIntrinsics.h @@ -44,6 +44,12 @@ namespace ofxRulr { void deserialize(const Json::Value &); bool getRunFinderEnabled() const; + + // Manually add an image to the capture set + void addImage(cv::Mat); + + // Add a folder of images to the capture set + void addFolder(const std::filesystem::path & path); protected: void populateInspector(ofxCvGui::InspectArguments &); void addCapture(bool triggeredFromTetheredCapture); diff --git a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/IReferenceVertices.h b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/IReferenceVertices.h index ff35a3e08f..e3586f7793 100644 --- a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/IReferenceVertices.h +++ b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/IReferenceVertices.h @@ -4,6 +4,7 @@ #include "ofxCvGui/Panels/Image.h" #include "ofxRulr/Utils/CaptureSet.h" +#include "ofxRulr/Nodes/Procedure/Base.h" #include "Constants_Plugin_Calibration.h" @@ -20,6 +21,7 @@ namespace ofxRulr { string getDisplayString() const override; ofParameter worldPosition{ "World", ofVec3f() }; ofParameter viewPosition{ "View", ofVec2f() }; + ofParameter name{ "Name", "" }; void setOwner(shared_ptr); protected: void drawObjectLines(); diff --git a/Plugin_Experiments/Plugin_Experiments.vcxproj b/Plugin_Experiments/Plugin_Experiments.vcxproj new file mode 100644 index 0000000000..2c30dab5f9 --- /dev/null +++ b/Plugin_Experiments/Plugin_Experiments.vcxproj @@ -0,0 +1,126 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {85FF8660-9C77-43A1-B8EE-F2B3182A6C52} + ofxRulr::Nodes::Experiments + 8.1 + Plugin_Experiments + + + + DynamicLibrary + true + v140 + MultiByte + + + DynamicLibrary + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + Use + pch_Plugin_Experiments.h + ProgramDatabase + ../../../addons/ofxRulr/Plugin_ArUco/src;%(AdditionalIncludeDirectories) + + + true + + + + + + Level3 + MaxSpeed + true + true + true + Use + pch_Plugin_Experiments.h + ../../../addons/ofxRulr/Plugin_ArUco/src;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + {fc33c381-dc7a-45ad-8ef0-647e3b03db69} + + + {45ce5aa6-9dfd-497a-8caf-1743a8075f5a} + + + + + + \ No newline at end of file diff --git a/Plugin_Experiments/Plugin_Experiments.vcxproj.filters b/Plugin_Experiments/Plugin_Experiments.vcxproj.filters new file mode 100644 index 0000000000..0a08ec4a07 --- /dev/null +++ b/Plugin_Experiments/Plugin_Experiments.vcxproj.filters @@ -0,0 +1,99 @@ + + + + + + + src\ofxRulr\Experiments\SolvePnP + + + src\ofxRulr\Experiments\SolvePnP + + + src\ofxRulr\Experiments\SolvePnP + + + src\ofxRulr\Experiments\SolveMirror + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\PhotoScan + + + + + + src\ofxRulr\Experiments\SolvePnP + + + src\ofxRulr\Experiments\SolvePnP + + + src\ofxRulr\Experiments\SolvePnP + + + src\ofxRulr\Experiments\SolveMirror + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\MirrorPlaneCapture + + + src\ofxRulr\Experiments\PhotoScan + + + + + {9913a1d7-50cf-431a-ba37-d925c1efc463} + + + {8551b9aa-83e9-4543-977a-36e938e411b5} + + + {fb0eceac-a621-45ff-b656-68b1b3888be2} + + + {57879dba-d2a0-4222-8555-3d67ad5d06b0} + + + {a13b624c-1893-4263-8bc5-3d213d810fae} + + + {6225c5c4-68e7-4fa6-90ab-17ef387eb1a9} + + + {5b820a98-344a-439f-b8b4-cf8c10af6673} + + + \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.cpp new file mode 100644 index 0000000000..747af3cd44 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.cpp @@ -0,0 +1,550 @@ +#include "pch_Plugin_Experiments.h" + +template +T mean(const vector & points) { + T accumulator(0); + + for (const auto & point : points) { + accumulator += point; + } + return accumulator / size(points); +} + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + //---------- + BoardInMirror::BoardInMirror() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string BoardInMirror::getTypeName() const { + return "Experiments::MirrorPlaneCapture::BoardInMirror"; + } + + //---------- + void BoardInMirror::init() { + RULR_NODE_DRAW_WORLD_LISTENER; + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_UPDATE_LISTENER; + + this->addInput(); + this->addInput(); + this->addInput(); + this->addInput(); + + this->panel = make_shared(); + this->captures.populateWidgets(this->panel); + + this->manageParameters(this->parameters); + } + + //---------- + void BoardInMirror::update() { + + } + + //---------- + void BoardInMirror::addCapture() { + this->throwIfMissingAConnection(); + + auto camera = this->getInput(); + auto grabber = camera->getGrabber(); + if (!grabber) { + throw(ofxRulr::Exception("Camera grabber not available")); + } + + auto frame = grabber->getFreshFrame(); + auto image = ofxCv::toCv(frame->getPixels()); + this->addImage(image); + } + + //---------- + void BoardInMirror::calibrate() { + + } + + //---------- + ofxCvGui::PanelPtr BoardInMirror::getPanel() { + return this->panel; + } + + //---------- + void BoardInMirror::populateInspector(ofxCvGui::InspectArguments & inspectArgs) { + auto inspector = inspectArgs.inspector; + inspector->addButton("Add capture", [this]() { + try { + Utils::ScopedProcess scopedProcess("Add capture"); + this->addCapture(); + scopedProcess.end(); + } + RULR_CATCH_ALL_TO_ALERT; + }, ' ')->setHeight(100.0f); + + inspector->addButton("Add folder of images...", [this]() { + auto result = ofSystemLoadDialog("Folder of calibration images", true); + if (result.bSuccess) { + this->addFolderOfImages(std::filesystem::path(result.getPath())); + } + }); + + inspector->addButton("Calibrate", [this]() { + try { + this->calibrate(); + } + RULR_CATCH_ALL_TO_ALERT; + }, OF_KEY_RETURN)->setHeight(100.0f); + } + + //---------- + void BoardInMirror::drawWorldStage() { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawWorld(); + } + } + + //---------- + void BoardInMirror::addImage(const cv::Mat & image, const string & name) { + this->throwIfMissingAConnection(); + this->throwIfMissingAConnection(); + + auto camera = this->getInput(); + auto board = this->getInput(); + + auto capture = make_shared(); + + capture->name = name; + + //function to test reprojection error for a set of points given a translation, rotation + auto getReprojectionError = [&camera](const vector & objectPoints + , const vector & imagePoints + , const cv::Mat & rotationVector + , const cv::Mat & translation) { + + float error = 0.0f; + vector reprojectedPoints; + cv::projectPoints(objectPoints + , rotationVector + , translation + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , reprojectedPoints); + + //Take square mean + for (size_t i = 0; i < imagePoints.size(); i++) { + auto delta = imagePoints[i] - reprojectedPoints[i]; + error += delta.x * delta.x + delta.y * delta.y; + } + //RMS + error /= (float)imagePoints.size(); + error = sqrt(error); + + return error; + }; + + //resolve pose of camera using the marker map + capture->cameraNavigation.enabled = this->parameters.cameraNavigation.enabled; + if (this->parameters.cameraNavigation.enabled) { + this->throwIfMissingAConnection(); + this->throwIfMissingAConnection(); + + auto detector = this->getInput(); + auto markerMap = this->getInput()->getMarkerMap(); + + auto markers = detector->getMarkerDetector().detect(image); + if (markers.size() < this->parameters.cameraNavigation.minimumMarkers) { + throw(ofxRulr::Exception("Found " + ofToString(markers.size()) + " real markers. Need " + ofToString(this->parameters.cameraNavigation.minimumMarkers) + " for camera navigation")); + } + + //build up dataset + vector worldPoints; + vector imagePoints; + { + for (const auto & marker : markers) { + try { + auto marker3D = markerMap->getMarker3DInfo(marker.id); + for (int i = 0; i < 4; i++) { + worldPoints.push_back(marker3D.points[i]); + imagePoints.push_back(marker[i]); + } + } + RULR_CATCH_ALL_TO_ERROR; //in the case that the marker did not exist + } + } + + cv::Mat rotationVector, translation; + camera->getExtrinsics(rotationVector, translation, true); + + //Solve camera extrinsics + { + //first pass EPNP + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true + , 100 + , 5.0f + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_EPNP); + + //second pass ITERATIVE + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true + , 100 + , 5.0f + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_ITERATIVE); + } + + camera->setExtrinsics(rotationVector, translation, true); + + capture->cameraNavigation.imagePoints = imagePoints; + capture->cameraNavigation.worldPoints = worldPoints; + capture->cameraNavigation.reprojectionError = getReprojectionError(worldPoints + , imagePoints + , rotationVector + , translation); + capture->cameraNavigation.cameraPosition = camera->getPosition(); + } + + auto cameraTransform = camera->getTransform(); + + //-- + //find board plane function + // + // + auto findBoardPlane = [this, camera, board, &getReprojectionError, &cameraTransform](const cv::Mat & image, Capture::Plane & plane, bool mirrored) { + + auto spaceName = string(mirrored ? "mirrored" : "real"); + //flip image if needed + cv::Mat image2; + if (mirrored) { + cv::flip(image, image2, 0); + } + else { + image2 = image; + } + + //get board image and object space points + if (!board->findBoard(image2 + , plane.imagePoints + , plane.objectPoints + , this->parameters.capture.findBoardMode + , camera->getCameraMatrix() + , camera->getDistortionCoefficients())) { + throw(Exception("Failed to find board in " + spaceName + " space")); + } + + //check we found enough corners + if (plane.objectPoints.size() < this->parameters.capture.minimumCorners) { + throw(Exception("Not enough corners found in " + spaceName + " space (" + ofToString(plane.objectPoints.size()) + " found. Needs " + ofToString(this->parameters.capture.minimumCorners) + ")")); + } + + //flip result if needed + if (mirrored) { + //flip the image + for (auto & imagePoint : plane.imagePoints) { + imagePoint.y = camera->getHeight() - imagePoint.y; // not -1 again because subpixel + } + + //flip the object points + for (auto & objectPoint : plane.objectPoints) { + objectPoint.y *= -1.0f; + } + } + + cv::Mat rotation, translation; + { + float acceptableReprojectionError = ofClamp(camera->getWidth() / 1000, 1, 10); + + //first pass EPNP + cv::solvePnPRansac(plane.objectPoints + , plane.imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotation + , translation + , false + , 100 + , acceptableReprojectionError + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_EPNP); + + //second pass ITERATIVE + cv::solvePnPRansac(plane.objectPoints + , plane.imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotation + , translation + , true + , 100 + , acceptableReprojectionError + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_ITERATIVE); + } + + //record reprojection error + plane.reprojectionError = getReprojectionError(plane.objectPoints + , plane.imagePoints + , rotation + , translation); + + //build up world space points + plane.boardTransform = ofxCv::makeMatrix(rotation, translation) * cameraTransform; + + for (const auto & objectPoint : plane.objectPoints) { + plane.worldPoints.push_back(ofxCv::toOf(objectPoint) * plane.boardTransform); + } + + if (!plane.plane.fitToPoints(plane.worldPoints, plane.planeFitResidual)) { + throw(Exception("Failed to fit board plane to points in " + spaceName + "space")); + } + + //take mean of object space points + plane.meanObjectPoint = mean(ofxCv::toOf(plane.objectPoints)); + }; + // + //-- + + + + //find board plane in real space + findBoardPlane(image, capture->realPlane, false); + + //find board plane in virtual space + findBoardPlane(image, capture->virtualPlane, true); + + //solve mirror plane + { + //choose an object point to use + capture->testObjectPoint = (capture->realPlane.meanObjectPoint + capture->virtualPlane.meanObjectPoint * ofVec3f(1, -1, 1)) / 2.0f; + + //get the test points in real and virtual planes + capture->realPlane.testPointWorld = capture->testObjectPoint * capture->realPlane.boardTransform; + capture->virtualPlane.testPointWorld = (capture->testObjectPoint * ofVec3f(1, -1, 1)) * capture->virtualPlane.boardTransform; + + capture->mirrorPlane = ofxRay::Plane(); + capture->mirrorPlane.setInfinite(true); + capture->mirrorPlane.setCenter((capture->realPlane.testPointWorld + capture->virtualPlane.testPointWorld) / 2.0f); + capture->mirrorPlane.setNormal((capture->realPlane.testPointWorld - capture->virtualPlane.testPointWorld).getNormalized()); + + + //where is the test point reflected? + { + const auto view = camera->getViewInWorldSpace(); + + //ray to test point in reflection + auto rayToReflectionPoint = ofxRay::Ray(camera->getPosition() + , ofVec3f() + , capture->color + , false); + + rayToReflectionPoint.setEnd(capture->virtualPlane.testPointWorld); + + if (!capture->mirrorPlane.intersect(rayToReflectionPoint, capture->estimatedPointOnMirrorPlane)) { + throw(new Exception("Ray towards reflected test point does not intersect with mirror plane")); + } + + capture->mirrorPlane.setCenter(capture->estimatedPointOnMirrorPlane); + } + + // for visualization purposes + capture->mirrorPlane.setScale({ 1.0f, 1.0f }); + capture->mirrorPlane.setUp({ 0.0f, 1.0f, 1.0f }); + capture->mirrorPlane.setInfinite(false); + capture->mirrorPlane.color = capture->color; + } + + //log to server + if (this->parameters.logToServer.enabled) { + try { + Utils::ScopedProcess scopedProcessLogToServer("Log to server"); + Json::Value requestJson; + + { + auto & data = requestJson["data"]; + + auto & jsonPlane = data["plane"]; + + jsonPlane["ABCD"] << capture->mirrorPlane.getABCD(); + jsonPlane["center"] << capture->mirrorPlane.getCenter(); + jsonPlane["normal"] << capture->mirrorPlane.getNormal(); + } + + ofHttpRequest request(this->parameters.logToServer.address.get(), "log"); + request.method = ofHttpRequest::Method::POST; + request.body = requestJson.toStyledString(); + request.contentType = "application/json"; + + ofURLFileLoader urlLoader; + auto response = urlLoader.handleRequest(request); + cout << response.data; + scopedProcessLogToServer.end(); + } + RULR_CATCH_ALL_TO_ALERT; + } + + this->captures.add(capture); + } + + //---------- + void BoardInMirror::addFolderOfImages(const std::filesystem::path & path) { + //get list of files + vector filePaths; + for (std::filesystem::directory_iterator it(path) + ; it != std::filesystem::directory_iterator() + ; ++it) { + filePaths.push_back(it->path().string()); + } + + Utils::ScopedProcess scopedProcess("Adding folder images", true, filePaths.size()); + + //for each file in path + for (auto filePath : filePaths) { + try { + Utils::ScopedProcess fileScopedProcess(filePath, false); + + //load image + auto image = cv::imread(filePath); + + //check it loaded + if (image.empty()) { + continue; + } + else { + //add it to the captures + this->addImage(image, ofFilePath::getBaseName(filePath)); + } + } + RULR_CATCH_ALL_TO({ + RULR_WARNING << e.what(); + }); + } + + scopedProcess.end(); + } + +#pragma mark Capture + //---------- + void BoardInMirror::Capture::drawWorld() { + this->drawPlane(this->realPlane); + this->drawPlane(this->virtualPlane); + + ofPushStyle(); + { + ofSetColor(100, 100, 255); + ofDrawSphere(this->realPlane.testPointWorld, 0.01); + + ofSetColor(100, 255, 100); + ofDrawSphere(this->virtualPlane.testPointWorld, 0.01); + } + ofPopStyle(); + + //draw the mirror + ofPushMatrix(); + { + ofQuaternion rotation; + rotation.makeRotate(ofVec3f(0, 0, 1), this->mirrorPlane.getNormal()); + + ofTranslate(this->mirrorPlane.getCenter()); + ofMultMatrix(ofMatrix4x4(rotation)); + + ofPushStyle(); + { + ofSetColor(this->color); + ofNoFill(); + ofDrawCircle(ofVec3f(), 0.35f / 2.0f); + } + ofPopStyle(); + } + ofPopMatrix(); + + ofVboMesh mesh; + mesh.setMode(OF_PRIMITIVE_LINES); + + //draw the rays for camera navigation + if (this->cameraNavigation.enabled) { + for (const auto & worldPoint : this->cameraNavigation.worldPoints) { + auto color = (ofFloatColor) this->color.get(); + mesh.addVertex(this->cameraNavigation.cameraPosition); + mesh.addColor(color); + + color.a = 0.0f; + mesh.addVertex(ofxCv::toOf(worldPoint)); + mesh.addColor(color); + } + } + + //draw the ray towards the test point where it lands on the mirror plane + { + mesh.addVertex(this->cameraNavigation.cameraPosition); + mesh.addColor(this->color.get()); + + mesh.addVertex(this->estimatedPointOnMirrorPlane); + mesh.addColor(this->color.get()); + } + + //draw the name if any + if (!this->name.empty()) { + ofDrawBitmapString(name, this->estimatedPointOnMirrorPlane); + } + + ofPushStyle(); + { + ofEnableAlphaBlending(); + mesh.draw(); + } + ofPopStyle(); + } + + //---------- + std::string BoardInMirror::Capture::getDisplayString() const { + stringstream message; + if (!this->name.empty()) { + message << this->name << endl; + } + + if (this->cameraNavigation.enabled) { + message << "Marker Map reprojection error : " << this->cameraNavigation.reprojectionError << endl; + } + message << "Real reprojection error : " << this->realPlane.reprojectionError << endl; + message << "Virtual reprojection error : " << this->virtualPlane.reprojectionError << endl; + return message.str(); + } + + //---------- + void BoardInMirror::Capture::drawPlane(const Plane & plane) const { + ofPushMatrix(); + { + ofMultMatrix(plane.boardTransform); + ofDrawAxis(0.1f); + } + ofPopMatrix(); + + ofPushStyle(); + { + ofSetColor(this->color); + ofxCv::drawCorners(plane.worldPoints, false); + } + ofPopStyle(); + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.h new file mode 100644 index 0000000000..daae9db394 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.h @@ -0,0 +1,94 @@ +#pragma once + +#include "ofxRulr.h" +#include "ofxNonLinearFit.h" +#include "ofxRulr/Utils/CaptureSet.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + class BoardInMirror : public Base { + public: + class Capture : public Utils::AbstractCaptureSet::BaseCapture { + public: + void drawWorld(); + string getDisplayString() const override; + + struct { + bool enabled; + vector imagePoints; + vector worldPoints; + ofVec3f cameraPosition; + ofParameter reprojectionError{ "Reprojection error", 0 }; + } cameraNavigation; + + struct Plane { + vector imagePoints; + vector objectPoints; + + ofxRay::Plane plane; + float reprojectionError; + float planeFitResidual; + ofMatrix4x4 boardTransform; + vector worldPoints; + ofVec3f worldPointCenter; + + ofVec3f meanObjectPoint; + ofVec3f testPointWorld; + }; + + Plane realPlane; + Plane virtualPlane; + + ofVec3f testObjectPoint; + ofVec3f estimatedPointOnMirrorPlane; + ofxRay::Plane mirrorPlane; + + string name; + protected: + void drawPlane(const Plane &) const; + }; + + BoardInMirror(); + string getTypeName() const override; + void init(); + void update(); + void addCapture(); + void calibrate(); + ofxCvGui::PanelPtr getPanel() override; + void populateInspector(ofxCvGui::InspectArguments &); + void drawWorldStage(); + + void addImage(const cv::Mat &, const string & name = ""); + void addFolderOfImages(const std::filesystem::path & path); + protected: + struct : ofParameterGroup { + struct : ofParameterGroup { + ofParameter enabled{ "Enabled", true }; + ofParameter minimumMarkers{ "Minimum markers", 3 }; + PARAM_DECLARE("Camera navigation", enabled, minimumMarkers); + } cameraNavigation; + + struct : ofParameterGroup { + ofParameter findBoardMode{ "Find board mode", FindBoardMode::Raw }; + ofParameter minimumCorners{ "Minimum corners", 8 }; + PARAM_DECLARE("Capture", findBoardMode, minimumCorners); + } capture; + + struct : ofParameterGroup { + ofParameter enabled{ "Enabled", false }; + ofParameter address{ "Address", "http://localhost:8000/Calibrate/LogServoValues"}; + PARAM_DECLARE("Log to server", enabled, address); + } logToServer; + + PARAM_DECLARE("SolveMirror", cameraNavigation, capture, logToServer); + } parameters; + shared_ptr panel; + + Utils::CaptureSet captures; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.cpp new file mode 100644 index 0000000000..c451c901e5 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.cpp @@ -0,0 +1,722 @@ +#include "pch_Plugin_Experiments.h" +#include "planesToPoint.h" + +template +T mean(const vector & points) { + T accumulator(0); + + for (const auto & point : points) { + accumulator += point; + } + return accumulator / size(points); +} + +#include "BoardOnMirror.h" + +float getMarkerReprojectionError(vector imagePoints, cv::Mat cameraMatrix, cv::Mat distortionCoefficients) { + cv::Mat translation, rotationVector; + + vector dummyObjectPoints; + { + dummyObjectPoints.emplace_back(0, 0, 0); + dummyObjectPoints.emplace_back(1, 0, 0); + dummyObjectPoints.emplace_back(1, 1, 0); + dummyObjectPoints.emplace_back(0, 1, 0); + } + + cv::solvePnP(dummyObjectPoints + , imagePoints + , cameraMatrix + , distortionCoefficients + , rotationVector + , translation); + + vector reprojectedPoints; + cv::projectPoints(dummyObjectPoints + , rotationVector + , translation + , cameraMatrix + , distortionCoefficients + , reprojectedPoints); + + //get RMS + float accumulate = 0.0f; + for (int i = 0; i < imagePoints.size(); i++) { + auto delta = reprojectedPoints[i] - imagePoints[i]; + accumulate += delta.dot(delta); + } + auto MS = accumulate / imagePoints.size(); + return sqrt(MS); +} + + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + //---------- + BoardOnMirror::BoardOnMirror() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string BoardOnMirror::getTypeName() const { + return "Experiments::MirrorPlaneCapture::BoardOnMirror"; + } + + //---------- + void BoardOnMirror::init() { + RULR_NODE_DRAW_WORLD_LISTENER; + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_UPDATE_LISTENER; + RULR_NODE_SERIALIZATION_LISTENERS; + + this->addInput(); + this->addInput(); + this->addInput(); + this->addInput(); + this->addInput(); + + this->panel = make_shared(); + this->captures.populateWidgets(this->panel); + + this->manageParameters(this->parameters); + } + + //---------- + void BoardOnMirror::update() { + + } + + //---------- + void BoardOnMirror::addCapture() { + this->throwIfMissingAConnection(); + + auto camera = this->getInput(); + auto grabber = camera->getGrabber(); + if (!grabber) { + throw(ofxRulr::Exception("Camera grabber not available")); + } + + auto frame = grabber->getFreshFrame(); + auto image = ofxCv::toCv(frame->getPixels()); + this->addImage(image); + } + + //---------- + void BoardOnMirror::calibrate() { + + } + + //---------- + ofxCvGui::PanelPtr BoardOnMirror::getPanel() { + return this->panel; + } + + //---------- + void BoardOnMirror::populateInspector(ofxCvGui::InspectArguments & inspectArgs) { + auto inspector = inspectArgs.inspector; + inspector->addButton("Add capture", [this]() { + try { + Utils::ScopedProcess scopedProcess("Add capture"); + this->addCapture(); + scopedProcess.end(); + } + RULR_CATCH_ALL_TO_ALERT; + }, ' ')->setHeight(100.0f); + + inspector->addButton("Add folder of images...", [this]() { + auto result = ofSystemLoadDialog("Folder of calibration images", true); + if (result.bSuccess) { + this->addFolderOfImages(std::filesystem::path(result.getPath())); + } + }); + + inspector->addButton("Calibrate", [this]() { + try { + this->calibrate(); + } + RULR_CATCH_ALL_TO_ALERT; + }, OF_KEY_RETURN)->setHeight(100.0f); + + inspector->addButton("Mean plane to clipboard", [this]() { + try { + this->copyMeanPlaneToClipboard(); + } + RULR_CATCH_ALL_TO_ALERT; + }, OF_KEY_RETURN)->setHeight(100.0f); + + inspector->addButton("Project heliostats from planes", [this]() { + try { + this->projectToHeliostats(); + } + RULR_CATCH_ALL_TO_ALERT; + }); + } + + //---------- + void BoardOnMirror::drawWorldStage() { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawWorld(); + } + } + + //---------- + void BoardOnMirror::addImage(const cv::Mat & image, const string & name) { + this->throwIfMissingAConnection(); + this->throwIfMissingAConnection(); + + auto camera = this->getInput(); + auto board = this->getInput(); + + auto capture = make_shared(); + + capture->name = name; + + //function to test reprojection error for a set of points given a translation, rotation + auto getReprojectionError = [&camera](const vector & objectPoints + , const vector & imagePoints + , const cv::Mat & rotationVector + , const cv::Mat & translation) { + + float error = 0.0f; + vector reprojectedPoints; + cv::projectPoints(objectPoints + , rotationVector + , translation + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , reprojectedPoints); + + //Take square mean + for (size_t i = 0; i < imagePoints.size(); i++) { + auto delta = imagePoints[i] - reprojectedPoints[i]; + error += delta.x * delta.x + delta.y * delta.y; + } + //RMS + error /= (float)imagePoints.size(); + error = sqrt(error); + + return error; + }; + + //resolve pose of camera using the marker map + capture->cameraNavigation.enabled = this->parameters.cameraNavigation.enabled; + if (this->parameters.cameraNavigation.enabled) { + this->throwIfMissingAConnection(); + this->throwIfMissingAConnection(); + + auto detector = this->getInput(); + auto markerMap = this->getInput()->getMarkerMap(); + + auto markers = detector->findMarkers(image); + if (markers.size() < this->parameters.cameraNavigation.minimumMarkers) { + throw(ofxRulr::Exception("Found " + ofToString(markers.size()) + " real markers. Need " + ofToString(this->parameters.cameraNavigation.minimumMarkers) + " for camera navigation")); + } + + //build up dataset + vector worldPoints; + vector imagePoints; + { + int countGoodMarkers = 0; + for (const auto & marker : markers) { + try { + //test reprojection error + auto markerReprojectionError = getMarkerReprojectionError((vector &) marker + , camera->getCameraMatrix() + , camera->getDistortionCoefficients()); + + if (markerReprojectionError <= this->parameters.cameraNavigation.maxMarkerError) { + countGoodMarkers++; + auto marker3D = markerMap->getMarker3DInfo(marker.id); + for (int i = 0; i < 4; i++) { + worldPoints.push_back(marker3D.points[i]); + imagePoints.push_back(marker[i]); + } + } + } + RULR_CATCH_ALL_TO_ERROR; //in the case that the marker did not exist + } + + if (countGoodMarkers < this->parameters.cameraNavigation.minimumMarkers) { + throw(ofxRulr::Exception("Found " + ofToString(countGoodMarkers) + " good markers in map. Need " + ofToString(this->parameters.cameraNavigation.minimumMarkers) + " for camera navigation")); + } + } + + cv::Mat rotationVector, translation; + camera->getExtrinsics(rotationVector, translation, true); + + //Solve camera extrinsics + { + //first pass EPNP + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true + , 100 + , 5.0f + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_EPNP); + + //second pass ITERATIVE + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true + , 100 + , 5.0f + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_ITERATIVE); + } + + camera->setExtrinsics(rotationVector, translation, true); + + capture->cameraNavigation.imagePoints = imagePoints; + capture->cameraNavigation.worldPoints = worldPoints; + capture->cameraNavigation.reprojectionError = getReprojectionError(worldPoints + , imagePoints + , rotationVector + , translation); + capture->cameraNavigation.cameraPosition = camera->getPosition(); + } + + auto cameraTransform = camera->getTransform(); + + //-- + //find board plane function + // + // + auto findBoardPlane = [this, camera, board, &getReprojectionError, &cameraTransform](const cv::Mat & image, Capture::Plane & plane) { + + //get board image and object space points + if (!board->findBoard(image + , plane.imagePoints + , plane.objectPoints + , this->parameters.capture.findBoardMode + , camera->getCameraMatrix() + , camera->getDistortionCoefficients())) { + throw(Exception("Failed to find board")); + } + + //check we found enough corners + if (plane.objectPoints.size() < this->parameters.capture.minimumCorners) { + throw(Exception("Not enough corners found (" + ofToString(plane.objectPoints.size()) + " found. Needs " + ofToString(this->parameters.capture.minimumCorners) + ")")); + } + + cv::Mat rotation, translation; + { + float acceptableReprojectionError = ofClamp(camera->getWidth() / 1000, 1, 10); + + //second pass ITERATIVE + cv::solvePnP(plane.objectPoints + , plane.imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotation + , translation + , false + , cv::SOLVEPNP_ITERATIVE); + } + + //record reprojection error + plane.reprojectionError = getReprojectionError(plane.objectPoints + , plane.imagePoints + , rotation + , translation); + + //build up world space points + plane.boardTransform = ofxCv::makeMatrix(rotation, translation) * cameraTransform; + + for (const auto & objectPoint : plane.objectPoints) { + plane.worldPoints.push_back(ofxCv::toOf(objectPoint) * plane.boardTransform); + } + + if (!plane.plane.fitToPoints(plane.worldPoints, plane.planeFitResidual)) { + throw(Exception("Failed to fit board plane")); + } + + //take mean of object space points + plane.meanObjectPoint = mean(ofxCv::toOf(plane.objectPoints)); + plane.meanWorldPoint = mean(plane.worldPoints); + plane.plane.setCenter(plane.meanWorldPoint); + + //this seems to be the data we actually want. And the normal should be consistent + plane.plane.setNormal(-(ofVec3f) plane.boardTransform.getRowAsVec3f(2)); + }; + // + //-- + + + //find board plane + findBoardPlane(image, capture->mirrorPlane); + + + //log to server + if (this->parameters.logToServer.enabled) { + try { + Utils::ScopedProcess scopedProcessLogToServer("Log to server"); + Json::Value requestJson; + + { + auto & data = requestJson["data"]; + + auto & jsonPlane = data["plane"]; + + jsonPlane["ABCD"] << capture->mirrorPlane.plane.getABCD(); + jsonPlane["center"] << capture->mirrorPlane.plane.getCenter(); + jsonPlane["normal"] << capture->mirrorPlane.plane.getNormal(); + } + + ofHttpRequest request(this->parameters.logToServer.address.get(), "log"); + request.method = ofHttpRequest::Method::POST; + request.body = requestJson.toStyledString(); + request.contentType = "application/json"; + + ofURLFileLoader urlLoader; + auto response = urlLoader.handleRequest(request); + cout << response.data; + scopedProcessLogToServer.end(); + } + RULR_CATCH_ALL_TO_ALERT; + } + + this->captures.add(capture); + } + + //---------- + void BoardOnMirror::addFolderOfImages(const std::filesystem::path & path) { + //get list of files + vector filePaths; + for (std::filesystem::directory_iterator it(path) + ; it != std::filesystem::directory_iterator() + ; ++it) { + filePaths.push_back(it->path().string()); + } + + Utils::ScopedProcess scopedProcess("Adding folder images", true, filePaths.size()); + + //for each file in path + for (auto filePath : filePaths) { + try { + Utils::ScopedProcess fileScopedProcess(filePath, false); + + //load image + auto image = cv::imread(filePath); + + //check it loaded + if (image.empty()) { + continue; + } + else { + //add it to the captures + this->addImage(image, ofFilePath::getBaseName(filePath)); + } + } + RULR_CATCH_ALL_TO({ + RULR_WARNING << e.what(); + }); + } + + scopedProcess.end(); + } + + //---------- + void BoardOnMirror::copyMeanPlaneToClipboard() const { + auto captures = this->captures.getSelection(); + if (captures.empty()) { + throw(Exception("No captures available")); + } + + ofVec3f meanCenter, meanNormal; + for (auto capture : captures) { + meanCenter += capture->mirrorPlane.plane.getCenter(); + meanNormal += capture->mirrorPlane.plane.getNormal(); + } + meanCenter /= captures.size(); + meanNormal /= captures.size(); + meanNormal.normalize(); + + ofxRay::Plane meanPlane; + meanPlane.setCenter(meanCenter); + meanPlane.setNormal(meanNormal); + + stringstream ss; + ss << meanPlane.getABCD(); + ofxClipboard::copy(ss.str()); + } + + //---------- + void BoardOnMirror::projectToHeliostats() { + this->throwIfMissingAConnection(); + auto heliostatsNode = this->getInput(); + + //get our selected captures + auto captures = this->captures.getSelection(); + + //existing heliostats + auto existingHeliostats = heliostatsNode->getHeliostats(); + + set> heliostatsToAdd; + + map, vector>> capturesToMove; + + for (auto capture : captures) { + auto backPosition = capture->mirrorPlane.meanWorldPoint + - capture->mirrorPlane.plane.getNormal() + * this->parameters.heliostatProjection.planeToA2; + + //check if matching heliostat already exists + shared_ptr heliostat; + for (auto heliostatOther : existingHeliostats) { + if ((heliostatOther->position.get() - backPosition).length() < this->parameters.heliostatProjection.distanceThreshold) { + heliostat = heliostatOther; + break; + } + } + + //if it doesn't already exist, check if it's one we are already trying to add + if (!heliostat) { + for (auto heliostatOther : heliostatsToAdd) { + if ((heliostatOther->position.get() - backPosition).length() < this->parameters.heliostatProjection.distanceThreshold) { + heliostat = heliostatOther; + break; + } + } + } + + //if it doesn't already exist, create it + if (!heliostat) { + heliostat = make_shared(); + heliostat->position = backPosition; + + + static int index = 0; + heliostat->name = "H-auto" + ofToString(index++); + + heliostatsToAdd.insert(heliostat); + } + + auto dictIt = capturesToMove.find(heliostat); + if (dictIt == capturesToMove.end()) { + capturesToMove.emplace(heliostat, vector>()); + dictIt = capturesToMove.find(heliostat); + } + + dictIt->second.push_back(capture); + } + + //kill heliostats which aren't full enough + for (auto it = heliostatsToAdd.begin(); it != heliostatsToAdd.end();) { + auto heliostat = *it; + if (capturesToMove[heliostat].size() < this->parameters.heliostatProjection.minimumCaptures) { + it = heliostatsToAdd.erase(it); + } + else { + it++; + } + } + + //move captures + for (auto it : capturesToMove) { + auto heliostat = it.first; + auto captures = it.second; + + //move the captures we moved from our set to that set + for (auto capture : captures) { + //clone by serialization + Json::Value json; + capture->serialize(json); + auto captureClone = make_shared(); + captureClone->deserialize(json); + heliostat->captures.add(captureClone); + + capture->onDeletePressed.notifyListeners(); + } + + //add them to the heliostats node if it's in the set + //if (heliostatsToAdd.find(heliostat) != heliostatsToAdd.end()) { + //HACK WTF? + heliostatsNode->add(heliostat); + //} + + //recalc centers for heliostats + heliostat->calcPosition(this->parameters.heliostatProjection.planeToA2); + } + } + + //---------- + void BoardOnMirror::serialize(Json::Value & json) { + this->captures.serialize(json["captures"]); + } + + //---------- + void BoardOnMirror::deserialize(const Json::Value & json) { + this->captures.deserialize(json["captures"]); + } + +#pragma mark Capture + //---------- + BoardOnMirror::Capture::Capture() { + RULR_SERIALIZE_LISTENERS; + } + + //---------- + void BoardOnMirror::Capture::drawWorld() { + this->drawPlane(this->mirrorPlane); + + //draw the mirror + ofPushMatrix(); + { + ofQuaternion rotation; + rotation.makeRotate(ofVec3f(0, 0, 1), this->mirrorPlane.plane.getNormal()); + + ofTranslate(this->mirrorPlane.plane.getCenter()); + ofMultMatrix(ofMatrix4x4(rotation)); + + ofPushStyle(); + { + ofSetColor(this->color); + ofNoFill(); + ofDrawCircle(ofVec3f(), 0.35f / 2.0f); + } + ofPopStyle(); + } + ofPopMatrix(); + + ofVboMesh mesh; + mesh.setMode(OF_PRIMITIVE_LINES); + + //draw the rays for camera navigation + if (this->cameraNavigation.enabled) { + for (const auto & worldPoint : this->cameraNavigation.worldPoints) { + auto color = (ofFloatColor) this->color.get(); + mesh.addVertex(this->cameraNavigation.cameraPosition); + mesh.addColor(color); + + color.a = 0.0f; + mesh.addVertex(ofxCv::toOf(worldPoint)); + mesh.addColor(color); + } + } + + //draw the ray towards the test point where it lands on the mirror plane + { + mesh.addVertex(this->cameraNavigation.cameraPosition); + mesh.addColor(this->color.get()); + + mesh.addVertex(this->mirrorPlane.meanWorldPoint); + mesh.addColor(this->color.get()); + } + + //draw the name if any + if (!this->name.empty()) { + ofDrawBitmapString(name, this->mirrorPlane.meanWorldPoint); + } + + ofPushStyle(); + { + ofEnableAlphaBlending(); + mesh.draw(); + } + ofPopStyle(); + } + + //---------- + std::string BoardOnMirror::Capture::getDisplayString() const { + stringstream message; + if (!this->name.empty()) { + message << this->name << endl; + } + + if (this->cameraNavigation.enabled) { + message << "Marker Map reprojection error : " << this->cameraNavigation.reprojectionError << endl; + } + message << "'Board reprojection error : " << this->mirrorPlane.reprojectionError << endl; + return message.str(); + } + + //---------- + void BoardOnMirror::Capture::serialize(Json::Value & json) { + json["name"] << this->name; + json["heliostatName"] << this->heliostatName; + + { + auto & jsonCameraNavigation = json["cameraNavigation"]; + jsonCameraNavigation["enabled"] = this->cameraNavigation.enabled; + jsonCameraNavigation["imagePoints"] << ofxCv::toOf(this->cameraNavigation.imagePoints); + jsonCameraNavigation["worldPoints"] << ofxCv::toOf(this->cameraNavigation.worldPoints); + jsonCameraNavigation["cameraPosition"] << this->cameraNavigation.cameraPosition; + Utils::Serializable::serialize(jsonCameraNavigation, this->cameraNavigation.reprojectionError); + } + + { + auto & jsonPlane = json["mirrorPlane"]; + jsonPlane["imagePoints"] << ofxCv::toOf(this->mirrorPlane.imagePoints); + jsonPlane["worldPoints"] << ofxCv::toOf(this->mirrorPlane.objectPoints); + jsonPlane["plane"] << this->mirrorPlane.plane; + jsonPlane["reprojectionError"] << this->mirrorPlane.reprojectionError; + jsonPlane["planeFitResidual"] << this->mirrorPlane.planeFitResidual; + jsonPlane["boardTransform"] << this->mirrorPlane.boardTransform; + jsonPlane["worldPoints"] << this->mirrorPlane.worldPoints; + jsonPlane["meanObjectPoint"] << this->mirrorPlane.meanObjectPoint; + jsonPlane["meanWorldPoint"] << this->mirrorPlane.meanWorldPoint; + } + } + + //---------- + void BoardOnMirror::Capture::deserialize(const Json::Value & json) { + json["name"] >> this->name; + json["heliostatName"] >> this->heliostatName; + + { + const auto & jsonCameraNavigation = json["cameraNavigation"]; + this->cameraNavigation.enabled = jsonCameraNavigation["enabled"].asBool(); + jsonCameraNavigation["imagePoints"] >> ofxCv::toOf(this->cameraNavigation.imagePoints); + jsonCameraNavigation["worldPoints"] >> ofxCv::toOf(this->cameraNavigation.worldPoints); + jsonCameraNavigation["cameraPosition"] >> this->cameraNavigation.cameraPosition; + Utils::Serializable::deserialize(jsonCameraNavigation, this->cameraNavigation.reprojectionError); + } + + { + const auto & jsonPlane = json["mirrorPlane"]; + jsonPlane["imagePoints"] >> ofxCv::toOf(this->mirrorPlane.imagePoints); + jsonPlane["worldPoints"] >> ofxCv::toOf(this->mirrorPlane.objectPoints); + jsonPlane["plane"] >> this->mirrorPlane.plane; + jsonPlane["reprojectionError"] >> this->mirrorPlane.reprojectionError; + jsonPlane["planeFitResidual"] >> this->mirrorPlane.planeFitResidual; + jsonPlane["boardTransform"] >> this->mirrorPlane.boardTransform; + jsonPlane["worldPoints"] >> this->mirrorPlane.worldPoints; + jsonPlane["meanObjectPoint"] >> this->mirrorPlane.meanObjectPoint; + jsonPlane["meanWorldPoint"] >> this->mirrorPlane.meanWorldPoint; + } + } + + //---------- + void BoardOnMirror::Capture::drawPlane(const Plane & plane) const { + ofPushMatrix(); + { + ofMultMatrix(plane.boardTransform); + ofDrawAxis(0.1f); + } + ofPopMatrix(); + + ofPushStyle(); + { + ofSetColor(this->color); + ofxCv::drawCorners(plane.worldPoints, false); + } + ofPopStyle(); + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.h new file mode 100644 index 0000000000..80c563d014 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.h @@ -0,0 +1,113 @@ +#pragma once + +#include "ofxRulr.h" +#include "ofxNonLinearFit.h" +#include "ofxRulr/Utils/CaptureSet.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + class BoardOnMirror : public Base { + public: + class Capture : public Utils::AbstractCaptureSet::BaseCapture { + public: + Capture(); + void drawWorld(); + string getDisplayString() const override; + + struct { + bool enabled; + vector imagePoints; + vector worldPoints; + ofVec3f cameraPosition; + ofParameter reprojectionError{ "Reprojection error", 0 }; + } cameraNavigation; + + struct Plane { + vector imagePoints; + vector objectPoints; + + ofxRay::Plane plane; + float reprojectionError; + float planeFitResidual; + ofMatrix4x4 boardTransform; + vector worldPoints; + + ofVec3f meanObjectPoint; + ofVec3f meanWorldPoint; + }; + + Plane mirrorPlane; + + string name; + string heliostatName; + + void serialize(Json::Value &); + void deserialize(const Json::Value &); + protected: + void drawPlane(const Plane &) const; + }; + + BoardOnMirror(); + string getTypeName() const override; + void init(); + void update(); + void addCapture(); + void calibrate(); + ofxCvGui::PanelPtr getPanel() override; + void populateInspector(ofxCvGui::InspectArguments &); + void drawWorldStage(); + + void addImage(const cv::Mat &, const string & name = ""); + void addFolderOfImages(const std::filesystem::path & path); + + void copyMeanPlaneToClipboard() const; + + void projectToHeliostats(); + + void serialize(Json::Value &); + void deserialize(const Json::Value &); + protected: + struct : ofParameterGroup { + struct : ofParameterGroup { + ofParameter enabled{ "Enabled", true }; + ofParameter minimumMarkers{ "Minimum markers", 3 }; + ofParameter maxMarkerError{ "Max marker error", 0.5f }; + PARAM_DECLARE("Camera navigation", enabled, minimumMarkers, maxMarkerError); + } cameraNavigation; + + struct : ofParameterGroup { + ofParameter findBoardMode{ "Find board mode", FindBoardMode::Raw }; + ofParameter minimumCorners{ "Minimum corners", 8 }; + PARAM_DECLARE("Capture", findBoardMode, minimumCorners); + } capture; + + struct : ofParameterGroup { + ofParameter planeToA2{ "Plane to A2 [m]", 0.15f }; + ofParameter distanceThreshold{ "Distance threshold [m]", 0.2f }; + ofParameter minimumCaptures{ "Minimum captures", 2 }; + PARAM_DECLARE("Heliostat projection", distanceThreshold, minimumCaptures); + } heliostatProjection; + +// struct : ofParameterGroup { +// ofParameter navigation{ "Navigation", WhenDrawOnWorldStage::Selected }; +// PARAM_DECLARE("Draw", navigation); +// } draw; + + struct : ofParameterGroup { + ofParameter enabled{ "Enabled", false }; + ofParameter address{ "Address", "http://localhost:8000/Calibrate/LogServoValues" }; + PARAM_DECLARE("Log to server", enabled, address); + } logToServer; + + PARAM_DECLARE("SolveMirror", cameraNavigation, capture, heliostatProjection, logToServer); + } parameters; + shared_ptr panel; + + Utils::CaptureSet captures; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.cpp new file mode 100644 index 0000000000..b7274b6b9e --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.cpp @@ -0,0 +1,555 @@ +#include "pch_Plugin_Experiments.h" + +#define INCHES_PER_METER (1.0f / 0.0254f) + +#define ARUCO_MIP_16h3_NonMirroring {0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 78, 79, 80, 81 } + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + //---------- + HaloBoard::HaloBoard() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + std::string HaloBoard::getTypeName() const { + return "ArUco::HaloBoard"; + } + + //---------- + void HaloBoard::init() { + RULR_NODE_UPDATE_LISTENER; + + auto panel = ofxCvGui::Panels::makeImage(this->preview); + panel->onDrawImage += [this](ofxCvGui::DrawImageArguments & args) { + //paper sizes + ofPushMatrix(); + { + float mmToPixels = 1 / 1000.0f * INCHES_PER_METER * this->parameters.preview.DPI; + ofScale(mmToPixels, mmToPixels); + + ofColor baseColor(200, 100, 100); + int index = 0; + for (auto paperSize : this->paperSizes) { + auto paperColor = baseColor; + paperColor.setHue(index * 40); + + ofPushStyle(); + { + ofSetColor(paperColor); + + //outline + ofNoFill(); + ofDrawRectangle(0, 0, 297, 210); + + //text + ofDrawBitmapString(paperSize->name, paperSize->width, paperSize->height); + } + ofPopStyle(); + + index++; + } + } + ofPopMatrix(); + }; + this->panel = panel; + + this->manageParameters(this->parameters); + + { + this->paperSizes.emplace(new PaperSize{ "A0", 1189, 841 }); + this->paperSizes.emplace(new PaperSize{ "A1", 841, 594 }); + this->paperSizes.emplace(new PaperSize{ "A2", 594, 420 }); + this->paperSizes.emplace(new PaperSize{ "A3", 420, 297 }); + this->paperSizes.emplace(new PaperSize{ "A4", 297, 210 }); + } + + this->dictionary = aruco::Dictionary::load("ARUCO_MIP_16h3"); + + this->detector = make_shared(this->dictionary.getName()); + { + auto & params = this->detector->getParameters(); + params.enclosedMarker = true; + } + } + + //---------- + void HaloBoard::update() { + //delete preview if it's wrong + if (this->parameters.size.width != this->cachedParameters.size.width + || this->parameters.size.height != this->cachedParameters.size.height + + || this->parameters.length.square != this->cachedParameters.length.square + || this->parameters.length.outerCorner != this->cachedParameters.length.outerCorner + + || this->parameters.preview.DPI != this->cachedParameters.preview.DPI) { + //we need to rebuild the preview + this->board.reset(); + } + + //rebuild the board if needed + if (!this->board) { + try { + auto board = make_shared(); + + // Function for adding corners + auto addCorner = [board](const Board::Position & position, const Board::MarkerCorner & markerCorner) { + //find if corner already exists + Board::BoardCorner * cornerData = nullptr; + + for (auto & corner : board->corners) { + if (corner.position == position) { + cornerData = &corner; + } + } + + //make one if not + if (!cornerData) { + board->corners.push_back(Board::BoardCorner()); + cornerData = &board->corners.back(); + cornerData->position = position; + } + + cornerData->markerCorners.push_back(markerCorner); + }; + + // Allocate the fbo + ofFbo previewFbo; + { + auto pixelSize = this->getPhysicalSize() * this->getPreviewPixelsPerMeter(); + ofFbo::Settings fboSettings; + { + fboSettings.width = pixelSize.x; + fboSettings.height = pixelSize.y; + fboSettings.internalformat = GL_RGBA; + }; + + previewFbo.allocate(fboSettings); + } + + previewFbo.begin(); + { + ofClear(255, 255); + + ofPushMatrix(); + { + //change into meters + auto pixelsPerMeter = this->getPreviewPixelsPerMeter(); + ofScale(pixelsPerMeter, pixelsPerMeter); + + //move in by the outer corner amount + ofTranslate(this->parameters.length.outerCorner, this->parameters.length.outerCorner); + + //scale by square + const auto square = this->parameters.length.square; + ofScale(square, square); + + auto markerIDs = this->getNonMirroringIDs(); + auto markerIDIterator = markerIDs.begin(); + + ofImage markerPreview; + + //Iterate over squares + for (int y = 0; y < this->parameters.size.height; y++) { + for (int x = 0; x < this->parameters.size.width; x++) { + if (x % 2 == y % 2) { + //active squares + + if (markerIDIterator == markerIDs.end()) { + throw(Exception("Not enough markers available in dictionary for this board design")); + } + + auto markerID = *markerIDIterator++; + + //make the marker image + { + auto markerImage = this->dictionary.getMarkerImage_id(markerID + , 1 + , false + , false + , false); + cv::Mat markerImageColor; + cv::cvtColor(markerImage, markerImageColor, CV_GRAY2RGBA); + ofxCv::copy(markerImageColor, markerPreview); + markerPreview.update(); + + // Turn off filtering + markerPreview.getTexture().setTextureMinMagFilter(GL_NEAREST, GL_NEAREST); + } + + //render the image into the fbo + markerPreview.draw(x, y, 1, 1); + + //Store the data + { + //Marker + { + Board::Marker marker{ + markerID, + Board::Position { + x, + y + } + }; + + board->markers.emplace(markerID, marker); + } + + //Corners + { + addCorner(Board::Position{ x, y }, Board::MarkerCorner{ markerID, 0 }); + addCorner(Board::Position{ x + 1, y }, Board::MarkerCorner{ markerID, 1 }); + addCorner(Board::Position{ x + 1, y + 1 }, Board::MarkerCorner{ markerID, 2 }); + addCorner(Board::Position{ x, y + 1 }, Board::MarkerCorner{ markerID, 3 }); + } + } + } + } + } + + //Draw the outside parts + { + auto outsideSize = this->parameters.length.outerCorner / this->parameters.length.square; + + ofPushStyle(); + { + ofFill(); + ofSetLineWidth(0); + ofSetColor(0, 255); + + auto width = this->parameters.size.width.get(); + auto height = this->parameters.size.height.get(); + + //top left + { + ofPath arc; + arc.setCircleResolution(100); + arc.arc(ofVec2f(0, 0), outsideSize, outsideSize, 180, 270); + arc.setFillColor(ofColor(0)); + arc.setFilled(true); + arc.close(); + arc.draw(); + } + + //bottom left + if (height % 2 == 1) { + ofPath arc; + arc.setCircleResolution(100); + arc.arc(ofVec2f(0, height), outsideSize, outsideSize, 90, 180); + arc.setFillColor(ofColor(0)); + arc.setFilled(true); + arc.close(); + arc.draw(); + } + + //top right + if (width % 2 == 1) { + ofPath arc; + arc.setCircleResolution(100); + arc.arc(ofVec2f(width, 0), outsideSize, outsideSize, 270, 360); + arc.setFillColor(ofColor(0)); + arc.setFilled(true); + arc.close(); + arc.draw(); + } + + //bottom right + if (width % 2 == 1 && height % 2 == 1) { + ofPath arc; + arc.setCircleResolution(100); + arc.arc(ofVec2f(width, height), outsideSize, outsideSize, 0, 90); + arc.setFillColor(ofColor(0)); + arc.setFilled(true); + arc.close(); + arc.draw(); + } + + //top edge + for (int x = 0; x < width; x++) { + if (x % 2 == 1) { + ofDrawRectangle(x, -outsideSize, 1, outsideSize); + } + } + + //left edge + for (int y = 0; y < height; y++) { + if (y % 2 == 1) { + ofDrawRectangle(-outsideSize, y, outsideSize, 1); + } + } + + //bottom edge + for (int x = 0; x < width; x++) { + if (x % 2 == height % 2) { + ofDrawRectangle(x, height, 1, outsideSize); + } + } + + //right edge + for (int y = 0; y < height; y++) { + if (y % 2 == width % 2) { + ofDrawRectangle(width, y, outsideSize, 1); + } + } + } + ofPopStyle(); + } + } + ofPopMatrix(); + } + previewFbo.end(); + + //copy the preview into the image + { + previewFbo.readToPixels(this->preview.getPixels()); + this->preview.update(); + } + + //sort the board + { + sort(board->corners.begin(), board->corners.end(), + [](const Board::BoardCorner & a, const Board::BoardCorner & b) -> bool + { + if (a.position.y == b.position.y) { + return a.position.x < b.position.x; + } + else { + return a.position.y < b.position.y; + } + + }); + } + + //save the board + this->board = board; + + //cache parameters + { + this->cachedParameters.size.width = this->parameters.size.width; + this->cachedParameters.size.height = this->parameters.size.height; + + this->cachedParameters.length.square = this->parameters.length.square; + this->cachedParameters.length.outerCorner = this->parameters.length.outerCorner; + + this->cachedParameters.preview.DPI = this->parameters.preview.DPI; + } + } + RULR_CATCH_ALL_TO_ERROR + } + } + + //---------- + void HaloBoard::serialize(Json::Value & json) { + Utils::Serializable::serialize(json, this->parameters); + } + + //---------- + void HaloBoard::deserialize(const Json::Value & json) { + Utils::Serializable::deserialize(json, this->parameters); + } + + //---------- + void HaloBoard::populateInspector(ofxCvGui::InspectArguments & inspectArgs) { + auto inspector = inspectArgs.inspector; + inspector->addParameterGroup(this->parameters); + inspector->addLiveValue("Physical size [m]", [this]() { + return this->getPhysicalSize(); + }); + } + + //---------- + //https://github.com/opencv/opencv_contrib/blob/master/modules/aruco/samples/detect_board_charuco.cpp + bool HaloBoard::findBoard(cv::Mat image, vector & imagePoints, vector & objectPoints, FindBoardMode, cv::Mat cameraMatrix, cv::Mat distortionCoefficients) const { + if (!this->board) { + throw(Exception("Board is not setup")); + } + + cv::Mat imageGrey; + if (image.channels() == 1) { + imageGrey = image; + } + else { + cv::cvtColor(image, imageGrey, CV_RGB2GRAY); + } + + if (this->parameters.detection.openCorners > 0) { + //get the corners in the image + vector corners; + cv::goodFeaturesToTrack(imageGrey + , corners + , 2000 + , 0.01 + , 10); + + //draw circles over the corners + for (auto & corner : corners) { + cv::circle(imageGrey + , corner + , this->parameters.detection.openCorners + , cv::Scalar(255) + , -1); + } + } + + imagePoints.clear(); + objectPoints.clear(); + + //Find the markers + vector foundMarkers; + { + this->detector->detect(imageGrey + , foundMarkers + , cameraMatrix + , distortionCoefficients + , this->parameters.length.square); + + if (foundMarkers.empty()) { + return false; + } + } + + //Corner finds + map> markerCornerFinds; + vector markerCornerScale; // scale in pixels for a square meeting at this corner (for subpix) + + for (const auto & marker : foundMarkers) { + + //for each corner of a found marker + for (int i = 0; i < 4; i++) { + Board::MarkerCorner markerCorner{ + marker.id, + i + }; + + //for each corner on the board + for (int boardCornerIndex = 0; boardCornerIndex < this->board->corners.size(); boardCornerIndex++) { + const auto & boardCorner = this->board->corners[boardCornerIndex]; + + //check if that corner matches this MarkerCorner + for (const auto & boardMarkerCorner : boardCorner.markerCorners) { + if (boardMarkerCorner == markerCorner) { + //store it into there + markerCornerFinds[boardCornerIndex].push_back(marker[i]); + + //get the length of a square side meeting this corner + markerCornerScale.push_back((ofxCv::toOf(marker[(i + 1) % 4]) - ofxCv::toOf(marker[i])).length()); + + //save some time, nothing else should match in this loop + break; + } + } + } + } + } + + { + const auto square = this->parameters.length.square.get(); + for (const auto & it : markerCornerFinds) { + const auto & boardCorner = this->board->corners[it.first]; + + //take the mean + ofVec2f mean; + { + ofVec2f accumulate; + for (const auto & find : it.second) { + accumulate += ofxCv::toOf(find); + } + mean = accumulate / it.second.size(); + } + + const auto & position = this->board->corners[it.first].position; + + imagePoints.push_back(ofxCv::toCv(mean)); + objectPoints.push_back(ofxCv::toCv(ofVec3f(position.x, position.y, 0.0f) * square)); + } + } + + if (imagePoints.empty()) { + return false; + } + + //perform sub-pixel refinement + if(this->parameters.refinement.enabled) { + float mean = 0.0f; + { + float accumulate = 0.0f; + for (const auto & find : markerCornerScale) { + accumulate += find; + } + mean = accumulate / markerCornerScale.size(); + } + + int subPixSearch = mean / 8.0f; //markers are 6x6 pixels (4x4 data) + + subPixSearch = (subPixSearch / 2) * 2 + 1; // ensure odd + if (subPixSearch < 1) { + subPixSearch = 1; // ensure non zero + } + + int zeroZone = mean * 0.01f; + + cv::cornerSubPix(imageGrey + , imagePoints + , cv::Size(subPixSearch + , subPixSearch) + , cv::Size(zeroZone + , zeroZone) + , cv::TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 50, 1e-6)); + } + + return true; + } + + //---------- + void HaloBoard::drawObject() const { + if (this->board) { + auto pixelsPerMeter = this->getPreviewPixelsPerMeter(); + auto square = this->parameters.length.square; + auto outerCorner = this->parameters.length.outerCorner; + + this->preview.draw(-outerCorner + , -outerCorner + , square * this->parameters.size.width + 2 * outerCorner + , square * this->parameters.size.height + 2 * outerCorner); + } + } + + //---------- + float HaloBoard::getSpacing() const { + return this->parameters.length.square.get(); + } + + //---------- + ofVec3f HaloBoard::getCenter() const { + return ofVec3f(this->parameters.size.width * this->parameters.length.square + , this->parameters.size.height * this->parameters.length.square + , 0.0f) / 2.0f; + } + + //---------- + ofxCvGui::PanelPtr HaloBoard::getPanel() { + return this->panel; + } + + //---------- + ofVec2f HaloBoard::getPhysicalSize() const { + return ofVec2f(this->parameters.size.width * this->parameters.length.square + , this->parameters.size.height * this->parameters.length.square) + + (this->parameters.length.outerCorner * 2); + } + + //---------- + std::vector HaloBoard::getNonMirroringIDs() const { + return vector ARUCO_MIP_16h3_NonMirroring; + } + + //---------- + float HaloBoard::getPreviewPixelsPerMeter() const { + return INCHES_PER_METER * this->parameters.preview.DPI; + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.h new file mode 100644 index 0000000000..b2d9f1be41 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.h @@ -0,0 +1,125 @@ +#pragma once + +#include "Constants_Plugin_ArUco.h" +#include "ofxRulr/Nodes/Item/AbstractBoard.h" +#include + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + class HaloBoard : public Nodes::Item::AbstractBoard { + public: + struct PaperSize { + string name; + float width; + float height; + }; + + HaloBoard(); + string getTypeName() const override; + void init(); + void update(); + void serialize(Json::Value &); + void deserialize(const Json::Value &); + void populateInspector(ofxCvGui::InspectArguments &); + + bool findBoard(cv::Mat, vector & imagePoints, vector & objectPoints, FindBoardMode findBoardMode, cv::Mat cameraMatrix, cv::Mat distortionCoefficients) const override; + void drawObject() const override; + float getSpacing() const override; + ofVec3f getCenter() const; + + ofxCvGui::PanelPtr getPanel() override; + + ofVec2f getPhysicalSize() const; + + vector getNonMirroringIDs() const; + + protected: + float getPreviewPixelsPerMeter() const; + + struct Parameters : ofParameterGroup { + struct : ofParameterGroup { + ofParameter width{ "Width", 5 }; + ofParameter height{ "Height", 5 }; + PARAM_DECLARE("Size [squares]", width, height); + } size; + + struct : ofParameterGroup { + ofParameter square{ "Square", 0.036, 0.001, 0.10 }; + ofParameter outerCorner{ "Outer corner", 0.01, 0, 0.1 }; + PARAM_DECLARE("Length [m]", square, outerCorner); + } length; + + struct : ofParameterGroup { + ofParameter DPI{ "Preview DPI", 75 }; + ofParameter showPaperSizes{ "Show paper sizes", false }; + PARAM_DECLARE("Preview", DPI, showPaperSizes); + } preview; + + struct : ofParameterGroup { + ofParameter refineStrategy{ "Refine strategy", true }; + ofParameter errorCorrectionRate{ "Error correction rate", 0.6 }; + ofParameter openCorners{ "Open corners [px]", 0 }; + PARAM_DECLARE("Detection", refineStrategy, errorCorrectionRate, openCorners); + } detection; + + struct : ofParameterGroup { + ofParameter enabled{ "Enabled", true }; + PARAM_DECLARE("Refinement", enabled); + } refinement; + + PARAM_DECLARE("HaloBoard", size, length, preview, detection, refinement); + }; + + struct Board { + struct Position { + int x; + int y; + + bool operator==(const Position & other) const { + return other.x == this->x && other.y == this->y; + } + }; + + struct Marker { + int id; + Position position; + }; + + struct MarkerCorner { + int markerID; + int cornerIndex; + + bool operator==(const MarkerCorner & other) const { + return other.markerID == this->markerID && other.cornerIndex == this->cornerIndex; + } + }; + + struct BoardCorner { + Position position; + vector markerCorners; + }; + + map markers; + vector corners; + }; + + Parameters parameters; + Parameters cachedParameters; + + aruco::Dictionary dictionary; + shared_ptr detector; + float cachedDPI = 0; + + shared_ptr board; + ofImage preview; + + ofxCvGui::PanelPtr panel; + + set> paperSizes; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.cpp new file mode 100644 index 0000000000..92ff8b64fb --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.cpp @@ -0,0 +1,227 @@ +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { +#pragma mark Heliostats + //---------- + Heliostats::Heliostats() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string Heliostats::getTypeName() const { + return "Halo::Heliostats"; + } + + //---------- + void Heliostats::init() { + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_DRAW_WORLD_LISTENER; + RULR_NODE_SERIALIZATION_LISTENERS; + +// { +// this->panel = make_shared(); +// this->panel->addButton("Add...", [this]() { +// auto nameString = ofSystemTextBoxDialog("Name"); +// if (!nameString.empty()) { +// +// auto heliostat = make_shared(); +// heliostat->name = nameString; +// this->heliostats.add(heliostat); +// } +// })->setHeight(100.0f); +// } + } + + //---------- + void Heliostats::populateInspector(ofxCvGui::InspectArguments & inspectArgs) { + auto inspector = inspectArgs.inspector; + this->heliostats.populateWidgets(inspector); + } + + //---------- + void Heliostats::serialize(Json::Value & json) { + this->heliostats.serialize(json["heliostats"]); + } + + //---------- + void Heliostats::deserialize(const Json::Value & json) { + this->heliostats.deserialize(json["heliostats"]); + } + + //---------- + ofxCvGui::PanelPtr Heliostats::getPanel() { + return this->panel; + } + + //---------- + void Heliostats::drawWorldStage() { + auto selection = this->heliostats.getSelection(); + for (auto heliostat : selection) { + heliostat->drawWorld(); + } + } + + //---------- + std::vector> Heliostats::getHeliostats() { + return this->heliostats.getSelection(); + } + + //---------- + void Heliostats::add(shared_ptr heliostat) { + this->heliostats.add(heliostat); + } + + //---------- + void Heliostats::removeHeliostat(shared_ptr heliostat) { + this->heliostats.remove(heliostat); + } + +#pragma mark Heliostat + //---------- + Heliostats::Heliostat::Heliostat() { + RULR_SERIALIZE_LISTENERS; + } + + //---------- + string Heliostats::Heliostat::getDisplayString() const { + stringstream ss; + ss << "Position : " << this->position << endl; + ss << "Rotation : " << this->rotationY; + return ss.str(); + } + + //---------- + void Heliostats::Heliostat::drawWorld() { + ofPushMatrix(); + { + ofTranslate(this->position); + ofRotate(this->rotationY.get(), 0, 1, 0); + ofDrawAxis(0.1f); + ofDrawBitmapString(this->name, ofVec3f()); + + ofTranslate(0.0f, -0.095f, 0.0f); + ofPushStyle(); + { + ofNoFill(); + ofSetColor(this->color); + + static ofBoxPrimitive * box = 0; + if(!box) { + box = new ofBoxPrimitive(); + box->setWidth(0.22f); + box->setHeight(0.29f); + box->setDepth(0.13f); + box->setResolution(1); + box->setUseVbo(true); + } + box->drawWireframe(); + } + ofPopStyle(); + } + ofPopMatrix(); + + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawWorld(); + } + } + + //---------- + void Heliostats::Heliostat::serialize(Json::Value & json) { + Utils::Serializable::serialize(json, this->name); + Utils::Serializable::serialize(json, this->position); + Utils::Serializable::serialize(json, this->rotationY); + Utils::Serializable::serialize(json, this->axis1Servo); + Utils::Serializable::serialize(json, this->axis2Servo); + this->captures.serialize(json["captures"]); + } + + //---------- + void Heliostats::Heliostat::deserialize(const Json::Value & json) { + Utils::Serializable::deserialize(json, this->name); + Utils::Serializable::deserialize(json, this->position); + Utils::Serializable::deserialize(json, this->rotationY); + Utils::Serializable::deserialize(json, this->axis1Servo); + Utils::Serializable::deserialize(json, this->axis2Servo); + this->captures.deserialize(json["captures"]); + } + + //---------- + void Heliostats::Heliostat::calcPosition(float axis2ToPlaneLength /*= 0.15f*/) { + auto captures = this->captures.getSelection(); + if (!captures.empty()) { + ofVec3f accumulator; + for (auto capture : captures) { + auto backPosition = capture->mirrorPlane.meanWorldPoint + - capture->mirrorPlane.plane.getNormal() + * axis2ToPlaneLength; + + accumulator += backPosition; + } + this->position = accumulator / captures.size(); + } + } + + //---------- + ofxCvGui::ElementPtr Heliostats::Heliostat::getDataDisplay() { + auto element = ofxCvGui::makeElement(); + + auto widget0 = make_shared>(this->name); + { + element->addChild(widget0); + } + + auto widget1 = make_shared>(this->position); + { + element->addChild(widget1); + } + + auto widget2 = make_shared(this->rotationY); + { + element->addChild(widget2); + } + + auto widget3 = make_shared>(this->axis1Servo); + { + element->addChild(widget3); + } + + auto widget4 = make_shared>(this->axis2Servo); + { + element->addChild(widget4); + } + + auto widget5 = make_shared>("Capture count : ", [this]() { + return this->captures.size(); + }); + { + element->addChild(widget5); + } + + element->onBoundsChange += [this, widget0, widget1, widget2, widget3, widget4, widget5](ofxCvGui::BoundsChangeArguments & args) { + auto bounds = args.localBounds; + bounds.height = 40.0f; + widget0->setBounds(bounds); + bounds.y += bounds.height; + widget1->setBounds(bounds); + bounds.y += bounds.height; + widget2->setBounds(bounds); + bounds.y += bounds.height; + widget3->setBounds(bounds); + bounds.y += bounds.height; + widget4->setBounds(bounds); + bounds.y += bounds.height; + widget5->setBounds(bounds); + }; + + element->setHeight(240.0f); + + return element; + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.h new file mode 100644 index 0000000000..28cf92c2b3 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.h @@ -0,0 +1,56 @@ +#pragma once + +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + class Heliostats : public Nodes::Base { + public: + class Heliostat : public Utils::AbstractCaptureSet::BaseCapture { + public: + Heliostat(); + string getDisplayString() const override; + void drawWorld(); + + ofParameter name{ "Name", "" }; + ofParameter position{ "Position", ofVec3f() }; + ofParameter rotationY { "Rotation Y", 0.0f, -90, 90 }; + + ofParameter axis1Servo{ "Axis 1 Servo", 0 }; + ofParameter axis2Servo{ "Axis 2 Servo", 0 }; + + void serialize(Json::Value &); + void deserialize(const Json::Value &); + + Utils::CaptureSet captures; + void calcPosition(float axis2ToPlaneLength = 0.15f); + + protected: + ofxCvGui::ElementPtr getDataDisplay() override; + }; + + Heliostats(); + string getTypeName() const override; + + void init(); + + void populateInspector(ofxCvGui::InspectArguments &); + void serialize(Json::Value &); + void deserialize(const Json::Value &); + + ofxCvGui::PanelPtr getPanel() override; + void drawWorldStage(); + + vector> getHeliostats(); + void add(shared_ptr); + void removeHeliostat(shared_ptr heliostat); + protected: + Utils::CaptureSet heliostats; + shared_ptr panel; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.cpp new file mode 100644 index 0000000000..4f2fd61b12 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.cpp @@ -0,0 +1,440 @@ +#include "pch_Plugin_Experiments.h" + +float getMarkerReprojectionError2(vector imagePoints, cv::Mat cameraMatrix, cv::Mat distortionCoefficients) { + cv::Mat translation, rotationVector; + + vector dummyObjectPoints; + { + dummyObjectPoints.emplace_back(0, 0, 0); + dummyObjectPoints.emplace_back(1, 0, 0); + dummyObjectPoints.emplace_back(1, 1, 0); + dummyObjectPoints.emplace_back(0, 1, 0); + } + + cv::solvePnP(dummyObjectPoints + , imagePoints + , cameraMatrix + , distortionCoefficients + , rotationVector + , translation); + + vector reprojectedPoints; + cv::projectPoints(dummyObjectPoints + , rotationVector + , translation + , cameraMatrix + , distortionCoefficients + , reprojectedPoints); + + //get RMS + float accumulate = 0.0f; + for (int i = 0; i < imagePoints.size(); i++) { + auto delta = reprojectedPoints[i] - imagePoints[i]; + accumulate += delta.dot(delta); + } + auto MS = accumulate / imagePoints.size(); + return sqrt(MS); +} + +//function to test reprojection error for a set of points given a translation, rotation +float getReprojectionError2 (shared_ptr camera, const vector & objectPoints + , const vector & imagePoints + , const cv::Mat & rotationVector + , const cv::Mat & translation) { + + float error = 0.0f; + vector reprojectedPoints; + cv::projectPoints(objectPoints + , rotationVector + , translation + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , reprojectedPoints); + + //Take square mean + for (size_t i = 0; i < imagePoints.size(); i++) { + auto delta = imagePoints[i] - reprojectedPoints[i]; + error += delta.x * delta.x + delta.y * delta.y; + } + //RMS + error /= (float)imagePoints.size(); + error = sqrt(error); + + return error; +}; + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { +#pragma mark Captures + //---------- + SolarAlignment::SolarAlignment() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + std::string SolarAlignment::getTypeName() const { + return "HALO::SolarAlignment"; + } + + //---------- + void SolarAlignment::init() { + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_SERIALIZATION_LISTENERS; + RULR_NODE_DRAW_WORLD_LISTENER; + + { + auto panel = ofxCvGui::Panels::makeImage(this->preview); + panel->onDrawImage += [this](ofxCvGui::DrawImageArguments & args) { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->draw(); + } + }; + this->panel = panel; + } + + this->addInput(); + this->addInput(); + + this->manageParameters(this->parameters); + } + + //---------- + void SolarAlignment::populateInspector(ofxCvGui::InspectArguments & inspectArgs) { + auto inspector = inspectArgs.inspector; + this->captures.populateWidgets(inspector); + inspector->addButton("Add capture", [this]() { + try { + Utils::ScopedProcess scopedProcess("Add capture"); + this->addCapture(); + scopedProcess.end(); + } + RULR_CATCH_ALL_TO_ALERT; + }, ' '); + inspector->addButton("Calibrate", [this]() { + try { + Utils::ScopedProcess scopedProcess("Calibrate"); + this->calibrate(); + scopedProcess.end(); + } + RULR_CATCH_ALL_TO_ALERT; + }, OF_KEY_RETURN)->setHeight(100.0f); + } + + //---------- + void SolarAlignment::serialize(Json::Value & json) { + this->captures.serialize(json["captures"]); + } + + //---------- + void SolarAlignment::deserialize(const Json::Value & json) { + this->captures.deserialize(json["captures"]); + } + + //---------- + ofxCvGui::PanelPtr SolarAlignment::getPanel() { + return this->panel; + } + + //---------- + void SolarAlignment::drawWorldStage() { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawWorld(); + } + } + + //---------- + void SolarAlignment::addCapture() { + this->throwIfMissingAnyConnection(); + auto camera = this->getInput(); + + auto capture = make_shared(); + + //navigate the camera + { + Utils::ScopedProcess scopedProcess("Marker map navigation image", false); + auto markerMap = this->getInput(); + markerMap->throwIfMissingAConnection(); + auto detector = markerMap->getInput(); + + auto frame = camera->getFreshFrame(); + cv::Mat image = ofxCv::toCv(frame->getPixels()); + + auto markers = detector->findMarkers(image); + if (markers.size() < this->parameters.cameraNavigation.minimumMarkers) { + throw(ofxRulr::Exception("Found " + ofToString(markers.size()) + " real markers. Need " + ofToString(this->parameters.cameraNavigation.minimumMarkers) + " for camera navigation")); + } + + //build up dataset + vector worldPoints; + vector imagePoints; + { + int countGoodMarkers = 0; + for (const auto & marker : markers) { + try { + //test reprojection error + auto markerReprojectionError = getMarkerReprojectionError2((vector &) marker + , camera->getCameraMatrix() + , camera->getDistortionCoefficients()); + + if (markerReprojectionError <= this->parameters.cameraNavigation.maxMarkerError) { + countGoodMarkers++; + auto marker3D = markerMap->getMarkerMap()->getMarker3DInfo(marker.id); + for (int i = 0; i < 4; i++) { + worldPoints.push_back(marker3D.points[i]); + imagePoints.push_back(marker[i]); + } + } + } + RULR_CATCH_ALL_TO_ERROR; //in the case that the marker did not exist + } + + if (countGoodMarkers < this->parameters.cameraNavigation.minimumMarkers) { + throw(ofxRulr::Exception("Found " + ofToString(countGoodMarkers) + " good markers in map. Need " + ofToString(this->parameters.cameraNavigation.minimumMarkers) + " for camera navigation")); + } + } + + cv::Mat rotationVector, translation; + camera->getExtrinsics(rotationVector, translation, true); + + //Solve camera extrinsics + { + //first pass EPNP + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true + , 100 + , 5.0f + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_EPNP); + + //second pass ITERATIVE + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true + , 100 + , 5.0f + , 0.99 + , cv::noArray() + , cv::SOLVEPNP_ITERATIVE); + } + + camera->setExtrinsics(rotationVector, translation, true); + + capture->cameraNavigation.imagePoints = imagePoints; + capture->cameraNavigation.worldPoints = worldPoints; + capture->cameraNavigation.reprojectionError = getReprojectionError2(camera + , worldPoints + , imagePoints + , rotationVector + , translation); + capture->cameraNavigation.cameraPosition = camera->getPosition(); + } + + //get the solar vector + { + Utils::ScopedProcess scopedProcess("Solar centroid image", false); + auto frame = camera->getFreshFrame(); + cv::Mat image = ofxCv::toCv(frame->getPixels()); + + if (image.channels() == 3) { + cv::cvtColor(image, image, CV_RGB2GRAY); + } + + cv::Mat binary; + + cv::threshold(image + , binary + , this->parameters.solarCentroid.threshold + , 255 + , CV_THRESH_BINARY); + + vector> contours; + vector centroids; + vector boundingRects; + vector moments; + + cv::findContours(binary + , contours + , CV_RETR_EXTERNAL + , CV_CHAIN_APPROX_NONE); + + if (contours.size() < 1) { + throw(ofxRulr::Exception("Found 0 contours. Need 1")); + } + + vector bestContour; + float bestArea = 0; + for (const auto & contour : contours) { + auto bounds = cv::boundingRect(contour); + auto area = bounds.area(); + if (area > bestArea) { + bestContour = contour; + bestArea = area; + } + } + + cv::Rect2i dilatedRect = cv::boundingRect(bestContour); + { + dilatedRect.x -= 5; + dilatedRect.y -= 5; + dilatedRect.width += 5; + dilatedRect.height += 5; + } + + auto moment = cv::moments(image(dilatedRect)); + capture->centroid = ofVec2f(moment.m10 / moment.m00 + dilatedRect.x + , moment.m01 / moment.m00 + dilatedRect.y); + + auto cameraView = camera->getViewInWorldSpace(); + + capture->ray = cameraView.castPixel(capture->centroid); + + cv::Mat preview; + image.copyTo(preview, binary); + ofxCv::copy(preview, this->preview); + this->preview.update(); + } + + //get the timestamp of the file + { + auto result = ofSystemTextBoxDialog("Epoch timestamp for capture"); + if (result.empty()) { + return; + } + + time_t epochTime = ofToInt(result); + capture->timestamp.set(chrono::system_clock::from_time_t(epochTime)); + capture->rebuildDateStrings(); + } + + //get the solar tracking for this time + { + auto result = ofSystemTextBoxDialog("Azimuth, Altitude from sunPosition.py"); + if (result.empty()) { + return; + } + + stringstream ss(result); + ss >> capture->azimuthAltitude; + } + + //calculate the solar vector in global frame + { + auto azRotate = ofMatrix4x4::newRotationMatrix(capture->azimuthAltitude.x, 0, 1, 0); + auto elRotate = ofMatrix4x4::newRotationMatrix(capture->azimuthAltitude.y, +1, 0, 0); + capture->solarVectorFromPySolar = azRotate * elRotate * ofVec3f(0, 0, -1); + } + + this->captures.add(capture); + } + + //---------- + void SolarAlignment::calibrate() { + + } + +#pragma mark Capture + //---------- + SolarAlignment::Capture::Capture() { + RULR_SERIALIZE_LISTENERS; + } + + //---------- + std::string SolarAlignment::Capture::getDisplayString() const { + stringstream ss; + ss << "Solar vector : " << this->ray.t << endl; + ss << "Azi/Alt : " << this->azimuthAltitude << endl; + return ss.str(); + } + + //---------- + void SolarAlignment::Capture::drawWorld() { + this->ray.color = this->color; + this->ray.draw(); + + ofPushStyle(); + { + ofPushMatrix(); + { + ofSetColor(this->color); + ofRotate(this->azimuthAltitude.x, 0, +1, 0); + ofRotate(this->azimuthAltitude.y, +1, 0, 0); + ofDrawLine(ofVec3f(), ofVec3f(0, 0, -10)); + } + ofPopMatrix(); + + ofPushMatrix(); + { + ofTranslate(0.1, 0.0, 0.0); + ofDrawLine(ofVec3f(), this->solarVectorFromPySolar); + } + ofPopMatrix(); + } + ofPopStyle(); + } + + //---------- + void SolarAlignment::Capture::serialize(Json::Value & json) { + json["ray_s"] << this->ray.s; + json["ray_t"] << this->ray.t; + json["centroid"] << this->centroid; + json["azimuthAltitude"] << this->azimuthAltitude; + + { + auto & jsonCameraNavigation = json["cameraNavigation"]; + jsonCameraNavigation["imagePoints"] << ofxCv::toOf(this->cameraNavigation.imagePoints); + jsonCameraNavigation["worldPoints"] << ofxCv::toOf(this->cameraNavigation.worldPoints); + jsonCameraNavigation["cameraPosition"] << this->cameraNavigation.cameraPosition; + Utils::Serializable::serialize(jsonCameraNavigation, this->cameraNavigation.reprojectionError); + } + } + + //---------- + void SolarAlignment::Capture::deserialize(const Json::Value & json) { + json["ray_s"] >> this->ray.s; + json["ray_t"] >> this->ray.t; + this->ray.defined = true; + json["centroid"] >> this->centroid; + json["azimuthAltitude"] >> this->azimuthAltitude; + + { + const auto & jsonCameraNavigation = json["cameraNavigation"]; + jsonCameraNavigation["imagePoints"] >> ofxCv::toOf(this->cameraNavigation.imagePoints); + jsonCameraNavigation["worldPoints"] >> ofxCv::toOf(this->cameraNavigation.worldPoints); + jsonCameraNavigation["cameraPosition"] >> this->cameraNavigation.cameraPosition; + Utils::Serializable::deserialize(jsonCameraNavigation, this->cameraNavigation.reprojectionError); + } + } + + //---------- + void SolarAlignment::Capture::draw() { + ofPushStyle(); + { + ofSetColor(this->color); + ofPushMatrix(); + { + ofTranslate(this->centroid); + ofDrawLine(-10, 0, 10, 0); + ofDrawLine(0, -10, 0, 10); + } + ofPopMatrix(); + } + ofPopStyle(); + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.h new file mode 100644 index 0000000000..5dd52d8975 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.h @@ -0,0 +1,78 @@ +#pragma once + +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace MirrorPlaneCapture { + class SolarAlignment : public Nodes::Base { + public: + class Capture : public Utils::AbstractCaptureSet::BaseCapture { + public: + Capture(); + string getDisplayString() const override; + void drawWorld(); + + ofVec2f centroid; + ofxRay::Ray ray; + ofVec2f azimuthAltitude; + ofVec3f solarVectorFromPySolar; + + void serialize(Json::Value &); + void deserialize(const Json::Value &); + + void draw(); + + struct { + vector imagePoints; + vector worldPoints; + ofParameter reprojectionError{ "Marker map reprojection error", 0.0f }; + ofParameter cameraPosition{ "Camera position", ofVec3f() }; + } cameraNavigation; + }; + + SolarAlignment(); + string getTypeName() const override; + + void init(); + + void populateInspector(ofxCvGui::InspectArguments &); + void serialize(Json::Value &); + void deserialize(const Json::Value &); + + ofxCvGui::PanelPtr getPanel() override; + void drawWorldStage(); + + void addCapture(); + void calibrate(); + protected: + struct : ofParameterGroup { + struct : ofParameterGroup { + ofParameter minimumMarkers{ "Minimum markers", 3 }; + ofParameter maxMarkerError{ "Max marker error", 0.5f }; + PARAM_DECLARE("Camera navigation", minimumMarkers, maxMarkerError); + } cameraNavigation; + + struct : ofParameterGroup { + ofParameter threshold{ "Threshold", 100, 0, 255 }; + PARAM_DECLARE("Solar centroid", threshold); + } solarCentroid; + + struct : ofParameterGroup { + ofParameter sunTrackingScript{ "sunPosition.pyv", "C:\\Users\\kimchips\\Dropbox (Kimchi and Chips)\\KC31 - Halo v2.0 Technical\\Sun Tracking\\sunPosition.py" }; + ofParameter python{ "python exe", "C:\\Users\\kimchips\\Anaconda3\\envs\\KC31\\python.exe" }; + PARAM_DECLARE("Solar tracking", sunTrackingScript, python); + } solarTracking; + + PARAM_DECLARE("SolarAlignment", cameraNavigation, solarCentroid, solarTracking); + } parameters; + + Utils::CaptureSet captures; + shared_ptr panel; + ofImage preview; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/planesToPoint.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/planesToPoint.cpp new file mode 100644 index 0000000000..f20a5657fa --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/planesToPoint.cpp @@ -0,0 +1,38 @@ +#include "pch_Plugin_Experiments.h" +#include "planesToPoint.h" + +class Model : public ofxNonLinearFit::Models::Base { +public: + unsigned int getParameterCount() const { + return 3; + } + + void getResidual(ofxRay::Plane plane, double & residual, double * gradient = 0) const { + if (gradient) { + throw(ofxRulr::Exception("not implemented")); + } + + residual = plane.getDistanceTo(this->point); + }; + + void evaluate(ofxRay::Plane &) const { + //nothing to do + } + + void cacheModel() { + this->point.x = this->parameters[0]; + this->point.y = this->parameters[1]; + this->point.z = this->parameters[2]; + } + + ofVec3f point; +}; + +ofVec3f planesToPoint(const vector & planes) { + ofxNonLinearFit::Fit fit; + + Model model; + double residual; + fit.optimise(model, &planes, &residual); + return model.point; +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/planesToPoint.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/planesToPoint.h new file mode 100644 index 0000000000..adc375e2e2 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/MirrorPlaneCapture/planesToPoint.h @@ -0,0 +1,5 @@ +#pragma once + +#include "pch_Plugin_Experiments.h" + +ofVec3f planesToPoint(const vector & planes); \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.cpp new file mode 100644 index 0000000000..e30f6f64ba --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.cpp @@ -0,0 +1,196 @@ +#include "pch_Plugin_Experiments.h" +#include "BundlerCamera.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace PhotoScan { + //---------- + void BundlerCamera::Capture::drawWorld() { + this->preview.drawVertices(); + } + + //---------- + string BundlerCamera::Capture::getDisplayString() const { + stringstream ss; + ss << "Tie points : " << this->worldPoints.size(); + return ss.str(); + } + + //---------- + BundlerCamera::BundlerCamera() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string BundlerCamera::getTypeName() const { + return "Experiments::PhotoScan::BundlerCamera"; + } + + //---------- + void BundlerCamera::init() { + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_DRAW_WORLD_LISTENER; + + this->addInput(); + + this->panel = ofxCvGui::Panels::makeImage(this->previewTiePoints); + + this->manageParameters(this->parameters); + } + + //---------- + void BundlerCamera::addCapture() { + + } + + //---------- + void BundlerCamera::calibrate() { + this->throwIfMissingAnyConnection(); + auto camera = this->getInput(); + + auto captures = this->captures.getSelection(); + if (captures.size() != 1) { + throw(ofxRulr::Exception("Please select exactly one capture when calibrating camera")); + } + + auto capture = captures[0]; + + vector> worldPointsSets(1); + vector> imagePointsSets(1); + { + const auto cameraWidth = camera->getWidth(); + const auto cameraHeight = camera->getHeight(); + + //clear preview + this->previewTiePoints.allocate(cameraWidth, cameraHeight, OF_IMAGE_COLOR_ALPHA); + this->previewTiePoints.getPixels().set(0); + auto previewTiePointsPixels = this->previewTiePoints.getPixels().getData(); + + for (int i = 0; i < capture->imagePoints.size(); i+= this->parameters.calibrate.decimator) { + const auto & rawWorldPoint = capture->worldPoints[i]; + const auto & rawImagePoint = capture->imagePoints[i]; + + auto imagePoint = cv::Point2f(cameraWidth / 2.0f + rawImagePoint.x + , cameraHeight / 2.0f - rawImagePoint.y + ); + + worldPointsSets[0].push_back(rawWorldPoint); + imagePointsSets[0].push_back(imagePoint); + + //colour the preview pixel + int pixelIndex = imagePoint.x + imagePoint.y * cameraWidth; + previewTiePointsPixels[pixelIndex] = 255; + } + + this->previewTiePoints.update(); + } + + cv::Mat cameraMatrix = camera->getCameraMatrix(); + cv::Mat distortionCoefficients = camera->getDistortionCoefficients(); + vector rotationVectors, translations; + + this->reprojectionError = cv::calibrateCamera(worldPointsSets + , imagePointsSets + , camera->getSize() + , cameraMatrix + , distortionCoefficients + , rotationVectors + , translations + , CV_CALIB_USE_INTRINSIC_GUESS | CV_CALIB_FIX_K5 | CV_CALIB_FIX_K6); + + camera->setIntrinsics(cameraMatrix, distortionCoefficients); + camera->setExtrinsics(rotationVectors[0] + , translations[0] + , true); + } + + //---------- + ofxCvGui::PanelPtr BundlerCamera::getPanel() { + return this->panel; + } + + //---------- + void BundlerCamera::populateInspector(ofxCvGui::InspectArguments & args) { + auto inspector = args.inspector; + + inspector->addButton("Import bundler...", [this]() { + try { + Utils::ScopedProcess scopedProcess("Loading bundler file"); + + auto fileResult = ofSystemLoadDialog("Select bundler_formatted.json file"); + if (!fileResult.bSuccess) { + return; + } + + this->importBundler(fileResult.filePath); + scopedProcess.end(); + } + RULR_CATCH_ALL_TO_ALERT; + }); + + this->captures.populateWidgets(inspector); + + inspector->addButton("Calibrate", [this]() { + try { + this->calibrate(); + } + RULR_CATCH_ALL_TO_ALERT; + }, OF_KEY_RETURN)->setHeight(100.0f); + + inspector->addLiveValue(this->reprojectionError); + } + + //---------- + void BundlerCamera::drawWorldStage() { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawWorld(); + } + } + + //---------- + void BundlerCamera::importBundler(const string & filePath) { + auto fileContents = ofFile(filePath).readToBuffer(); + Json::Value json; + Json::Reader().parse((const string &)fileContents, json); + + for (const auto & jsonView : json) { + auto capture = make_shared(); + capture->f = jsonView["f"].asFloat(); + capture->k1 = jsonView["k1"].asFloat(); + capture->k2 = jsonView["k2"].asFloat(); + + const auto & jsonTiePoints = jsonView["tiePoints"]; + for (const auto & jsonTiePoint : jsonTiePoints) { + ofVec3f worldPoint; + for (int i = 0; i < 3; i++) { + worldPoint[i] = jsonTiePoint["worldSpace"][i].asFloat(); + } + + ofVec2f imagePoint; + for (int i = 0; i < 2; i++) { + imagePoint[i] = jsonTiePoint["imageSpace"][i].asFloat(); + } + + ofFloatColor color(255, 255, 255, 255); + for (int i = 0; i < 3; i++) { + color[i] = jsonTiePoint["color"][i].asFloat(); + } + color /= 255; + + capture->worldPoints.push_back(ofxCv::toCv(worldPoint)); + capture->imagePoints.push_back(ofxCv::toCv(imagePoint)); + capture->tiePointColors.push_back(color); + } + + capture->preview.addVertices(ofxCv::toOf(capture->worldPoints)); + capture->preview.addColors(capture->tiePointColors); + + this->captures.add(capture); + } + } + } + } + } +} diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.h new file mode 100644 index 0000000000..d2ad04fc91 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.h @@ -0,0 +1,60 @@ +#pragma once + +#include "ofxRulr.h" +#include "ofxNonLinearFit.h" +#include "ofxRulr/Utils/CaptureSet.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace PhotoScan { + class BundlerCamera : public Base { + public: + class Capture : public Utils::AbstractCaptureSet::BaseCapture { + public: + void drawWorld(); + string getDisplayString() const override; + + ofParameter f{ "f", 0.0f }; + ofParameter k1{ "k1", 0.0f }; + ofParameter k2{ "k2", 0.0f }; + + vector worldPoints; + vector imagePoints; + vector tiePointColors; + + ofMesh preview; + }; + + BundlerCamera(); + string getTypeName() const override; + void init(); + void addCapture(); + void calibrate(); + ofxCvGui::PanelPtr getPanel() override; + void populateInspector(ofxCvGui::InspectArguments &); + void drawWorldStage(); + + void importBundler(const string & filePath); + protected: + struct : ofParameterGroup { + struct : ofParameterGroup { + ofParameter decimator{ "Decimator", 1, 1, 256 }; + PARAM_DECLARE("Calibrate", decimator); + } calibrate; + + PARAM_DECLARE("BundlerCamera", calibrate); + } parameters; + + ofxCvGui::PanelPtr panel; + + Utils::CaptureSet captures; + + ofParameter reprojectionError{ "Reprojection error [px]", 0, 0, 100.0 }; + + ofImage previewTiePoints; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.cpp new file mode 100644 index 0000000000..9a0aedff25 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.cpp @@ -0,0 +1,350 @@ +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolveMirror { + //---------- + SolveMirror::SolveMirror() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string SolveMirror::getTypeName() const { + return "Experiments::SolveMirror::SolveMirror"; + } + + //---------- + void SolveMirror::init() { + RULR_NODE_DRAW_WORLD_LISTENER; + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_UPDATE_LISTENER; + + this->addInput(); + this->addInput(); + this->addInput(); + + this->panel = make_shared(); + this->captures.populateWidgets(this->panel); + } + + //---------- + void SolveMirror::update() { + + } + + //---------- + void SolveMirror::addCapture() { + this->throwIfMissingAnyConnection(); + auto camera = this->getInput(); + auto grabber = camera->getGrabber(); + if (!grabber) { + throw(ofxRulr::Exception("Camera grabber not available")); + } + auto frame = grabber->getFreshFrame(); + auto image = ofxCv::toCv(frame->getPixels()); + auto detector = this->getInput(); + auto markerMap = this->getInput()->getMarkerMap(); + + auto capture = make_shared(); + + auto getReprojectionError = [& camera](const vector & objectPoints + , const vector & imagePoints + , const cv::Mat & rotationVector + , const cv::Mat & translation) { + + float error = 0.0f; + vector reprojectedPoints; + cv::projectPoints(objectPoints + , rotationVector + , translation + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , reprojectedPoints); + + for (size_t i = 0; i < imagePoints.size(); i++) { + auto delta = imagePoints[i] - reprojectedPoints[i]; + error += delta.x * delta.x + delta.y * delta.y; + } + error /= (float)imagePoints.size(); + error = sqrt(error); + return error; + }; + + //resolve pose of camera + { + auto markers = detector->getMarkerDetector().detect(image); + if (markers.size() < this->parameters.cameraNavigation.minimumMarkers) { + throw(ofxRulr::Exception("Found " + ofToString(markers.size()) + " real markers. Need " + ofToString(this->parameters.cameraNavigation.minimumMarkers) + " for camera navigation")); + } + + //build up dataset + vector worldPoints; + vector imagePoints; + { + for (const auto & marker : markers) { + try { + auto marker3D = markerMap->getMarker3DInfo(marker.id); + for (int i = 0; i < 4; i++) { + worldPoints.push_back(marker3D.points[i]); + imagePoints.push_back(marker[i]); + } + } + RULR_CATCH_ALL_TO_ERROR; //in the case that the marker did not exist + } + } + + cv::Mat rotationVector, translation; + camera->getExtrinsics(rotationVector, translation, true); + + cv::solvePnPRansac(worldPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotationVector + , translation + , true); + + camera->setExtrinsics(rotationVector, translation, true); + + capture->cameraNavigation.imagePoints = imagePoints; + capture->cameraNavigation.worldPoints = worldPoints; + capture->cameraNavigation.reprojectionError = getReprojectionError(worldPoints + , imagePoints + , rotationVector + , translation); + cout << "capture->cameraNavigation.reprojectionError " << capture->cameraNavigation.reprojectionError << endl; + } + + //get the image space points of mirrored markers + vector mirroredMarkers; + { + //flip the image + cv::Mat imageFlipped; + cv::flip(image, imageFlipped, 0); + + //detect the markers + mirroredMarkers = detector->getMarkerDetector().detect(imageFlipped); + + if (mirroredMarkers.empty()) { + throw(ofxRulr::Exception("No mirrored markers found")); + } + + //flip the markers back + for (auto & marker : mirroredMarkers) { + for (int i = 0; i < 4; i++) { + marker[i].y = grabber->getHeight() - marker[i].y; //not -1 again, because we're subpixels + } + } + } + + //get the object points in mirrored space + vector flippedMarkerMapWorldSpacePoints; + vector reflectionImageSpacePoints; + { + for (const auto & marker : mirroredMarkers) { + try { + auto markerInfo = markerMap->getMarker3DInfo(marker.id); + + for (const auto & point : markerInfo.points) { + capture->reflections.realPoints.push_back(point); + auto pointInverted = point; + pointInverted.y *= -1.0f; + flippedMarkerMapWorldSpacePoints.push_back(pointInverted); + } + + reflectionImageSpacePoints.push_back(marker[0]); + reflectionImageSpacePoints.push_back(marker[1]); + reflectionImageSpacePoints.push_back(marker[2]); + reflectionImageSpacePoints.push_back(marker[3]); + } + catch (...) { + ofLogNotice("SolveMirror") << "Couldn't find Marker #" << marker.id << "In the marker map. Ignoring."; + } + } + } + + //resolve pose of inverted marker map relative to camera + ofMatrix4x4 flippedMarkerMapTransform; + { + cv::Mat rotation, translation; + cv::solvePnP(flippedMarkerMapWorldSpacePoints + , reflectionImageSpacePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , rotation + , translation); + + capture->reflections.reprojectionError = getReprojectionError(flippedMarkerMapWorldSpacePoints + , reflectionImageSpacePoints + , rotation + , translation); + cout << "capture->reflections.reprojectionError " << capture->reflections.reprojectionError << endl; + + flippedMarkerMapTransform = ofxCv::makeMatrix(rotation, translation) * camera->getTransform(); + } + + //resolve 3d points as seen by camera through mirror + { + for (const auto & flippedWorldSpacePoint : flippedMarkerMapWorldSpacePoints) { + capture->reflections.virtualPoints.push_back(ofxCv::toCv(ofxCv::toOf(flippedWorldSpacePoint) * flippedMarkerMapTransform)); + } + } + + //make view rays + { + for (const auto & virtualPoint : capture->reflections.virtualPoints) { + ofxRay::Ray ray; + ray.infinite = false; + ray.defined = true; + ray.s = camera->getPosition(); + ray.setEnd(ofxCv::toOf(virtualPoint)); + ray.color = capture->color; + capture->reflections.viewRays.push_back(ray); + } + } + + this->captures.add(capture); + } + + //---------- + void SolveMirror::calibrate() { + vector dataPoints; + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + for (int i = 0; i < capture->reflections.realPoints.size(); i++) { + dataPoints.push_back(PlaneDataPoint { + ofxCv::toOf(capture->reflections.realPoints[i]) + , ofxCv::toOf(capture->reflections.virtualPoints[i]) + }); + } + } + + ofxNonLinearFit::Fit fit; + double fitResidual; + fit.optimise(this->mirrorPlaneModel, &dataPoints, & fitResidual); + this->planeFitResidual = sqrt(fitResidual); + + auto & plane = this->mirrorPlaneModel.plane; + plane.setInfinite(true); + + //calculate plane intersection points + ofVec3f meanPlaneIntersection; + { + size_t intersectionCount = 0; + + for (auto capture : captures) { + capture->reflections.pointOnMirror.clear(); + + for (auto & viewRay : capture->reflections.viewRays) { + ofVec3f position; + plane.intersect(viewRay, position); + capture->reflections.pointOnMirror.push_back(ofxCv::toCv(position)); + viewRay.setEnd(position); + meanPlaneIntersection += position; + intersectionCount++; + } + } + + meanPlaneIntersection /= intersectionCount; + plane.setCenter(meanPlaneIntersection); + plane.setScale(ofVec2f(0.35f, 0.35f)); + plane.setInfinite(false); + } + + } + + //---------- + ofxCvGui::PanelPtr SolveMirror::getPanel() { + return this->panel; + } + + //---------- + void SolveMirror::populateInspector(ofxCvGui::InspectArguments & inspectArgs) { + auto inspector = inspectArgs.inspector; + inspector->addButton("Add capture", [this]() { + try { + this->addCapture(); + } + RULR_CATCH_ALL_TO_ALERT; + }, ' ')->setHeight(100.0f); + inspector->addButton("Calibrate", [this]() { + try { + this->calibrate(); + } + RULR_CATCH_ALL_TO_ALERT; + }, OF_KEY_RETURN)->setHeight(100.0f); + inspector->addLiveValue(this->planeFitResidual); + } + + //---------- + void SolveMirror::drawWorldStage() { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawWorld(); + } + + this->mirrorPlaneModel.plane.draw(); + } + +#pragma mark Capture + //---------- + void SolveMirror::Capture::drawWorld() { + ofPushStyle(); + { + ofSetColor(this->color); + for (size_t i = 0; i < this->reflections.realPoints.size(); i++) { + ofDrawLine(ofxCv::toOf(this->reflections.realPoints[i]) + , ofxCv::toOf(this->reflections.virtualPoints[i])); + } + } + ofPopStyle(); + + for (const auto & viewRay : this->reflections.viewRays) { + viewRay.draw(); + } + } + + //---------- + std::string SolveMirror::Capture::getDisplayString() const { + return "markers"; + } + +#pragma mark PlaneModel + //---------- + unsigned int SolveMirror::PlaneModel::getParameterCount() const { + return 3; + } + + //---------- + void SolveMirror::PlaneModel::getResidual(PlaneDataPoint dataPoint, double & residual, double * gradient /* = 0 */) const { + if (gradient) { + throw(ofxRulr::Exception("Gradient not supported for this model")); + } + + auto evaluatedDataPoint = dataPoint; + this->evaluate(evaluatedDataPoint); + residual = evaluatedDataPoint.virtualPosition.squareDistance(dataPoint.virtualPosition); + } + + //---------- + void SolveMirror::PlaneModel::evaluate(PlaneDataPoint & dataPoint) const { + dataPoint.virtualPosition = this->plane.reflect(dataPoint.realPosition); + } + + //---------- + void SolveMirror::PlaneModel::cacheModel() { + ofVec3f normal(0, 0, 1); + ofMatrix4x4 transform; + transform.rotate(this->parameters[0], 1, 0, 0); + transform.rotate(this->parameters[1], 0, 1, 0); + + this->plane.setUp(ofVec3f(0, 1, 0) * transform); + this->plane.setScale(ofVec2f(1.0f, 1.0f)); + this->plane.setNormal(normal * transform); + this->plane.setCenter(normal * this->parameters[2]); + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.h new file mode 100644 index 0000000000..79007a5d25 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.h @@ -0,0 +1,78 @@ +#pragma once + +#include "ofxRulr.h" +#include "ofxNonLinearFit.h" +#include "ofxRulr/Utils/CaptureSet.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolveMirror { + class SolveMirror : public Base { + public: + class Capture : public Utils::AbstractCaptureSet::BaseCapture { + public: + void drawWorld(); + string getDisplayString() const override; + + struct { + vector imagePoints; + vector worldPoints; + ofParameter reprojectionError; + } cameraNavigation; + + struct { + vector realPoints; + vector virtualPoints; + vector pointOnMirror; + ofParameter reprojectionError; + + vector viewRays; + } reflections; + }; + + struct PlaneDataPoint { + ofVec3f realPosition; + ofVec3f virtualPosition; + }; + + class PlaneModel : public ofxNonLinearFit::Models::Base { + public: + unsigned int getParameterCount() const override; + void getResidual(PlaneDataPoint, double & residual, double * gradient /* = 0 */) const override; + void evaluate(PlaneDataPoint &) const override; + void cacheModel() override; + + ofxRay::Plane plane; + }; + + SolveMirror(); + string getTypeName() const override; + void init(); + void update(); + void addCapture(); + void calibrate(); + ofxCvGui::PanelPtr getPanel() override; + void populateInspector(ofxCvGui::InspectArguments &); + void drawWorldStage(); + protected: + struct : ofParameterGroup { + struct : ofParameterGroup { + ofParameter minimumMarkers { "Minimum markers", 3 }; + PARAM_DECLARE("Camera navigation", minimumMarkers); + } cameraNavigation; + + ofParameter noise{ "Noise [px]", 0, 0, 100.0 }; + PARAM_DECLARE("SolveMirror", cameraNavigation); + } parameters; + shared_ptr panel; + + Utils::CaptureSet captures; + + PlaneModel mirrorPlaneModel; + ofParameter planeFitResidual{ "Plane fit residual [m]", 0.0f }; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.cpp new file mode 100644 index 0000000000..1c321fa16d --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.cpp @@ -0,0 +1,125 @@ +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolvePnP { + //---------- + ProjectPoints::ProjectPoints() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string ProjectPoints::getTypeName() const { + return "Experiments::SolvePnP::ProjectPoints"; + } + + //---------- + void ProjectPoints::init() { + RULR_NODE_UPDATE_LISTENER; + + this->addInput(); + this->addInput(); + this->manageParameters(this->parameters); + + this->panel = make_shared(this->fbo); + panel->onDrawImage += [this](ofxCvGui::DrawImageArguments &) { + for (const auto & projectedPoint : this->projectedPoints) { + ofPushMatrix(); + { + ofTranslate(projectedPoint); + + ofPushStyle(); + { + //draw circle + ofNoFill(); + ofSetColor(255, 0, 0); + ofDrawCircle(ofVec2f(), 5.0f); + + //draw cross + ofRotate(45.0f); + ofDrawLine(-5, 0, 5, 0); + ofDrawLine(0, -5, 0, 5); + } + ofPopStyle(); + } + ofPopMatrix(); + } + }; + + panel->onDraw += [this](ofxCvGui::DrawArguments & args) { + auto camera = this->getInput(); + if (camera) { + if (!camera->getTransform().isIdentity()) { + ofxCvGui::Utils::drawText("Camera must have zero transform" + , args.localBounds + , true); + } + } + }; + } + + //---------- + void ProjectPoints::update() { + auto testObject = this->getInput(); + auto camera = this->getInput(); + if (camera && testObject) { + //allocate the fbo if unavailable + if (this->fbo.getWidth() != camera->getWidth() + || this->fbo.getHeight() != camera->getHeight()) { + this->fbo.allocate(camera->getWidth(), camera->getHeight(), GL_RGBA); + } + + auto viewCamera = camera->getViewInWorldSpace(); + + //draw into the fbo + this->fbo.begin(); + { + ofClear(0, 0); + viewCamera.beginAsCamera(); + { + testObject->drawWorldStage(); + } + viewCamera.endAsCamera(); + } + this->fbo.end(); + + //project the points + { + auto cameraMatrix = camera->getCameraMatrix(); + auto distortionCoefficients = camera->getDistortionCoefficients(); + + cv::Mat rotation, translation; + testObject->getExtrinsics(rotation, translation); + + cv::projectPoints(ofxCv::toCv(testObject->getObjectPoints()) + , rotation + , translation + , cameraMatrix + , distortionCoefficients + , ofxCv::toCv(this->projectedPoints)); + } + + //apply the noise + { + for (auto & point : this->projectedPoints) { + point.x += ofRandomf() * this->parameters.noise; + point.y += ofRandomf() * this->parameters.noise; + } + } + } + } + + //---------- + ofxCvGui::PanelPtr ProjectPoints::getPanel() { + return this->panel; + } + + //---------- + const std::vector & ProjectPoints::getProjectedPoints() { + return this->projectedPoints; + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.h new file mode 100644 index 0000000000..5346ef2b2a --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ofxRulr.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolvePnP { + class ProjectPoints : public Base { + public: + ProjectPoints(); + string getTypeName() const override; + void init(); + void update(); + ofxCvGui::PanelPtr getPanel() override; + + const vector & getProjectedPoints(); + protected: + struct : ofParameterGroup { + ofParameter noise{ "Noise [px]", 0, 0, 100.0 }; + PARAM_DECLARE("ProjectPoints", noise); + } parameters; + + shared_ptr panel; + ofFbo fbo; + + vector projectedPoints; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.cpp new file mode 100644 index 0000000000..cc497fe648 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.cpp @@ -0,0 +1,156 @@ +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolvePnP { + //---------- + SolvePnP::SolvePnP() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string SolvePnP::getTypeName() const { + return "Experiments::SolvePnP::SolvePnP"; + } + + //---------- + void SolvePnP::init() { + RULR_NODE_DRAW_WORLD_LISTENER; + RULR_NODE_INSPECTOR_LISTENER; + RULR_NODE_UPDATE_LISTENER; + + this->addInput(); + this->addInput(); + this->addInput(); + } + + //---------- + void SolvePnP::update() { + auto testObject = this->getInput(); + auto projectPoints = this->getInput(); + auto camera = this->getInput(); + if (testObject && projectPoints && camera) { + auto imagePoints = ofxCv::toCv(projectPoints->getProjectedPoints()); + auto objectPoints = ofxCv::toCv(testObject->getObjectPoints()); + + try { + cv::solvePnP(objectPoints + , imagePoints + , camera->getCameraMatrix() + , camera->getDistortionCoefficients() + , this->rotationVector + , this->translation + , false); + + //get the ground truth + struct { + cv::Mat rotationVector, translation; + } groundTruth; + testObject->getExtrinsics(groundTruth.rotationVector, groundTruth.translation); + + //get the error + cv::absdiff(this->rotationVector, groundTruth.rotationVector, this->error.rotationVector); + cv::absdiff(this->translation, groundTruth.translation, this->error.translation); + + //calculate the moving average + if (this->movingAverageError.rotationVector.empty() + || this->movingAverageError.translation.empty()) { + this->movingAverageError.rotationVector = this->error.rotationVector.clone(); + this->movingAverageError.translation = this->error.translation.clone(); + } + else { + cv::addWeighted(this->movingAverageError.rotationVector, 0.99, this->error.rotationVector, 0.01, 0.0, this->movingAverageError.rotationVector); + cv::addWeighted(this->movingAverageError.translation, 0.99, this->error.translation, 0.01, 0.0, this->movingAverageError.translation); + } + } + RULR_CATCH_ALL_TO_ERROR; + } + } + + //---------- + void SolvePnP::drawWorldStage() { + try { + auto transform = ofxCv::makeMatrix(this->rotationVector, this->translation); + + ofPushMatrix(); + { + ofMultMatrix(transform); + auto testObject = this->getInput(); + if (testObject) { + testObject->drawObject(); + } + } + ofPopMatrix(); + } + RULR_CATCH_ALL_TO_ERROR; + } + + //---------- + void SolvePnP::populateInspector(ofxCvGui::InspectArguments & args) { + auto inspector = args.inspector; + + inspector->addTitle("Result", ofxCvGui::Widgets::Title::Level::H3); + inspector->addLiveValue("Rotation", [this]() { + if (this->rotationVector.empty()) { + return ofVec3f(); + } + else { + auto value = cv::Point3f(this->rotationVector); + return ofxCv::toOf(value); + } + }); + inspector->addLiveValue("Translation", [this]() { + if (this->translation.empty()) { + return ofVec3f(); + } + else { + auto value = cv::Point3f(this->translation); + return ofxCv::toOf(value); + } + }); + + inspector->addTitle("Error", ofxCvGui::Widgets::Title::Level::H3); + inspector->addLiveValue("Rotation", [this]() { + if (this->error.rotationVector.empty()) { + return ofVec3f(); + } + else { + auto value = cv::Point3f(this->error.rotationVector); + return ofxCv::toOf(value); + } + }); + inspector->addLiveValue("Translation", [this]() { + if (this->error.translation.empty()) { + return ofVec3f(); + } + else { + auto value = cv::Point3f(this->error.translation); + return ofxCv::toOf(value); + } + }); + + inspector->addTitle("Error (moving average)", ofxCvGui::Widgets::Title::Level::H3); + inspector->addLiveValue("Rotation", [this]() { + if (this->movingAverageError.rotationVector.empty()) { + return ofVec3f(); + } + else { + auto value = cv::Point3f(this->movingAverageError.rotationVector); + return ofxCv::toOf(value); + } + }); + inspector->addLiveValue("Translation", [this]() { + if (this->movingAverageError.translation.empty()) { + return ofVec3f(); + } + else { + auto value = cv::Point3f(this->movingAverageError.translation); + return ofxCv::toOf(value); + } + }); + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.h new file mode 100644 index 0000000000..39f0710f3e --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.h @@ -0,0 +1,31 @@ +#pragma once + +#include "ofxRulr.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolvePnP { + class SolvePnP : public Base { + public: + SolvePnP(); + string getTypeName() const override; + void init(); + void update(); + void drawWorldStage(); + void populateInspector(ofxCvGui::InspectArguments &); + protected: + cv::Mat rotationVector, translation; + + struct { + cv::Mat rotationVector, translation; + } error; + + struct { + cv::Mat rotationVector, translation; + } movingAverageError; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/TestObject.cpp b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/TestObject.cpp new file mode 100644 index 0000000000..e7964f56b1 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/TestObject.cpp @@ -0,0 +1,73 @@ +#include "pch_Plugin_Experiments.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolvePnP { + //---------- + TestObject::TestObject() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + string TestObject::getTypeName() const { + return "Experiments::SolvePnP::TestObject"; + } + + //---------- + void TestObject::init() { + RULR_RIGIDBODY_DRAW_OBJECT_LISTENER; + this->manageParameters(this->parameters); + + this->planeObject.setCenter({ 0.0f, 0.0f, 0.0f }); + this->planeObject.setScale({0.5f, 0.5f}); + this->planeObject.setNormal({ 0.0f, 0.0f, -1.0f }); + this->planeObject.setUp({ 0.0f, +1.0f, 0.0f }); + this->planeObject.setInfinite(false); + } + + //---------- + void TestObject::drawObject() { + ofPushMatrix(); + { + ofScale(this->parameters.diameter); + this->planeObject.draw(); + } + ofPopMatrix(); + } + + + //---------- + std::vector TestObject::getObjectPoints() { + vector points = { + { -0.5f, -0.5f, 0.0f } + ,{ +0.5f, -0.5f, 0.0f } + ,{ +0.5f, +0.5f, 0.0f } + ,{ -0.5f, +0.5f, 0.0f } + }; + + //apply diameter + for (auto & point : points) { + point = point * this->parameters.diameter; + } + + return points; + } + + + //---------- + std::vector TestObject::getWorldPoints() { + auto points = this->getObjectPoints(); + + //apply diameter and transform + ofMatrix4x4 worldTransform = this->getTransform(); + for (auto & point : points) { + point = point * worldTransform; + } + + return points; + } + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/TestObject.h b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/TestObject.h new file mode 100644 index 0000000000..48722685e5 --- /dev/null +++ b/Plugin_Experiments/src/ofxRulr/Nodes/Experiments/SolvePnP/TestObject.h @@ -0,0 +1,28 @@ +#pragma once + +#include "ofxRulr.h" + +namespace ofxRulr { + namespace Nodes { + namespace Experiments { + namespace SolvePnP { + class TestObject : public Nodes::Item::RigidBody { + public: + TestObject(); + string getTypeName() const override; + void init(); + void drawObject(); + vector getObjectPoints(); + vector getWorldPoints(); + protected: + struct : ofParameterGroup { + ofParameter diameter{ "Diameter [m]", 0.1, 0.01, 1.0 }; + PARAM_DECLARE("TestObject", diameter); + } parameters; + + ofxRay::Plane planeObject; + }; + } + } + } +} \ No newline at end of file diff --git a/Plugin_Experiments/src/pch_Plugin_Experiments.cpp b/Plugin_Experiments/src/pch_Plugin_Experiments.cpp new file mode 100644 index 0000000000..607d8b2c89 --- /dev/null +++ b/Plugin_Experiments/src/pch_Plugin_Experiments.cpp @@ -0,0 +1 @@ +#include "pch_Plugin_Experiments.h" \ No newline at end of file diff --git a/Plugin_Experiments/src/pch_Plugin_Experiments.h b/Plugin_Experiments/src/pch_Plugin_Experiments.h new file mode 100644 index 0000000000..329460f26b --- /dev/null +++ b/Plugin_Experiments/src/pch_Plugin_Experiments.h @@ -0,0 +1,26 @@ +#pragma once + +#include "pch_RulrNodes.h" +#include "pch_RulrCore.h" +#include "ofxPlugin.h" +#include "ofxNonLinearFit.h" + +#include "ofxRulr.h" +#include "ofxRulr/Utils/CaptureSet.h" +#include "ofxRulr/Nodes/Item/Camera.h" +#include "ofxRulr/Nodes/Item/Projector.h" + +#include "ofxRulr/Nodes/Experiments/SolvePnP/TestObject.h" +#include "ofxRulr/Nodes/Experiments/SolvePnP/ProjectPoints.h" +#include "ofxRulr/Nodes/Experiments/SolvePnP/SolvePnP.h" + +#include "ofxRulr/Nodes/Experiments/SolveMirror/SolveMirror.h" + +#include "pch_Plugin_ArUco.h" +#include "ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardInMirror.h" +#include "ofxRulr/Nodes/Experiments/MirrorPlaneCapture/BoardOnMirror.h" +#include "ofxRulr/Nodes/Experiments/MirrorPlaneCapture/HaloBoard.h" +#include "ofxRulr/Nodes/Experiments/MirrorPlaneCapture/Heliostats.h" +#include "ofxRulr/Nodes/Experiments/MirrorPlaneCapture/SolarAlignment.h" + +#include "ofxRulr/Nodes/Experiments/PhotoScan/BundlerCamera.h" diff --git a/Plugin_Experiments/src/plugin.cpp b/Plugin_Experiments/src/plugin.cpp new file mode 100644 index 0000000000..4fe1837532 --- /dev/null +++ b/Plugin_Experiments/src/plugin.cpp @@ -0,0 +1,19 @@ +#include "pch_Plugin_Experiments.h" + +OFXPLUGIN_PLUGIN_MODULES_BEGIN(ofxRulr::Nodes::Base) +{ + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::SolvePnP::TestObject); + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::SolvePnP::ProjectPoints); + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::SolvePnP::SolvePnP); + + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::SolveMirror::SolveMirror); + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::MirrorPlaneCapture::BoardInMirror); + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::MirrorPlaneCapture::BoardOnMirror); + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::MirrorPlaneCapture::HaloBoard); + + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::MirrorPlaneCapture::Heliostats); + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::MirrorPlaneCapture::SolarAlignment); + + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::Experiments::PhotoScan::BundlerCamera); +} +OFXPLUGIN_PLUGIN_MODULES_END \ No newline at end of file diff --git a/Plugin_LSS/Plugin_LSS.vcxproj b/Plugin_LSS/Plugin_LSS.vcxproj index 74d8d20c5f..b6e8335890 100644 --- a/Plugin_LSS/Plugin_LSS.vcxproj +++ b/Plugin_LSS/Plugin_LSS.vcxproj @@ -79,13 +79,13 @@ - + NotUsing - + NotUsing - + NotUsing @@ -99,10 +99,10 @@ - - - - + + + + diff --git a/RulrLibrary/RulrLibrary_Debug.def b/RulrLibrary/RulrLibrary_Debug.def index 861cf05c6b..4a7fb1b71e 100644 --- a/RulrLibrary/RulrLibrary_Debug.def +++ b/RulrLibrary/RulrLibrary_Debug.def @@ -16,24 +16,28 @@ EXPORTS glfwShowWindow glfwSwapBuffers glfwWindowHint + ??0ofBoxPrimitive@@QEAA@XZ ??0ofBuffer@@QEAA@AEAV?$basic_istream@DU?$char_traits@D@std@@@std@@_K@Z ??0ofBuffer@@QEAA@PEBD_K@Z ??0ofBuffer@@QEAA@XZ ??0ofFbo@@QEAA@XZ ??0ofFile@@QEAA@AEBVpath@filesystem@boost@@W4Mode@0@_N@Z ??0ofFile@@QEAA@XZ + ??0ofHttpRequest@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0_N@Z ??0ofLogError@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0ofLogNotice@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0ofLogWarning@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0ofMesh@@QEAA@XZ ??0ofNode@@QEAA@XZ ??0ofParameterGroup@@QEAA@XZ + ??0ofPath@@QEAA@XZ ??0ofPlanePrimitive@@QEAA@XZ ??0ofPolyline@@QEAA@XZ ??0ofRectangle@@QEAA@AEBV0@@Z ??0ofRectangle@@QEAA@MMMM@Z ??0ofSerial@@QEAA@XZ ??0ofTexture@@QEAA@XZ + ??0ofURLFileLoader@@QEAA@XZ ??0ofVbo@@QEAA@AEBV0@@Z ??0ofVbo@@QEAA@XZ ??0ofVboMesh@@QEAA@XZ @@ -54,6 +58,7 @@ EXPORTS ??1ofxOscMessage@@QEAA@XZ ??4ofVbo@@QEAAAEAV0@AEBV0@@Z ??9tpp@@YA_NAEBVfIterator@Delaunay@0@0@Z + ??BofBuffer@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?ofCircle@@YAXAEBVofVec3f@@M@Z ?ofCircle@@YAXMMM@Z ?ofClamp@@YAMMMM@Z @@ -62,7 +67,9 @@ EXPORTS ?ofCreateWindow@@YA?AV?$shared_ptr@VofAppBaseWindow@@@std@@AEBVofWindowSettings@@@Z ?ofDisableAlphaBlending@@YAXXZ ?ofDisableBlendMode@@YAXXZ + ?ofDrawArrow@@YAXAEBVofVec3f@@0M@Z ?ofDrawAxis@@YAXM@Z + ?ofDrawBox@@YAXAEBVofVec3f@@MMM@Z ?ofDrawCircle@@YAXAEBVofVec3f@@M@Z ?ofDrawCircle@@YAXMMM@Z ?ofDrawGridPlane@@YAXM_K_N@Z @@ -99,11 +106,14 @@ EXPORTS ?ofPushMatrix@@YAXXZ ?ofPushStyle@@YAXXZ ?ofPushView@@YAXXZ + ?ofRandomf@@YAMXZ + ?ofRotate@@YAXM@Z ?ofRotate@@YAXMMMM@Z ?ofRunApp@@YAHPEAVofBaseApp@@@Z ?ofSaveImage@@YAXAEBV?$ofPixels_@E@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4ofImageQualityType@@@Z ?ofSaveImage@@YAXAEBV?$ofPixels_@M@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4ofImageQualityType@@@Z ?ofScale@@YAXAEBVofVec3f@@@Z + ?ofScale@@YAXM@Z ?ofScale@@YAXMMM@Z ?ofSetCircleResolution@@YAXH@Z ?ofSetColor@@YAXAEBV?$ofColor_@E@@@Z @@ -136,6 +146,8 @@ EXPORTS ?bind@?$ofImage_@E@@QEBAXH@Z ?clear@?$ofImage_@E@@QEAAXXZ ?draw@?$ofImage_@E@@UEBAXMM@Z + ?draw@?$ofImage_@E@@UEBAXMMMM@Z + ?getTexture@?$ofImage_@E@@UEAAAEAVofTexture@@XZ ?load@?$ofImage_@E@@QEAA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBUofImageLoadSettings@@@Z ?load@?$ofImage_@E@@QEAA_NAEBVofFile@@AEBUofImageLoadSettings@@@Z ?loadImage@?$ofImage_@E@@QEAA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z @@ -155,16 +167,20 @@ EXPORTS ?isAllocated@?$ofPixels_@E@@QEBA_NXZ ?resize@?$ofPixels_@E@@QEAA_NHHW4ofInterpolationMethod@@@Z ?rotate90@?$ofPixels_@E@@QEAAXH@Z + ?set@?$ofPixels_@E@@QEAAXE@Z ?setColor@?$ofPixels_@E@@QEAAXAEBV?$ofColor_@E@@@Z ?size@?$ofPixels_@E@@QEBA_KXZ ?swap@?$ofPixels_@E@@QEAAXAEAV1@@Z ??0?$ofColor_@E@@QEAA@MM@Z ??0?$ofColor_@E@@QEAA@MMMM@Z + ??0?$ofImage_@E@@QEAA@$$QEAV0@@Z + ??0?$ofImage_@E@@QEAA@AEBV0@@Z ??0?$ofImage_@E@@QEAA@XZ ??0?$ofPixels_@E@@QEAA@AEBV0@@Z ??0?$ofPixels_@E@@QEAA@XZ ??1?$ofImage_@E@@UEAA@XZ ??1?$ofPixels_@E@@QEAA@XZ + ??4?$ofImage_@E@@QEAAAEAV0@$$QEAV0@@Z ??4?$ofImage_@E@@QEAAAEAV0@AEAV?$ofPixels_@E@@@Z ??4?$ofImage_@E@@QEAAAEAV0@AEBV0@@Z ??4?$ofPixels_@E@@QEAAAEAV0@AEBV0@@Z @@ -189,6 +205,7 @@ EXPORTS ??0?$ofPixels_@I@@QEAA@AEBV0@@Z ??0?$ofPixels_@I@@QEAA@XZ ??1?$ofPixels_@I@@QEAA@XZ + ??0Reader@Json@@QEAA@XZ ??0Value@Json@@QEAA@_N@Z ??0Value@Json@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0Value@Json@@QEAA@H@Z @@ -202,17 +219,21 @@ EXPORTS ??AValue@Json@@QEBAAEBV01@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??AValue@Json@@QEBAAEBV01@H@Z ??AValue@Json@@QEBAAEBV01@PEBD@Z + ?parse@Reader@Json@@QEAA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEAVValue@2@_N@Z ?append@Value@Json@@QEAAAEAV12@AEBV12@@Z ?asBool@Value@Json@@QEBA_NXZ ?asFloat@Value@Json@@QEBAMXZ ?asInt@Value@Json@@QEBAHXZ ?asString@Value@Json@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ + ?begin@Value@Json@@QEAA?AVValueIterator@2@XZ ?begin@Value@Json@@QEBA?AVValueConstIterator@2@XZ + ?end@Value@Json@@QEAA?AVValueIterator@2@XZ ?end@Value@Json@@QEBA?AVValueConstIterator@2@XZ ?getMemberNames@Value@Json@@QEBA?AV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@XZ ?isMember@Value@Json@@QEBA_NPEBD@Z ?isNull@Value@Json@@QEBA_NXZ ?isString@Value@Json@@QEBA_NXZ + ?toStyledString@Value@Json@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?deref@ValueIteratorBase@Json@@IEBAAEAVValue@2@XZ ?increment@ValueIteratorBase@Json@@IEAAXXZ ?isEqual@ValueIteratorBase@Json@@IEBA_NAEBV12@@Z @@ -234,16 +255,20 @@ EXPORTS ?getWidth@?$ofPixels_@M@@QEBAHXZ ?setFromPixels@?$ofPixels_@M@@QEAAXPEBMHHH@Z ?size@?$ofPixels_@M@@QEBA_KXZ + ??_0?$ofColor_@M@@QEAAAEAV0@AEBM@Z ??0?$ofColor_@M@@QEAA@MM@Z ??0?$ofColor_@M@@QEAA@MMMM@Z ??0?$ofImage_@M@@QEAA@XZ ??0?$ofPixels_@M@@QEAA@XZ ??1?$ofImage_@M@@UEAA@XZ ??1?$ofPixels_@M@@QEAA@XZ + ??A?$ofColor_@M@@QEAAAEAM_K@Z ??X?$ofColor_@M@@QEAAAEAV0@AEBM@Z ??1BaseFunctionId@priv@of@@UEAA@XZ ?draw@of3dPrimitive@@UEBAXXZ + ?drawWireframe@of3dPrimitive@@QEBAXXZ ?mapTexCoordsFromTexture@of3dPrimitive@@QEAAXAEAVofTexture@@@Z + ?setUseVbo@of3dPrimitive@@QEAAX_N@Z ?escape@ofAbstractParameter@@MEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?getEscapedName@ofAbstractParameter@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?isReferenceTo@ofAbstractParameter@@UEBA_NAEBV1@@Z @@ -254,22 +279,30 @@ EXPORTS ?audioOut@ofBaseSoundOutput@@UEAAXAEAVofSoundBuffer@@@Z ?audioOut@ofBaseSoundOutput@@UEAAXPEAMHH@Z ?audioOut@ofBaseSoundOutput@@UEAAXPEAMHHH_K@Z + ?setDepth@ofBoxPrimitive@@QEAAXM@Z + ?setHeight@ofBoxPrimitive@@QEAAXM@Z + ?setResolution@ofBoxPrimitive@@QEAAXH@Z + ?setWidth@ofBoxPrimitive@@QEAAXM@Z ?getData@ofBuffer@@QEBAPEBDXZ ?getText@ofBuffer@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?set@ofBuffer@@QEAAXPEBD_K@Z ?size@ofBuffer@@QEBA_KXZ ?worldToScreen@ofCamera@@QEBA?AVofVec3f@@V2@VofRectangle@@@Z ??0Settings@ofFbo@@QEAA@V?$shared_ptr@VofBaseGLRenderer@@@std@@@Z + ?allocate@ofFbo@@QEAAXHHHH@Z ?allocate@ofFbo@@QEAAXUSettings@1@@Z ?begin@ofFbo@@QEBAX_N@Z ?bind@ofFbo@@QEBAXXZ ?draw@ofFbo@@UEBAXMM@Z ?end@ofFbo@@QEBAXXZ ?getDepthTexture@ofFbo@@QEAAAEAVofTexture@@XZ + ?readToPixels@ofFbo@@QEBAXAEAV?$ofPixels_@E@@H@Z ?readToPixels@ofFbo@@QEBAXAEAV?$ofPixels_@M@@H@Z ?unbind@ofFbo@@QEBAXXZ ?open@ofFile@@QEAA_NAEBVpath@filesystem@boost@@W4Mode@1@_N@Z ?readToBuffer@ofFile@@QEAA?AVofBuffer@@XZ + ?getPath@ofFileDialogResult@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ + ?getBaseName@ofFilePath@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?getFileExt@ofFilePath@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?removeExt@ofFilePath@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?defaultSetting@ofImageLoadSettings@@2U1@A @@ -279,13 +312,16 @@ EXPORTS ?makeIdentityMatrix@ofMatrix4x4@@QEAAXXZ ?makeLookAtMatrix@ofMatrix4x4@@QEAAXAEBVofVec3f@@00@Z ?makeRotationMatrix@ofMatrix4x4@@QEAAXAEBVofQuaternion@@@Z + ?makeRotationMatrix@ofMatrix4x4@@QEAAXMAEBVofVec3f@@@Z ?makeRotationMatrix@ofMatrix4x4@@QEAAXMAEBVofVec3f@@M0M0@Z + ?makeRotationMatrix@ofMatrix4x4@@QEAAXMMMM@Z ?makeScaleMatrix@ofMatrix4x4@@QEAAXMMM@Z ?makeTranslationMatrix@ofMatrix4x4@@QEAAXMMM@Z ?postMult@ofMatrix4x4@@QEAAXAEBV1@@Z ?set@ofMatrix4x4@@QEAAXMMMMMMMMMMMMMMMM@Z ?setRotate@ofMatrix4x4@@QEAAXAEBVofQuaternion@@@Z ?addColor@ofMesh@@QEAAXAEBV?$ofColor_@M@@@Z + ?addColors@ofMesh@@QEAAXAEBV?$vector@V?$ofColor_@M@@V?$allocator@V?$ofColor_@M@@@std@@@std@@@Z ?addIndex@ofMesh@@QEAAXI@Z ?addIndices@ofMesh@@QEAAXPEBI_K@Z ?addTexCoord@ofMesh@@QEAAXAEBVofVec2f@@@Z @@ -328,6 +364,7 @@ EXPORTS ?getPosition@ofNode@@QEBA?AVofVec3f@@XZ ?lookAt@ofNode@@QEAAXAEBVofVec3f@@@Z ?setPosition@ofNode@@QEAAXAEBVofVec3f@@@Z + ?setScale@ofNode@@QEAAXMMM@Z ?add@ofParameterGroup@@QEAAXAEAVofAbstractParameter@@@Z ?clear@ofParameterGroup@@QEAAXXZ ?fromString@ofParameterGroup@@UEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z @@ -344,6 +381,12 @@ EXPORTS ?setSerializable@ofParameterGroup@@UEAAX_N@Z ?toString@ofParameterGroup@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?notifyParameterChanged@Value@ofParameterGroup@@QEAAXAEAVofAbstractParameter@@@Z + ?arc@ofPath@@QEAAXAEBVofVec3f@@MMMM@Z + ?close@ofPath@@QEAAXXZ + ?draw@ofPath@@QEBAXXZ + ?setCircleResolution@ofPath@@QEAAXH@Z + ?setFillColor@ofPath@@QEAAXAEBV?$ofColor_@E@@@Z + ?setFilled@ofPath@@QEAAX_N@Z ?set@ofPlanePrimitive@@QEAAXMMHHW4ofPrimitiveMode@@@Z ?addVertex@ofPolyline@@QEAAXAEBVofVec3f@@@Z ?addVertex@ofPolyline@@QEAAXMMM@Z @@ -351,6 +394,7 @@ EXPORTS ?close@ofPolyline@@QEAAXXZ ?draw@ofPolyline@@QEBAXXZ ?getVertices@ofPolyline@@QEBAAEBV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@XZ + ?getEuler@ofQuaternion@@QEBA?AVofVec3f@@XZ ?makeRotate@ofQuaternion@@QEAAXAEBVofVec3f@@0@Z ?makeRotate@ofQuaternion@@QEAAXMAEBVofVec3f@@@Z ?set@ofQuaternion@@QEAAXAEBVofMatrix4x4@@@Z @@ -385,6 +429,7 @@ EXPORTS ?unbind@ofTexture@@QEBAXH@Z ?drawString@ofTrueTypeFont@@QEBAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@MM@Z ?getStringBoundingBox@ofTrueTypeFont@@QEBA?AVofRectangle@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@MM_N@Z + ?handleRequest@ofURLFileLoader@@QEAA?AVofHttpResponse@@AEAVofHttpRequest@@@Z ?draw@ofVbo@@QEBAXHHH@Z ?setIndexData@ofVbo@@QEAAXPEBIHH@Z ?setVertexData@ofVbo@@QEAAXPEBVofVec2f@@HH@Z @@ -397,6 +442,7 @@ EXPORTS ?addAddon@Register@ofxAssets@@QEAAXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?getShaderPointer@Register@ofxAssets@@QEAA?AV?$shared_ptr@VShader@ofxAssets@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z ?getSingletonStore@?$Singleton@VRegister@ofxAssets@@@ofxSingleton@@SAAEAVSingletonStore@12@XZ + ?copy@ofxClipboard@@YAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?calibrateProjector@ofxCv@@YAMAEAVMat@cv@@00V?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@V?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@5@HH_NMM3H@Z ?findChessboardCornersPreTest@ofxCv@@YA_NVMat@cv@@V?$Size_@H@3@AEAV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@std@@H@Z ?getMaxVal@ofxCv@@YAMAEBVMat@cv@@@Z @@ -413,6 +459,7 @@ EXPORTS ?toCv@ofxCv@@YAAEBV?$Point_@M@cv@@AEBVofVec2f@@@Z ?toCv@ofxCv@@YAAEBV?$Point3_@M@cv@@AEBVofVec3f@@@Z ?toCv@ofxCv@@YAAEBV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@std@@AEBV?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@3@@Z + ?toCv@ofxCv@@YAAEBV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@std@@AEBV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@3@@Z ?toOf@ofxCv@@YA?AVofRectangle@@V?$Rect_@H@cv@@@Z ?toOf@ofxCv@@YAAEAV?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@std@@AEAV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@3@@Z ?toOf@ofxCv@@YAAEAV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@AEAV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@3@@Z @@ -451,6 +498,7 @@ EXPORTS ??0Draws@Panels@ofxCvGui@@QEAA@AEAVofBaseDraws@@@Z ??0Draws@Panels@ofxCvGui@@QEAA@XZ ??0Image@Panels@ofxCvGui@@QEAA@AEAVofAbstractImage@@@Z + ??0Scroll@Panels@ofxCvGui@@QEAA@XZ ??0Texture@Panels@ofxCvGui@@QEAA@AEBVofTexture@@@Z ?makeBaseDraws@Panels@ofxCvGui@@YA?AV?$shared_ptr@VDraws@Panels@ofxCvGui@@@std@@AEAVofBaseDraws@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z ?makeBlank@Panels@ofxCvGui@@YA?AV?$shared_ptr@VBase@Panels@ofxCvGui@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z @@ -573,8 +621,24 @@ EXPORTS ?sendBundle@ofxOscSender@@QEAAXAEAVofxOscBundle@@@Z ?sendMessage@ofxOscSender@@QEAAXAEAVofxOscMessage@@_N@Z ?setup@ofxOscSender@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z + ??0Plane@ofxRay@@QEAA@XZ ??0Ray@ofxRay@@QEAA@VofVec3f@@0_N@Z + ??0Ray@ofxRay@@QEAA@VofVec3f@@0V?$ofColor_@E@@_N@Z ??0Ray@ofxRay@@QEAA@XZ + ?draw@Plane@ofxRay@@UEBAXXZ + ?fitToPoints@Plane@ofxRay@@QEAA_NAEBV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@AEAM@Z + ?getABCD@Plane@ofxRay@@QEBA?AVofVec4f@@XZ + ?getCenter@Plane@ofxRay@@QEBAAEBVofVec3f@@XZ + ?getDistanceTo@Plane@ofxRay@@QEBAMAEBVofVec3f@@@Z + ?getNormal@Plane@ofxRay@@QEBAAEBVofVec3f@@XZ + ?intersect@Plane@ofxRay@@QEBA_NAEBVRay@2@AEAVofVec3f@@@Z + ?randomiseVectors@Plane@ofxRay@@UEAAXM@Z + ?reflect@Plane@ofxRay@@QEBA?AVofVec3f@@AEBV3@@Z + ?setCenter@Plane@ofxRay@@QEAAXAEBVofVec3f@@@Z + ?setInfinite@Plane@ofxRay@@QEAAX_N@Z + ?setNormal@Plane@ofxRay@@QEAAXAEBVofVec3f@@@Z + ?setScale@Plane@ofxRay@@QEAAXAEBVofVec2f@@@Z + ?setUp@Plane@ofxRay@@QEAAXAEBVofVec3f@@@Z ?beginAsCamera@Projector@ofxRay@@QEBAX_N@Z ?castPixel@Projector@ofxRay@@QEBA?AVRay@2@AEBVofVec2f@@@Z ?endAsCamera@Projector@ofxRay@@QEBAXXZ @@ -591,6 +655,7 @@ EXPORTS ?getStart@Ray@ofxRay@@QEBAAEBVofVec3f@@XZ ?intersect@Ray@ofxRay@@QEBA?AV12@AEBV12@@Z ?randomiseVectors@Ray@ofxRay@@UEAAXM@Z + ?setEnd@Ray@ofxRay@@QEAAXAEBVofVec3f@@@Z ??0Exception@ofxRulr@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?what@Exception@ofxRulr@@UEBAPEBDXZ ??0AbstractPin@Graph@ofxRulr@@QEAA@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z @@ -611,6 +676,7 @@ EXPORTS ?addInput@Base@Nodes@ofxRulr@@IEAAXV?$shared_ptr@VAbstractPin@Graph@ofxRulr@@@std@@@Z ?checkDrawOnWorldStage@Base@Nodes@ofxRulr@@QEAA_NAEBVWhenDrawOnWorldStage@3@@Z ?drawWorldAdvanced@Base@Nodes@ofxRulr@@QEAAXAEAUDrawWorldAdvancedArgs@3@@Z + ?drawWorldStage@Base@Nodes@ofxRulr@@QEAAXXZ ?getColor@Base@Nodes@ofxRulr@@QEAAAEBV?$ofColor_@E@@XZ ?getIcon@Base@Nodes@ofxRulr@@QEAAAEBVofBaseDraws@@XZ ?getInputPins@Base@Nodes@ofxRulr@@QEBAAEBV?$Set@VAbstractPin@Graph@ofxRulr@@@Utils@3@XZ @@ -644,11 +710,14 @@ EXPORTS ??0RigidBody@Item@Nodes@ofxRulr@@QEAA@XZ ??0View@Item@Nodes@ofxRulr@@QEAA@_N@Z ??1Camera@Item@Nodes@ofxRulr@@UEAA@XZ + ?findBoard@AbstractBoard@Item@Nodes@ofxRulr@@UEBA_NVMat@cv@@AEAV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@std@@AEAV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@8@VFindBoardMode@4@00@Z + ?getTypeName@AbstractBoard@Item@Nodes@ofxRulr@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?getObjectPoints@Board@Item@Nodes@ofxRulr@@QEBA?AV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@std@@XZ ?getSize@Board@Item@Nodes@ofxRulr@@QEBA?AV?$Size_@H@cv@@XZ ?getFrame@Camera@Item@Nodes@ofxRulr@@QEAA?AV?$shared_ptr@VFrame@ofxMachineVision@@@std@@XZ ?getFreshFrame@Camera@Item@Nodes@ofxRulr@@QEAA?AV?$shared_ptr@VFrame@ofxMachineVision@@@std@@XZ ?getGrabber@Camera@Item@Nodes@ofxRulr@@QEAA?AV?$shared_ptr@VSimple@Grabber@ofxMachineVision@@@std@@XZ + ?drawWorldStage@RigidBody@Item@Nodes@ofxRulr@@QEAAXXZ ?getExtrinsics@RigidBody@Item@Nodes@ofxRulr@@QEAAXAEAVMat@cv@@0_N@Z ?getPosition@RigidBody@Item@Nodes@ofxRulr@@QEBA?AVofVec3f@@XZ ?getRotationEuler@RigidBody@Item@Nodes@ofxRulr@@QEBA?AVofVec3f@@XZ @@ -696,10 +765,13 @@ EXPORTS ?getAllCapturesUntyped@AbstractCaptureSet@Utils@ofxRulr@@QEBA?AV?$vector@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@V?$allocator@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@@2@@std@@XZ ?getSelectionUntyped@AbstractCaptureSet@Utils@ofxRulr@@QEBA?AV?$vector@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@V?$allocator@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@@2@@std@@XZ ?populateWidgets@AbstractCaptureSet@Utils@ofxRulr@@QEAAXV?$shared_ptr@VWidgets@Panels@ofxCvGui@@@std@@@Z + ?remove@AbstractCaptureSet@Utils@ofxRulr@@QEAAXV?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@@Z ?serialize@AbstractCaptureSet@Utils@ofxRulr@@QEAAXAEAVValue@Json@@@Z + ?size@AbstractCaptureSet@Utils@ofxRulr@@QEBA_KXZ ?getDataDisplay@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@MEAA?AV?$shared_ptr@VElement@ofxCvGui@@@std@@XZ ?getTypeName@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?isSelected@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@QEBA_NXZ + ?rebuildDateStrings@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@QEAAXXZ ?glDisable@Graphics@Utils@ofxRulr@@SAXI@Z ?glEnable@Graphics@Utils@ofxRulr@@SAXI@Z ?popPointSize@Graphics@Utils@ofxRulr@@SAXXZ @@ -722,8 +794,14 @@ EXPORTS ?Triangulate@ofxTriangulate@@SAXAEBV?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@std@@0AEBVCamera@ofxRay@@1AEAV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@3@M@Z ??$ofDrawBitmapString@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@YAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@MMM@Z ??$ofFromString@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV01@@Z + ??5@YAAEAV?$basic_istream@DU?$char_traits@D@std@@@std@@AEAV01@AEAVofQuaternion@@@Z + ??5@YAAEAV?$basic_istream@DU?$char_traits@D@std@@@std@@AEAV01@AEAVPlane@ofxRay@@@Z + ??5@YAAEAV?$basic_istream@DU?$char_traits@D@std@@@std@@AEAV01@AEAVRay@ofxRay@@@Z ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVofAbstractParameter@@@Z ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVofBuffer@@@Z + ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVofQuaternion@@@Z + ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVPlane@ofxRay@@@Z + ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVRay@ofxRay@@@Z ??1Delaunay@tpp@@QEAA@XZ ??0fIterator@Delaunay@tpp@@AEAA@PEAV12@@Z ??1fIterator@Delaunay@tpp@@QEAA@XZ diff --git a/RulrLibrary/RulrLibrary_Release.def b/RulrLibrary/RulrLibrary_Release.def index 391fbb5300..73fd24006a 100644 --- a/RulrLibrary/RulrLibrary_Release.def +++ b/RulrLibrary/RulrLibrary_Release.def @@ -16,24 +16,28 @@ EXPORTS glfwShowWindow glfwSwapBuffers glfwWindowHint + ??0ofBoxPrimitive@@QEAA@XZ ??0ofBuffer@@QEAA@AEAV?$basic_istream@DU?$char_traits@D@std@@@std@@_K@Z ??0ofBuffer@@QEAA@PEBD_K@Z ??0ofBuffer@@QEAA@XZ ??0ofFbo@@QEAA@XZ ??0ofFile@@QEAA@AEBVpath@filesystem@boost@@W4Mode@0@_N@Z ??0ofFile@@QEAA@XZ + ??0ofHttpRequest@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0_N@Z ??0ofLogError@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0ofLogNotice@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0ofLogWarning@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0ofMesh@@QEAA@XZ ??0ofNode@@QEAA@XZ ??0ofParameterGroup@@QEAA@XZ + ??0ofPath@@QEAA@XZ ??0ofPlanePrimitive@@QEAA@XZ ??0ofPolyline@@QEAA@XZ ??0ofRectangle@@QEAA@AEBV0@@Z ??0ofRectangle@@QEAA@MMMM@Z ??0ofSerial@@QEAA@XZ ??0ofTexture@@QEAA@XZ + ??0ofURLFileLoader@@QEAA@XZ ??0ofVbo@@QEAA@AEBV0@@Z ??0ofVbo@@QEAA@XZ ??0ofVboMesh@@QEAA@XZ @@ -54,6 +58,7 @@ EXPORTS ??1ofxOscMessage@@QEAA@XZ ??4ofVbo@@QEAAAEAV0@AEBV0@@Z ??9tpp@@YA_NAEBVfIterator@Delaunay@0@0@Z + ??BofBuffer@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?ofCircle@@YAXAEBVofVec3f@@M@Z ?ofCircle@@YAXMMM@Z ?ofClamp@@YAMMMM@Z @@ -62,7 +67,9 @@ EXPORTS ?ofCreateWindow@@YA?AV?$shared_ptr@VofAppBaseWindow@@@std@@AEBVofWindowSettings@@@Z ?ofDisableAlphaBlending@@YAXXZ ?ofDisableBlendMode@@YAXXZ + ?ofDrawArrow@@YAXAEBVofVec3f@@0M@Z ?ofDrawAxis@@YAXM@Z + ?ofDrawBox@@YAXAEBVofVec3f@@MMM@Z ?ofDrawCircle@@YAXAEBVofVec3f@@M@Z ?ofDrawCircle@@YAXMMM@Z ?ofDrawGridPlane@@YAXM_K_N@Z @@ -99,11 +106,14 @@ EXPORTS ?ofPushMatrix@@YAXXZ ?ofPushStyle@@YAXXZ ?ofPushView@@YAXXZ + ?ofRandomf@@YAMXZ + ?ofRotate@@YAXM@Z ?ofRotate@@YAXMMMM@Z ?ofRunApp@@YAHPEAVofBaseApp@@@Z ?ofSaveImage@@YAXAEBV?$ofPixels_@E@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4ofImageQualityType@@@Z ?ofSaveImage@@YAXAEBV?$ofPixels_@M@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4ofImageQualityType@@@Z ?ofScale@@YAXAEBVofVec3f@@@Z + ?ofScale@@YAXM@Z ?ofScale@@YAXMMM@Z ?ofSetCircleResolution@@YAXH@Z ?ofSetColor@@YAXAEBV?$ofColor_@E@@@Z @@ -136,6 +146,8 @@ EXPORTS ?bind@?$ofImage_@E@@QEBAXH@Z ?clear@?$ofImage_@E@@QEAAXXZ ?draw@?$ofImage_@E@@UEBAXMM@Z + ?draw@?$ofImage_@E@@UEBAXMMMM@Z + ?getTexture@?$ofImage_@E@@UEAAAEAVofTexture@@XZ ?load@?$ofImage_@E@@QEAA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBUofImageLoadSettings@@@Z ?load@?$ofImage_@E@@QEAA_NAEBVofFile@@AEBUofImageLoadSettings@@@Z ?loadImage@?$ofImage_@E@@QEAA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z @@ -155,16 +167,20 @@ EXPORTS ?isAllocated@?$ofPixels_@E@@QEBA_NXZ ?resize@?$ofPixels_@E@@QEAA_NHHW4ofInterpolationMethod@@@Z ?rotate90@?$ofPixels_@E@@QEAAXH@Z + ?set@?$ofPixels_@E@@QEAAXE@Z ?setColor@?$ofPixels_@E@@QEAAXAEBV?$ofColor_@E@@@Z ?size@?$ofPixels_@E@@QEBA_KXZ ?swap@?$ofPixels_@E@@QEAAXAEAV1@@Z ??0?$ofColor_@E@@QEAA@MM@Z ??0?$ofColor_@E@@QEAA@MMMM@Z + ??0?$ofImage_@E@@QEAA@$$QEAV0@@Z + ??0?$ofImage_@E@@QEAA@AEBV0@@Z ??0?$ofImage_@E@@QEAA@XZ ??0?$ofPixels_@E@@QEAA@AEBV0@@Z ??0?$ofPixels_@E@@QEAA@XZ ??1?$ofImage_@E@@UEAA@XZ ??1?$ofPixels_@E@@QEAA@XZ + ??4?$ofImage_@E@@QEAAAEAV0@$$QEAV0@@Z ??4?$ofImage_@E@@QEAAAEAV0@AEAV?$ofPixels_@E@@@Z ??4?$ofImage_@E@@QEAAAEAV0@AEBV0@@Z ??4?$ofPixels_@E@@QEAAAEAV0@AEBV0@@Z @@ -189,6 +205,7 @@ EXPORTS ??0?$ofPixels_@I@@QEAA@AEBV0@@Z ??0?$ofPixels_@I@@QEAA@XZ ??1?$ofPixels_@I@@QEAA@XZ + ??0Reader@Json@@QEAA@XZ ??0Value@Json@@QEAA@_N@Z ??0Value@Json@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??0Value@Json@@QEAA@H@Z @@ -202,17 +219,21 @@ EXPORTS ??AValue@Json@@QEBAAEBV01@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ??AValue@Json@@QEBAAEBV01@H@Z ??AValue@Json@@QEBAAEBV01@PEBD@Z + ?parse@Reader@Json@@QEAA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEAVValue@2@_N@Z ?append@Value@Json@@QEAAAEAV12@AEBV12@@Z ?asBool@Value@Json@@QEBA_NXZ ?asFloat@Value@Json@@QEBAMXZ ?asInt@Value@Json@@QEBAHXZ ?asString@Value@Json@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ + ?begin@Value@Json@@QEAA?AVValueIterator@2@XZ ?begin@Value@Json@@QEBA?AVValueConstIterator@2@XZ + ?end@Value@Json@@QEAA?AVValueIterator@2@XZ ?end@Value@Json@@QEBA?AVValueConstIterator@2@XZ ?getMemberNames@Value@Json@@QEBA?AV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@XZ ?isMember@Value@Json@@QEBA_NPEBD@Z ?isNull@Value@Json@@QEBA_NXZ ?isString@Value@Json@@QEBA_NXZ + ?toStyledString@Value@Json@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?deref@ValueIteratorBase@Json@@IEBAAEAVValue@2@XZ ?increment@ValueIteratorBase@Json@@IEAAXXZ ?isEqual@ValueIteratorBase@Json@@IEBA_NAEBV12@@Z @@ -234,16 +255,20 @@ EXPORTS ?getWidth@?$ofPixels_@M@@QEBAHXZ ?setFromPixels@?$ofPixels_@M@@QEAAXPEBMHHH@Z ?size@?$ofPixels_@M@@QEBA_KXZ + ??_0?$ofColor_@M@@QEAAAEAV0@AEBM@Z ??0?$ofColor_@M@@QEAA@MM@Z ??0?$ofColor_@M@@QEAA@MMMM@Z ??0?$ofImage_@M@@QEAA@XZ ??0?$ofPixels_@M@@QEAA@XZ ??1?$ofImage_@M@@UEAA@XZ ??1?$ofPixels_@M@@QEAA@XZ + ??A?$ofColor_@M@@QEAAAEAM_K@Z ??X?$ofColor_@M@@QEAAAEAV0@AEBM@Z ??1BaseFunctionId@priv@of@@UEAA@XZ ?draw@of3dPrimitive@@UEBAXXZ + ?drawWireframe@of3dPrimitive@@QEBAXXZ ?mapTexCoordsFromTexture@of3dPrimitive@@QEAAXAEAVofTexture@@@Z + ?setUseVbo@of3dPrimitive@@QEAAX_N@Z ?escape@ofAbstractParameter@@MEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?getEscapedName@ofAbstractParameter@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?isReferenceTo@ofAbstractParameter@@UEBA_NAEBV1@@Z @@ -254,22 +279,30 @@ EXPORTS ?audioOut@ofBaseSoundOutput@@UEAAXAEAVofSoundBuffer@@@Z ?audioOut@ofBaseSoundOutput@@UEAAXPEAMHH@Z ?audioOut@ofBaseSoundOutput@@UEAAXPEAMHHH_K@Z + ?setDepth@ofBoxPrimitive@@QEAAXM@Z + ?setHeight@ofBoxPrimitive@@QEAAXM@Z + ?setResolution@ofBoxPrimitive@@QEAAXH@Z + ?setWidth@ofBoxPrimitive@@QEAAXM@Z ?getData@ofBuffer@@QEBAPEBDXZ ?getText@ofBuffer@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?set@ofBuffer@@QEAAXPEBD_K@Z ?size@ofBuffer@@QEBA_KXZ ?worldToScreen@ofCamera@@QEBA?AVofVec3f@@V2@VofRectangle@@@Z ??0Settings@ofFbo@@QEAA@V?$shared_ptr@VofBaseGLRenderer@@@std@@@Z + ?allocate@ofFbo@@QEAAXHHHH@Z ?allocate@ofFbo@@QEAAXUSettings@1@@Z ?begin@ofFbo@@QEBAX_N@Z ?bind@ofFbo@@QEBAXXZ ?draw@ofFbo@@UEBAXMM@Z ?end@ofFbo@@QEBAXXZ ?getDepthTexture@ofFbo@@QEAAAEAVofTexture@@XZ + ?readToPixels@ofFbo@@QEBAXAEAV?$ofPixels_@E@@H@Z ?readToPixels@ofFbo@@QEBAXAEAV?$ofPixels_@M@@H@Z ?unbind@ofFbo@@QEBAXXZ ?open@ofFile@@QEAA_NAEBVpath@filesystem@boost@@W4Mode@1@_N@Z ?readToBuffer@ofFile@@QEAA?AVofBuffer@@XZ + ?getPath@ofFileDialogResult@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ + ?getBaseName@ofFilePath@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?getFileExt@ofFilePath@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?removeExt@ofFilePath@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV23@@Z ?defaultSetting@ofImageLoadSettings@@2U1@A @@ -279,13 +312,16 @@ EXPORTS ?makeIdentityMatrix@ofMatrix4x4@@QEAAXXZ ?makeLookAtMatrix@ofMatrix4x4@@QEAAXAEBVofVec3f@@00@Z ?makeRotationMatrix@ofMatrix4x4@@QEAAXAEBVofQuaternion@@@Z + ?makeRotationMatrix@ofMatrix4x4@@QEAAXMAEBVofVec3f@@@Z ?makeRotationMatrix@ofMatrix4x4@@QEAAXMAEBVofVec3f@@M0M0@Z + ?makeRotationMatrix@ofMatrix4x4@@QEAAXMMMM@Z ?makeScaleMatrix@ofMatrix4x4@@QEAAXMMM@Z ?makeTranslationMatrix@ofMatrix4x4@@QEAAXMMM@Z ?postMult@ofMatrix4x4@@QEAAXAEBV1@@Z ?set@ofMatrix4x4@@QEAAXMMMMMMMMMMMMMMMM@Z ?setRotate@ofMatrix4x4@@QEAAXAEBVofQuaternion@@@Z ?addColor@ofMesh@@QEAAXAEBV?$ofColor_@M@@@Z + ?addColors@ofMesh@@QEAAXAEBV?$vector@V?$ofColor_@M@@V?$allocator@V?$ofColor_@M@@@std@@@std@@@Z ?addIndex@ofMesh@@QEAAXI@Z ?addIndices@ofMesh@@QEAAXPEBI_K@Z ?addTexCoord@ofMesh@@QEAAXAEBVofVec2f@@@Z @@ -328,6 +364,7 @@ EXPORTS ?getPosition@ofNode@@QEBA?AVofVec3f@@XZ ?lookAt@ofNode@@QEAAXAEBVofVec3f@@@Z ?setPosition@ofNode@@QEAAXAEBVofVec3f@@@Z + ?setScale@ofNode@@QEAAXMMM@Z ?add@ofParameterGroup@@QEAAXAEAVofAbstractParameter@@@Z ?clear@ofParameterGroup@@QEAAXXZ ?fromString@ofParameterGroup@@UEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z @@ -344,6 +381,12 @@ EXPORTS ?setSerializable@ofParameterGroup@@UEAAX_N@Z ?toString@ofParameterGroup@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?notifyParameterChanged@Value@ofParameterGroup@@QEAAXAEAVofAbstractParameter@@@Z + ?arc@ofPath@@QEAAXAEBVofVec3f@@MMMM@Z + ?close@ofPath@@QEAAXXZ + ?draw@ofPath@@QEBAXXZ + ?setCircleResolution@ofPath@@QEAAXH@Z + ?setFillColor@ofPath@@QEAAXAEBV?$ofColor_@E@@@Z + ?setFilled@ofPath@@QEAAX_N@Z ?set@ofPlanePrimitive@@QEAAXMMHHW4ofPrimitiveMode@@@Z ?addVertex@ofPolyline@@QEAAXAEBVofVec3f@@@Z ?addVertex@ofPolyline@@QEAAXMMM@Z @@ -351,6 +394,7 @@ EXPORTS ?close@ofPolyline@@QEAAXXZ ?draw@ofPolyline@@QEBAXXZ ?getVertices@ofPolyline@@QEBAAEBV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@XZ + ?getEuler@ofQuaternion@@QEBA?AVofVec3f@@XZ ?makeRotate@ofQuaternion@@QEAAXAEBVofVec3f@@0@Z ?makeRotate@ofQuaternion@@QEAAXMAEBVofVec3f@@@Z ?set@ofQuaternion@@QEAAXAEBVofMatrix4x4@@@Z @@ -385,6 +429,7 @@ EXPORTS ?unbind@ofTexture@@QEBAXH@Z ?drawString@ofTrueTypeFont@@QEBAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@MM@Z ?getStringBoundingBox@ofTrueTypeFont@@QEBA?AVofRectangle@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@MM_N@Z + ?handleRequest@ofURLFileLoader@@QEAA?AVofHttpResponse@@AEAVofHttpRequest@@@Z ?draw@ofVbo@@QEBAXHHH@Z ?setIndexData@ofVbo@@QEAAXPEBIHH@Z ?setVertexData@ofVbo@@QEAAXPEBVofVec2f@@HH@Z @@ -397,6 +442,7 @@ EXPORTS ?addAddon@Register@ofxAssets@@QEAAXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?getShaderPointer@Register@ofxAssets@@QEAA?AV?$shared_ptr@VShader@ofxAssets@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z ?getSingletonStore@?$Singleton@VRegister@ofxAssets@@@ofxSingleton@@SAAEAVSingletonStore@12@XZ + ?copy@ofxClipboard@@YAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?calibrateProjector@ofxCv@@YAMAEAVMat@cv@@00V?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@V?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@5@HH_NMM3H@Z ?findChessboardCornersPreTest@ofxCv@@YA_NVMat@cv@@V?$Size_@H@3@AEAV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@std@@H@Z ?getMaxVal@ofxCv@@YAMAEBVMat@cv@@@Z @@ -413,6 +459,7 @@ EXPORTS ?toCv@ofxCv@@YAAEBV?$Point_@M@cv@@AEBVofVec2f@@@Z ?toCv@ofxCv@@YAAEBV?$Point3_@M@cv@@AEBVofVec3f@@@Z ?toCv@ofxCv@@YAAEBV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@std@@AEBV?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@3@@Z + ?toCv@ofxCv@@YAAEBV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@std@@AEBV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@3@@Z ?toOf@ofxCv@@YA?AVofRectangle@@V?$Rect_@H@cv@@@Z ?toOf@ofxCv@@YAAEAV?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@std@@AEAV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@3@@Z ?toOf@ofxCv@@YAAEAV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@AEAV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@3@@Z @@ -451,6 +498,7 @@ EXPORTS ??0Draws@Panels@ofxCvGui@@QEAA@AEAVofBaseDraws@@@Z ??0Draws@Panels@ofxCvGui@@QEAA@XZ ??0Image@Panels@ofxCvGui@@QEAA@AEAVofAbstractImage@@@Z + ??0Scroll@Panels@ofxCvGui@@QEAA@XZ ??0Texture@Panels@ofxCvGui@@QEAA@AEBVofTexture@@@Z ?makeBaseDraws@Panels@ofxCvGui@@YA?AV?$shared_ptr@VDraws@Panels@ofxCvGui@@@std@@AEAVofBaseDraws@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z ?makeBlank@Panels@ofxCvGui@@YA?AV?$shared_ptr@VBase@Panels@ofxCvGui@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z @@ -573,8 +621,24 @@ EXPORTS ?sendBundle@ofxOscSender@@QEAAXAEAVofxOscBundle@@@Z ?sendMessage@ofxOscSender@@QEAAXAEAVofxOscMessage@@_N@Z ?setup@ofxOscSender@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z + ??0Plane@ofxRay@@QEAA@XZ ??0Ray@ofxRay@@QEAA@VofVec3f@@0_N@Z + ??0Ray@ofxRay@@QEAA@VofVec3f@@0V?$ofColor_@E@@_N@Z ??0Ray@ofxRay@@QEAA@XZ + ?draw@Plane@ofxRay@@UEBAXXZ + ?fitToPoints@Plane@ofxRay@@QEAA_NAEBV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@std@@AEAM@Z + ?getABCD@Plane@ofxRay@@QEBA?AVofVec4f@@XZ + ?getCenter@Plane@ofxRay@@QEBAAEBVofVec3f@@XZ + ?getDistanceTo@Plane@ofxRay@@QEBAMAEBVofVec3f@@@Z + ?getNormal@Plane@ofxRay@@QEBAAEBVofVec3f@@XZ + ?intersect@Plane@ofxRay@@QEBA_NAEBVRay@2@AEAVofVec3f@@@Z + ?randomiseVectors@Plane@ofxRay@@UEAAXM@Z + ?reflect@Plane@ofxRay@@QEBA?AVofVec3f@@AEBV3@@Z + ?setCenter@Plane@ofxRay@@QEAAXAEBVofVec3f@@@Z + ?setInfinite@Plane@ofxRay@@QEAAX_N@Z + ?setNormal@Plane@ofxRay@@QEAAXAEBVofVec3f@@@Z + ?setScale@Plane@ofxRay@@QEAAXAEBVofVec2f@@@Z + ?setUp@Plane@ofxRay@@QEAAXAEBVofVec3f@@@Z ?beginAsCamera@Projector@ofxRay@@QEBAX_N@Z ?castPixel@Projector@ofxRay@@QEBA?AVRay@2@AEBVofVec2f@@@Z ?endAsCamera@Projector@ofxRay@@QEBAXXZ @@ -591,6 +655,7 @@ EXPORTS ?getStart@Ray@ofxRay@@QEBAAEBVofVec3f@@XZ ?intersect@Ray@ofxRay@@QEBA?AV12@AEBV12@@Z ?randomiseVectors@Ray@ofxRay@@UEAAXM@Z + ?setEnd@Ray@ofxRay@@QEAAXAEBVofVec3f@@@Z ??0Exception@ofxRulr@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?what@Exception@ofxRulr@@UEBAPEBDXZ ??0AbstractPin@Graph@ofxRulr@@QEAA@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z @@ -611,6 +676,7 @@ EXPORTS ?addInput@Base@Nodes@ofxRulr@@IEAAXV?$shared_ptr@VAbstractPin@Graph@ofxRulr@@@std@@@Z ?checkDrawOnWorldStage@Base@Nodes@ofxRulr@@QEAA_NAEBVWhenDrawOnWorldStage@3@@Z ?drawWorldAdvanced@Base@Nodes@ofxRulr@@QEAAXAEAUDrawWorldAdvancedArgs@3@@Z + ?drawWorldStage@Base@Nodes@ofxRulr@@QEAAXXZ ?getColor@Base@Nodes@ofxRulr@@QEAAAEBV?$ofColor_@E@@XZ ?getIcon@Base@Nodes@ofxRulr@@QEAAAEBVofBaseDraws@@XZ ?getInputPins@Base@Nodes@ofxRulr@@QEBAAEBV?$Set@VAbstractPin@Graph@ofxRulr@@@Utils@3@XZ @@ -644,11 +710,14 @@ EXPORTS ??0RigidBody@Item@Nodes@ofxRulr@@QEAA@XZ ??0View@Item@Nodes@ofxRulr@@QEAA@_N@Z ??1Camera@Item@Nodes@ofxRulr@@UEAA@XZ + ?findBoard@AbstractBoard@Item@Nodes@ofxRulr@@UEBA_NVMat@cv@@AEAV?$vector@V?$Point_@M@cv@@V?$allocator@V?$Point_@M@cv@@@std@@@std@@AEAV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@8@VFindBoardMode@4@00@Z + ?getTypeName@AbstractBoard@Item@Nodes@ofxRulr@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?getObjectPoints@Board@Item@Nodes@ofxRulr@@QEBA?AV?$vector@V?$Point3_@M@cv@@V?$allocator@V?$Point3_@M@cv@@@std@@@std@@XZ ?getSize@Board@Item@Nodes@ofxRulr@@QEBA?AV?$Size_@H@cv@@XZ ?getFrame@Camera@Item@Nodes@ofxRulr@@QEAA?AV?$shared_ptr@VFrame@ofxMachineVision@@@std@@XZ ?getFreshFrame@Camera@Item@Nodes@ofxRulr@@QEAA?AV?$shared_ptr@VFrame@ofxMachineVision@@@std@@XZ ?getGrabber@Camera@Item@Nodes@ofxRulr@@QEAA?AV?$shared_ptr@VSimple@Grabber@ofxMachineVision@@@std@@XZ + ?drawWorldStage@RigidBody@Item@Nodes@ofxRulr@@QEAAXXZ ?getExtrinsics@RigidBody@Item@Nodes@ofxRulr@@QEAAXAEAVMat@cv@@0_N@Z ?getPosition@RigidBody@Item@Nodes@ofxRulr@@QEBA?AVofVec3f@@XZ ?getRotationEuler@RigidBody@Item@Nodes@ofxRulr@@QEBA?AVofVec3f@@XZ @@ -696,10 +765,13 @@ EXPORTS ?getAllCapturesUntyped@AbstractCaptureSet@Utils@ofxRulr@@QEBA?AV?$vector@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@V?$allocator@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@@2@@std@@XZ ?getSelectionUntyped@AbstractCaptureSet@Utils@ofxRulr@@QEBA?AV?$vector@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@V?$allocator@V?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@@2@@std@@XZ ?populateWidgets@AbstractCaptureSet@Utils@ofxRulr@@QEAAXV?$shared_ptr@VWidgets@Panels@ofxCvGui@@@std@@@Z + ?remove@AbstractCaptureSet@Utils@ofxRulr@@QEAAXV?$shared_ptr@VBaseCapture@AbstractCaptureSet@Utils@ofxRulr@@@std@@@Z ?serialize@AbstractCaptureSet@Utils@ofxRulr@@QEAAXAEAVValue@Json@@@Z + ?size@AbstractCaptureSet@Utils@ofxRulr@@QEBA_KXZ ?getDataDisplay@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@MEAA?AV?$shared_ptr@VElement@ofxCvGui@@@std@@XZ ?getTypeName@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@UEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ?isSelected@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@QEBA_NXZ + ?rebuildDateStrings@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@QEAAXXZ ?glDisable@Graphics@Utils@ofxRulr@@SAXI@Z ?glEnable@Graphics@Utils@ofxRulr@@SAXI@Z ?popPointSize@Graphics@Utils@ofxRulr@@SAXXZ @@ -722,8 +794,14 @@ EXPORTS ?Triangulate@ofxTriangulate@@SAXAEBV?$vector@VofVec2f@@V?$allocator@VofVec2f@@@std@@@std@@0AEBVCamera@ofxRay@@1AEAV?$vector@VofVec3f@@V?$allocator@VofVec3f@@@std@@@3@M@Z ??$ofDrawBitmapString@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@YAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@MMM@Z ??$ofFromString@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV01@@Z + ??5@YAAEAV?$basic_istream@DU?$char_traits@D@std@@@std@@AEAV01@AEAVofQuaternion@@@Z + ??5@YAAEAV?$basic_istream@DU?$char_traits@D@std@@@std@@AEAV01@AEAVPlane@ofxRay@@@Z + ??5@YAAEAV?$basic_istream@DU?$char_traits@D@std@@@std@@AEAV01@AEAVRay@ofxRay@@@Z ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVofAbstractParameter@@@Z ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVofBuffer@@@Z + ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVofQuaternion@@@Z + ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVPlane@ofxRay@@@Z + ??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBVRay@ofxRay@@@Z ??1Delaunay@tpp@@QEAA@XZ ??0fIterator@Delaunay@tpp@@AEAA@PEAV12@@Z ??1fIterator@Delaunay@tpp@@QEAA@XZ diff --git a/ofxRulr_Plugin.props b/ofxRulr_Plugin.props index 8d5f85fea2..a91a6a3250 100644 --- a/ofxRulr_Plugin.props +++ b/ofxRulr_Plugin.props @@ -24,6 +24,9 @@ $(SolutionDir)\bin;%(AdditionalLibraryDirectories) RulrLibrary_$(Configuration).lib;%(AdditionalDependencies) + + BUILD_$(AssemblyName);%(PreprocessorDefinitions) + \ No newline at end of file