From 6f67c290a341ebfe4ec09f2131285c9ff5febf76 Mon Sep 17 00:00:00 2001 From: Elliot Woods Date: Wed, 22 Nov 2017 16:12:57 +0900 Subject: [PATCH] Introduce SLS nodes Fine tuning of AR nodes Working on ProjectorFromGraycode node --- Application/Rulr.sln | 15 +- Core/src/ofxRulr/Graph/World.h | 2 + Core/src/ofxRulr/Nodes/Base.cpp | 30 +- Core/src/ofxRulr/Nodes/Base.h | 1 - Core/src/ofxRulr/Utils/CaptureSet.cpp | 12 +- Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp | 23 +- Nodes/src/ofxRulr/Nodes/Test/ARCube.h | 3 +- .../VVVV/SplitSymbolNames/main.v4p | 8 +- .../ofxRulr/Nodes/ArUco/AlignMarkerMap.cpp | 2 +- .../src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp | 4 +- .../src/ofxRulr/Nodes/ArUco/Detector.cpp | 53 +- .../src/ofxRulr/Nodes/ArUco/Detector.h | 20 +- .../src/ofxRulr/Nodes/ArUco/FindMarkers.cpp | 4 +- .../Calibrate/ProjectorFromGraycode.cpp | 484 +++++++++--------- .../Calibrate/ProjectorFromGraycode.h | 43 +- Plugin_Calibrate/src/pch_Plugin_Calibrate.h | 2 + Plugin_SLS/Plugin_SLS.vcxproj | 90 ++++ Plugin_SLS/Plugin_SLS.vcxproj.filters | 36 ++ Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.cpp | 211 ++++++++ Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.h | 64 +++ Plugin_SLS/src/pch_Plugin_SLS.cpp | 1 + Plugin_SLS/src/pch_Plugin_SLS.h | 13 + Plugin_SLS/src/plugin.cpp | 8 + RulrLibrary/RulrLibrary_Debug.def | 27 +- RulrLibrary/RulrLibrary_Release.def | 25 +- 25 files changed, 857 insertions(+), 324 deletions(-) create mode 100644 Plugin_SLS/Plugin_SLS.vcxproj create mode 100644 Plugin_SLS/Plugin_SLS.vcxproj.filters create mode 100644 Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.cpp create mode 100644 Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.h create mode 100644 Plugin_SLS/src/pch_Plugin_SLS.cpp create mode 100644 Plugin_SLS/src/pch_Plugin_SLS.h create mode 100644 Plugin_SLS/src/plugin.cpp diff --git a/Application/Rulr.sln b/Application/Rulr.sln index f601a57f5b..ceecfa2fd2 100644 --- a/Application/Rulr.sln +++ b/Application/Rulr.sln @@ -39,7 +39,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ofxBlackmagicLib", "..\..\o EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "..\Core\Core.vcxproj", "{3E918808-ADEE-4B04-9042-25F795280BAD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_KinectForWindows2", "..\Plugin_KinectForWindows2\Plugin_KinectForWindows2.vcxproj", "{CBFC3D47-1E57-4291-90B6-C344B836B8DA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_KFW2", "..\Plugin_KinectForWindows2\Plugin_KinectForWindows2.vcxproj", "{CBFC3D47-1E57-4291-90B6-C344B836B8DA}" ProjectSection(ProjectDependencies) = postProject {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} = {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} EndProjectSection @@ -165,6 +165,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_BrightnessAssignment EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_Calibrate", "..\Plugin_Calibrate\Plugin_Calibrate.vcxproj", "{C135C231-F751-482D-9E4C-C29473E3DD98}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SLS", "SLS", "{B5D96B9A-620B-49D9-8A5D-B30C62FC76E4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_SLS", "..\Plugin_SLS\Plugin_SLS.vcxproj", "{651FDD8B-6C0C-4A82-89ED-182CFF129D03}" + ProjectSection(ProjectDependencies) = postProject + {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} = {C400D413-78E4-4F60-B5E2-7965FCAC0EEC} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -327,6 +334,10 @@ Global {C135C231-F751-482D-9E4C-C29473E3DD98}.Debug|x64.Build.0 = Debug|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}.Release|x64.ActiveCfg = Release|x64 + {651FDD8B-6C0C-4A82-89ED-182CFF129D03}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -387,5 +398,7 @@ Global {BF44EFB4-1225-4F66-8ACC-349133784013} = {148566DA-E924-4085-B479-ACA0D1601C9A} {5C78C8F2-6D2D-4B46-9D92-E29015F0E66A} = {BF44EFB4-1225-4F66-8ACC-349133784013} {C135C231-F751-482D-9E4C-C29473E3DD98} = {DB1A8915-E9B1-47A2-8331-CA76EBA11A00} + {B5D96B9A-620B-49D9-8A5D-B30C62FC76E4} = {148566DA-E924-4085-B479-ACA0D1601C9A} + {651FDD8B-6C0C-4A82-89ED-182CFF129D03} = {B5D96B9A-620B-49D9-8A5D-B30C62FC76E4} EndGlobalSection EndGlobal diff --git a/Core/src/ofxRulr/Graph/World.h b/Core/src/ofxRulr/Graph/World.h index d074dff25b..771cdedf25 100644 --- a/Core/src/ofxRulr/Graph/World.h +++ b/Core/src/ofxRulr/Graph/World.h @@ -23,6 +23,8 @@ namespace ofxRulr { ofxCvGui::PanelGroupPtr getGuiGrid() const; shared_ptr getPatch() const; shared_ptr getWorldStage() const; + + ofParameter lockSelection{ "Lock selection", false }; protected: static ofxCvGui::Controller * gui; ///< Why is this static? Needs comment. I presume it's so we can grid multiple worlds? ofxCvGui::PanelGroupPtr guiGrid; diff --git a/Core/src/ofxRulr/Nodes/Base.cpp b/Core/src/ofxRulr/Nodes/Base.cpp index 5a73084cc9..b74b7c9bfe 100755 --- a/Core/src/ofxRulr/Nodes/Base.cpp +++ b/Core/src/ofxRulr/Nodes/Base.cpp @@ -2,6 +2,7 @@ #include "Base.h" #include "ofxRulr/Graph/Editor/NodeHost.h" +#include "ofxRulr/Graph/World.h" #include "../Exception.h" #include "GraphicsManager.h" @@ -168,7 +169,14 @@ namespace ofxRulr { } }; - inspector->add(new Widgets::Title(this->getTypeName(), ofxCvGui::Widgets::Title::Level::H3)); + inspector->addToggle("Lock selection" + , []() { + return ofxCvGui::InspectController::X().getInspectorLocked(); + }, [](bool inspectorLocked) { + ofxCvGui::InspectController::X().setInspectorLocked(inspectorLocked); + }); + + inspector->add(new Widgets::Title("Type : " + this->getTypeName(), ofxCvGui::Widgets::Title::Level::H3)); { auto widget = inspector->addMultipleChoice("Draw on World Stage", { "Always", "Selected", "Never" }); @@ -178,26 +186,6 @@ namespace ofxRulr { }; } - inspector->add(new Widgets::Button("Save Node...", [this] () { - try { - auto result = ofSystemSaveDialog(this->getDefaultFilename() + ".json", "Save node [" + this->getName() + "] as json"); - if (result.bSuccess) { - this->save(result.getPath()); - } - } - RULR_CATCH_ALL_TO_ALERT - })); - - inspector->add(new Widgets::Button("Load Node...", [this] () { - try { - auto result = ofSystemLoadDialog("Load node [" + this->getName() + "] from json"); - if (result.bSuccess) { - this->load(result.getPath()); - } - } - RULR_CATCH_ALL_TO_ALERT - })); - //pin status for (auto inputPin : this->getInputPins()) { inspector->add(new Widgets::Indicator(inputPin->getName(), [inputPin]() { diff --git a/Core/src/ofxRulr/Nodes/Base.h b/Core/src/ofxRulr/Nodes/Base.h index 6c14366db5..7cad63862d 100644 --- a/Core/src/ofxRulr/Nodes/Base.h +++ b/Core/src/ofxRulr/Nodes/Base.h @@ -156,7 +156,6 @@ namespace ofxRulr { ofxLiquidEvent> onConnect; ofxLiquidEvent> onDisconnect; ofxLiquidEvent onAnyInputConnectionChanged; - protected: void addInput(shared_ptr); diff --git a/Core/src/ofxRulr/Utils/CaptureSet.cpp b/Core/src/ofxRulr/Utils/CaptureSet.cpp index 2aca23f73a..3fade5f4e6 100644 --- a/Core/src/ofxRulr/Utils/CaptureSet.cpp +++ b/Core/src/ofxRulr/Utils/CaptureSet.cpp @@ -362,11 +362,13 @@ namespace ofxRulr { //---------- void AbstractCaptureSet::deserialize(const Json::Value & json) { this->captures.clear(); - auto & jsonCaptures = json["captures"]; - for (const auto & jsonCapture : jsonCaptures) { - auto capture = this->makeEmpty(); - capture->deserialize(jsonCapture); - this->add(capture); //ensure event listeners are attached + if (json.isMember("captures")) { + auto & jsonCaptures = json["captures"]; + for (const auto & jsonCapture : jsonCaptures) { + auto capture = this->makeEmpty(); + capture->deserialize(jsonCapture); + this->add(capture); //ensure event listeners are attached + } } } diff --git a/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp b/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp index 0f5126db81..6dbad4c3e1 100644 --- a/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp +++ b/Nodes/src/ofxRulr/Nodes/Test/ARCube.cpp @@ -152,11 +152,26 @@ namespace ofxRulr { //draw the 3D world on top const auto & view = camera->getViewInWorldSpace(); view.beginAsCamera(true); - glEnable(GL_DEPTH_TEST); - glClear(GL_DEPTH_BUFFER_BIT); - this->drawWorldStage(); - glDisable(GL_DEPTH_TEST); + { + glEnable(GL_DEPTH_TEST); + { + glClear(GL_DEPTH_BUFFER_BIT); + this->drawWorldStage(); + } + glDisable(GL_DEPTH_TEST); + } view.endAsCamera(); + + //emulate alpha by drawing undistorted image again on top + if (this->parameters.alpha < 1.0f) { + ofEnableAlphaBlending(); + { + ofSetColor(255, 255.0f * (1.0f - this->parameters.alpha)); + //draw the undistorted image + this->undistorted.draw(0, 0); + } + ofDisableAlphaBlending(); + } } this->fbo.end(); } diff --git a/Nodes/src/ofxRulr/Nodes/Test/ARCube.h b/Nodes/src/ofxRulr/Nodes/Test/ARCube.h index 4128549b4b..99591fd19a 100644 --- a/Nodes/src/ofxRulr/Nodes/Test/ARCube.h +++ b/Nodes/src/ofxRulr/Nodes/Test/ARCube.h @@ -50,8 +50,9 @@ namespace ofxRulr { ofParameter activewhen{ "Active when", ActiveWhen::Selected }; ofParameter drawStyle{ "Draw style", DrawStyle::Cube }; ofParameter fillMode{ "Fill mode", FillMode::Fill }; + ofParameter alpha{ "Alpha", 1.0f, 0.0f, 1.0f }; ofParameter findBoardMode{ "Find board mode", FindBoardMode::Optimized }; - PARAM_DECLARE("ARCube", activewhen, drawStyle, fillMode, findBoardMode); + PARAM_DECLARE("ARCube", activewhen, drawStyle, fillMode, alpha, findBoardMode); } parameters; ofBoxPrimitive cube; diff --git a/PlatformExamples/VVVV/SplitSymbolNames/main.v4p b/PlatformExamples/VVVV/SplitSymbolNames/main.v4p index 3b145cec76..778f0fbae8 100644 --- a/PlatformExamples/VVVV/SplitSymbolNames/main.v4p +++ b/PlatformExamples/VVVV/SplitSymbolNames/main.v4p @@ -1,13 +1,13 @@ - + - + - + - + diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.cpp index 8f78d07f7b..abe7faddba 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/AlignMarkerMap.cpp @@ -51,7 +51,7 @@ namespace ofxRulr { element->onDraw += [this](ofxCvGui::DrawArguments) { if (this->isSelected()) { - auto residual = this->residual.get(); + auto residual = sqrt(this->residual.get()); if (residual != 0.0f) { ofxCvGui::Utils::drawText("Residual " + ofToString(residual, 3) + "m" , 0, 130, false); diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp index 365c8e59df..151519bb54 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/ChArUcoBoard.cpp @@ -203,9 +203,9 @@ namespace ofxRulr { auto pixelsPerMeter = this->getPreviewPixelsPerMeter(); ofScale(1.0f / pixelsPerMeter, 1.0f / pixelsPerMeter, 1.0f); this->preview.draw(-this->preview.getWidth() / 2.0f - , -this->preview.getHeight() / 2.0f + , +this->preview.getHeight() / 2.0f , this->preview.getWidth() - , +this->preview.getHeight()); + , -this->preview.getHeight()); } ofPopMatrix(); } diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.cpp index 8b12d546ba..b0522163ef 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.cpp @@ -52,11 +52,20 @@ namespace ofxRulr { }; this->panel = panel; } + + //add listeners + this->parameters.detectorType.addListener(this, &Detector::changeDetectorCallback); + this->parameters.markerLength.addListener(this, &Detector::changeFloatCallback); + this->parameters.threshold.areaSize.addListener(this, &Detector::changeIntCallback); + this->parameters.threshold.subtract.addListener(this, &Detector::changeIntCallback); + this->parameters.threshold.parameterRange.addListener(this, &Detector::changeIntCallback); + this->parameters.refinement.refinementType.addListener(this, &Detector::changeRefinementTypeCallback); + this->parameters.refinement.windowSize.addListener(this, &Detector::changeIntCallback); } //---------- void Detector::update() { - if (this->cachedDetector != this->parameters.detectorType.get()) { + if(this->detectorDirty) { this->rebuildDetector(); } } @@ -68,6 +77,11 @@ namespace ofxRulr { inspector->addLiveValue("Dictionary type", [this]() { return aruco::Dictionary::getTypeString(this->dictionaryType); }); + inspector->addButton("Retry detect", [this]() { + this->findMarkers(this->lastDetection.image + , this->lastDetection.cameraMatrix + , this->lastDetection.distortionCoefficients); + }); } //---------- @@ -117,12 +131,21 @@ namespace ofxRulr { } //---------- - const std::vector & Detector::findMarkers(const cv::Mat & image, shared_ptr camera) { - if (!camera) { + const std::vector & Detector::findMarkers(const cv::Mat & image + , const cv::Mat & cameraMatrix + , const cv::Mat & distortionCoefficients) { + this->lastDetection = { image + , cameraMatrix + , distortionCoefficients + }; + + if (cameraMatrix.empty()) { this->foundMarkers = this->markerDetector.detect(image); } else { - aruco::CameraParameters cameraParameters(camera->getCameraMatrix(), camera->getDistortionCoefficients(), camera->getSize()); + aruco::CameraParameters cameraParameters(cameraMatrix + , distortionCoefficients + , cv::Size(image.cols, image.rows)); this->foundMarkers = this->markerDetector.detect(image, cameraParameters, this->parameters.markerLength); } auto thresholdedImage = this->markerDetector.getThresholdedImage(); @@ -159,7 +182,6 @@ namespace ofxRulr { this->dictionary = aruco::Dictionary::loadPredefined(this->dictionaryType); this->markerDetector.setDictionary(this->dictionaryType); - this->cachedDetector = this->parameters.detectorType; //setup refinement method { @@ -181,6 +203,27 @@ namespace ofxRulr { } this->cachedMarkerImages.clear(); + this->detectorDirty = false; + } + + //---------- + void Detector::changeDetectorCallback(DetectorType &) { + this->detectorDirty = true; + } + + //---------- + void Detector::changeFloatCallback(float &) { + this->detectorDirty = true; + } + + //---------- + void Detector::changeIntCallback(int &) { + this->detectorDirty = true; + } + + //---------- + void Detector::changeRefinementTypeCallback(RefinementType &){ + this->detectorDirty = true; } } } diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h index 09209fcb2d..dc4e7fe7e4 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/Detector.h @@ -28,7 +28,9 @@ namespace ofxRulr { ofxCvGui::PanelPtr getPanel() override; - const vector & findMarkers(const cv::Mat &, shared_ptr camera = nullptr); + const vector & findMarkers(const cv::Mat & image + , const cv::Mat & cameraMatrix = cv::Mat() + , const cv::Mat & distortionCoefficients = cv::Mat()); protected: MAKE_ENUM(DetectorType , (Original, MIP_3612h, ARTKP, ARTAG) @@ -40,6 +42,11 @@ namespace ofxRulr { void rebuildDetector(); + void changeDetectorCallback(DetectorType &); + void changeFloatCallback(float &); + void changeIntCallback(int &); + void changeRefinementTypeCallback(RefinementType &); + struct : ofParameterGroup { ofParameter detectorType{ "Detector type", DetectorType::Original }; ofParameter markerLength{ "Marker length [m]", 0.05, 0.001, 10 }; @@ -63,14 +70,21 @@ namespace ofxRulr { aruco::Dictionary dictionary; aruco::Dictionary::DICT_TYPES dictionaryType; aruco::MarkerDetector markerDetector; - DetectorType cachedDetector; - RefinementType cachedRefinement; map> cachedMarkerImages; + //useful when debugging to research quickly + struct { + cv::Mat image; + cv::Mat cameraMatrix; + cv::Mat distortionCoefficients; + } lastDetection; + ofImage preview; vector foundMarkers; ofxCvGui::PanelPtr panel; + + bool detectorDirty = true; }; } } diff --git a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.cpp b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.cpp index 1b0c59b490..5f86bde191 100644 --- a/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.cpp +++ b/Plugin_ArUco/src/ofxRulr/Nodes/ArUco/FindMarkers.cpp @@ -71,7 +71,9 @@ namespace ofxRulr { auto cameraMatrix = cameraNode->getCameraMatrix(); auto distortionCoefficients = cameraNode->getDistortionCoefficients(); - this->rawMarkers = detectorNode->findMarkers(image, cameraNode); + this->rawMarkers = detectorNode->findMarkers(image + , cameraNode->getCameraMatrix() + , cameraNode->getDistortionCoefficients()); //update preview image { diff --git a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.cpp b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.cpp index 1205e419d4..1ae129a3b1 100644 --- a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.cpp +++ b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.cpp @@ -14,6 +14,140 @@ namespace ofxRulr { namespace Nodes { namespace Procedure { namespace Calibrate { +#pragma mark Capture + //---------- + ProjectorFromGraycode::Capture::Capture() { + RULR_NODE_SERIALIZATION_LISTENERS; + } + + //---------- + string ProjectorFromGraycode::Capture::getDisplayString() const { + stringstream ss; + ss << this->worldPoints.size() << " points"; + return ss.str(); + } + + //---------- + void ProjectorFromGraycode::Capture::serialize(Json::Value & json) { + json["worldPoints"] << this->worldPoints; + json["projectorImagePoints"] << this->projectorImagePoints; + } + + //---------- + void ProjectorFromGraycode::Capture::deserialize(const Json::Value & json) { + json["worldPoints"] >> this->worldPoints; + json["projectorImagePoints"] >> this->projectorImagePoints; + } + + //---------- + void ProjectorFromGraycode::Capture::drawWorld(shared_ptr projector) + { + ofMesh points; + points.setMode(ofPrimitiveMode::OF_PRIMITIVE_POINTS); + + ofMesh zigzag; + zigzag.setMode(ofPrimitiveMode::OF_PRIMITIVE_LINE_STRIP); + + for (int i = 0; i < this->worldPoints.size(); i++) { + const auto & worldPoint = this->worldPoints[i]; + if (i == 0) { + ofPushStyle(); + { + ofSetColor(this->color); + ofDrawSphere(worldPoint, 0.05f); + } + ofPopStyle(); + } + + zigzag.addVertex(worldPoint); + zigzag.addColor((float)i / (float)this->worldPoints.size()); + } + zigzag.draw(); + + Utils::Graphics::pushPointSize(5.0f); + { + points.drawVertices(); + } + Utils::Graphics::popPointSize(); + + //draw transform axes + ofPushMatrix(); + { + ofMultMatrix(this->transform); + ofDrawAxis(0.1f); + } + ofPopMatrix(); + + //draw lines to projector + if (projector) { + const auto projectorWidth = projector->getWidth(); + const auto projectorHeight = projector->getHeight(); + const auto projectorPosition = projector->getPosition(); + const auto projectorView = projector->getViewInWorldSpace(); + + ofMesh projectorRays; + projectorRays.setMode(ofPrimitiveMode::OF_PRIMITIVE_TRIANGLES); + + for (int i = 0; i < this->worldPoints.size(); i++) { + const auto & worldPoint = this->worldPoints[i]; + auto color = ofFloatColor( + this->projectorImagePoints[i].x / projector->getWidth(), + this->projectorImagePoints[i].y / projector->getHeight(), + 0 + ); + points.addVertex(worldPoint); + points.addColor(color); + + auto distance = worldPoint.distance(projectorPosition); + auto projectorRay = projectorView.castPixel(this->projectorImagePoints[i]); + projectorRays.addVertex(projectorRay.s); + projectorRays.addVertex(projectorRay.s + projectorRay.t * distance / projectorRay.t.length()); + projectorRays.addVertex(worldPoint); + + projectorRays.addColor(ofFloatColor(0.5, 0.5, 0.5, 0.0f)); + color.a = 0.5f; + projectorRays.addColor(color); + color.a = 1.0f; + projectorRays.addColor(color); + } + + ofPushStyle(); + { + ofEnableAlphaBlending(); + projectorRays.drawWireframe(); + ofDisableAlphaBlending(); + } + ofPopStyle(); + } + else { + for (int i = 0; i < this->worldPoints.size(); i++) { + const auto & worldPoint = this->worldPoints[i]; + points.addVertex(worldPoint); + } + } + } + + //---------- + void ProjectorFromGraycode::Capture::drawOnCameraImage() { + ofPushStyle(); + { + ofSetColor(this->color); + ofxCv::drawCorners(ofxCv::toCv(this->cameraImagePoints), false); + } + ofPopStyle(); + } + + //---------- + void ProjectorFromGraycode::Capture::drawOnProjectorImage() { + ofPushStyle(); + { + ofSetColor(this->color); + ofxCv::drawCorners(ofxCv::toCv(this->projectorImagePoints), false); + } + ofPopStyle(); + } + +#pragma mark ProjectorFromGraycode //---------- ProjectorFromGraycode::ProjectorFromGraycode() { RULR_NODE_INIT_LISTENER; @@ -31,6 +165,7 @@ namespace ofxRulr { //---------- void ProjectorFromGraycode::init() { + RULR_NODE_UPDATE_LISTENER; RULR_NODE_DRAW_WORLD_LISTENER; RULR_NODE_INSPECTOR_LISTENER; RULR_NODE_SERIALIZATION_LISTENERS; @@ -40,58 +175,77 @@ namespace ofxRulr { this->addInput(); this->addInput(); - this->panel = ofxCvGui::Panels::makeTexture(this->preview.getTexture()); + { + auto panel = ofxCvGui::Panels::Groups::makeStrip(); + + { + auto cameraPanel = ofxCvGui::Panels::makeTexture(this->preview.projectorInCamera, "Camera"); + cameraPanel->onDrawImage += [this](ofxCvGui::DrawImageArguments &) { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawOnCameraImage(); + } + }; + panel->add(cameraPanel); + } + { + auto projectorPanel = ofxCvGui::Panels::makeTexture(this->preview.cameraInProjector, "Projector"); + projectorPanel->onDrawImage += [this](ofxCvGui::DrawImageArguments &) { + auto captures = this->captures.getSelection(); + for (auto capture : captures) { + capture->drawOnProjectorImage(); + } + }; + panel->add(projectorPanel); + } + this->panel = panel; + } + } + + //---------- + void ProjectorFromGraycode::update() { + { + auto projector = this->getInput(); + if (projector) { + auto width = this->preview.cameraInProjector.getWidth(); + auto height = this->preview.cameraInProjector.getHeight(); + if (width != projector->getWidth() + || height != projector->getHeight()) { + this->preview.cameraInProjector.allocate(projector->getWidth() + , projector->getHeight() + , GL_LUMINANCE); + } + } + } + { + auto camera = this->getInput(); + if (camera) { + auto width = this->preview.projectorInCamera.getWidth(); + auto height = this->preview.projectorInCamera.getHeight(); + if (width != camera->getWidth() + || height != camera->getHeight()) { + this->preview.projectorInCamera.allocate(camera->getWidth() + , camera->getHeight() + , GL_LUMINANCE); + } + } + } } //---------- void ProjectorFromGraycode::drawWorldStage() { - ofMesh preview; - preview.setMode(ofPrimitiveMode::OF_PRIMITIVE_POINTS); - auto projectorNode = this->getInput(); - bool useColor = false; - float projectorWidth, projectorHeight; + float projectorWidth = 0, projectorHeight = 0; if (projectorNode) { - useColor = true; projectorWidth = projectorNode->getWidth(); projectorHeight = projectorNode->getHeight(); } - auto selection = this->getSelection(); - - for (auto index : selection) { - ofMesh line; - auto & capture = this->captures[index]; - line.setMode(ofPrimitiveMode::OF_PRIMITIVE_LINE_STRIP); - - for (int i = 0; i < capture.worldPoints.size(); i++) { - const auto & worldPoint = capture.worldPoints[i]; - if (i == 0) { - ofDrawBitmapString(ofToString(index), worldPoint); - } - preview.addVertex(worldPoint); - line.addVertex(worldPoint); - line.addColor((float)i / (float)capture.worldPoints.size()); - } - - for (auto & projectorImagePoint : capture.projectorImagePoints) { - if (useColor) { - preview.addColor(ofColor( - ofMap(projectorImagePoint.x, 0, projectorWidth, 0, 255), - ofMap(projectorImagePoint.y, 0, projectorHeight, 0, 255), - 0)); - } - } - - line.draw(); - } - - Utils::Graphics::pushPointSize(5.0f); - { - preview.drawVertices(); + auto captures = this->captures.getSelection(); + for (auto & capture : captures) { + capture->drawWorld(projectorNode); } - Utils::Graphics::popPointSize(); } //---------- @@ -116,6 +270,7 @@ namespace ofxRulr { auto projectorNode = this->getInput(); if (this->parameters.capture.autoScan) { + Utils::ScopedProcess scopedProcess("Graycode scan", false); graycodeNode->runScan(); } @@ -137,71 +292,13 @@ namespace ofxRulr { ofPixels medianCopy(median); auto medianCopyMat = toCv(medianCopy); - if (this->parameters.capture.searchBrightArea) { - vector> contours; - - //crop the image down to active area - cv::Rect bounds; - cv::Mat croppedImage; - { - //threshold the image - cv::Mat thresholded; - cv::threshold(medianCopyMat, thresholded, this->parameters.capture.brightAreaThreshold, 255, THRESH_BINARY); - - //find contours - cv::findContours(thresholded, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); - - //add all contours into one set of points - vector contoursCollapsed; - for (auto & contour : contours) { - auto contourBounds = cv::boundingRect(cv::Mat(contour)); - if (contourBounds.area() < 25) { - continue; - } - for (auto & point : contour) { - contoursCollapsed.push_back(point); - } - } - if (contoursCollapsed.size() > 0) { - bounds = cv::boundingRect(cv::Mat(contoursCollapsed)); - croppedImage = medianCopyMat(bounds); - } - else { - croppedImage = medianCopyMat; - } - } - - ofxCv::copy(croppedImage, this->preview.getPixels()); - this->preview.update(); - - //find checkerboard in cropped image - if (!boardNode->findBoard(croppedImage - , pointsInCameraImage - , boardObjectPoints - , this->parameters.capture.findBoardMode - , cameraNode->getCameraMatrix() - , cameraNode->getDistortionCoefficients())) { - throw(ofxRulr::Exception("Board not found in camera image")); - } - - //relocate points back into non-cropped space - for (auto & point : pointsInCameraImage) { - point.x += bounds.x; - point.y += bounds.y; - } - } - else { - ofxCv::copy(medianCopyMat, this->preview.getPixels()); - this->preview.update(); - - if (!boardNode->findBoard(medianCopyMat - , pointsInCameraImage - , boardObjectPoints - , this->parameters.capture.findBoardMode - , cameraNode->getCameraMatrix() - , cameraNode->getDistortionCoefficients())) { - throw(ofxRulr::Exception("Board not found in camera image")); - } + if (!boardNode->findBoard(medianCopyMat + , pointsInCameraImage + , boardObjectPoints + , this->parameters.capture.findBoardMode + , cameraNode->getCameraMatrix() + , cameraNode->getDistortionCoefficients())) { + throw(ofxRulr::Exception("Board not found in camera image")); } } @@ -217,9 +314,7 @@ namespace ofxRulr { , cameraNode->getDistortionCoefficients() , rotation , translation - , false - , 1.0f - , boardObjectPoints.size() / 2 * 3); + , false); } else { cv::solvePnP(boardObjectPoints @@ -237,7 +332,7 @@ namespace ofxRulr { vector boardPointsInWorldSpace; { for (auto & boardObjectPoint : boardObjectPoints) { - boardPointsInWorldSpace.push_back(toOf(boardObjectPoint) * boardTransform * cameraNode->getTransform()); + boardPointsInWorldSpace.push_back(toOf(boardObjectPoint) * (boardTransform * cameraNode->getTransform())); } } @@ -246,30 +341,22 @@ namespace ofxRulr { { Utils::ScopedProcess scopedProcessBuildMesh("Build delauney mesh", false); auto & projectorInCameraPreview = graycodeNode->getDecoder().getProjectorInCamera().getPixels(); + auto boardBoundsInCamera = cv::boundingRect(Mat(pointsInCameraImage)); + //erode away the active (to reduce noise) auto activeEroded = dataSet.getActive(); auto activeErodedMat = toCv(activeEroded); { - auto boardBounds = cv::boundingRect(Mat(pointsInCameraImage)); - ofVec2f boardSizeInCamera(boardBounds.width, boardBounds.height); + ofVec2f boardSizeInCamera(boardBoundsInCamera.width, boardBoundsInCamera.height); float erosionSize = boardSizeInCamera.length() * this->parameters.capture.erosion; cv::erode(activeErodedMat, activeErodedMat, cv::Mat(), cv::Point(-1, -1), (int)erosionSize); } - //show preview of eroded - { - Mat copy; - toCv(projectorInCameraPreview).copyTo(copy, activeErodedMat); - ofxCv::copy(copy, this->preview.getPixels()); - this->preview.update(); - } - - - //make vertices and tex coords Delaunay::Point tempP; vector delauneyPoints; auto activeErodedPixel = activeEroded.getData(); + auto maskInCameraSpace = ofxCv::toOf(boardBoundsInCamera); for (auto & pixel : dataSet) { if (!*activeErodedPixel++) { continue; @@ -277,12 +364,22 @@ namespace ofxRulr { auto projectorXY = pixel.getProjectorXY(); auto cameraXY = pixel.getCameraXY(); + if (!maskInCameraSpace.inside(cameraXY)) { + continue; + } delauneyPoints.emplace_back(cameraXY.x, cameraXY.y); projectorInCameraMesh.addVertex(cameraXY); projectorInCameraMesh.addColor(ofFloatColor(projectorXY.x, projectorXY.y, 0)); } + //check delauney point count + if (delauneyPoints.size() > this->parameters.capture.maximumDelauneyPoints) { + stringstream message; + message << "Too many points found in scan (" << delauneyPoints.size() << " found. Maximum is " << this->parameters.capture.maximumDelauneyPoints.get() << ")"; + throw(ofxRulr::Exception(message.str())); + } + //triangulate auto delauney = make_shared(delauneyPoints); delauney->Triangulate(); @@ -331,8 +428,8 @@ namespace ofxRulr { } //read all projector pixel positions and add working points into capture - Capture capture; { + auto capture = make_shared(); auto worldPoint = boardPointsInWorldSpace.begin(); for (auto & cameraPoint : pointsInCameraImage) { @@ -344,28 +441,20 @@ namespace ofxRulr { continue; } - capture.projectorImagePoints.push_back(toOf((cv::Point2f) projectorPoint)); - capture.worldPoints.push_back(*worldPoint++); + capture->cameraImagePoints.push_back(toOf(cameraPoint)); + capture->projectorImagePoints.push_back(toOf((cv::Point2f) projectorPoint)); + capture->worldPoints.push_back(*worldPoint++); } + capture->transform = boardTransform; + this->captures.add(capture); } - this->captures.push_back(capture); - - scopedProcess.end(); - } - - //---------- - void ProjectorFromGraycode::deleteLastCapture() { - if (!this->captures.empty()) { - this->captures.pop_back(); + //make previews + { + this->preview.projectorInCamera.loadData(dataSet.getMedian()); + this->preview.cameraInProjector.loadData(dataSet.getMedianInverse()); } - this->preview.clear(); - } - - //---------- - void ProjectorFromGraycode::clearCaptures() { - this->captures.clear(); - this->preview.clear(); + scopedProcess.end(); } //---------- @@ -381,23 +470,26 @@ namespace ofxRulr { vector worldPoints; vector projectorImagePoints; - auto selection = this->getSelection(); - for (auto index : selection) { - auto & capture = this->captures[index]; - + auto selection = this->captures.getSelection(); + for (auto capture : selection) { worldPoints.insert(worldPoints.end() - , capture.worldPoints.begin() - , capture.worldPoints.end()); + , capture->worldPoints.begin() + , capture->worldPoints.end()); projectorImagePoints.insert(projectorImagePoints.end() - , capture.projectorImagePoints.begin() - , capture.projectorImagePoints.end()); + , capture->projectorImagePoints.begin() + , capture->projectorImagePoints.end()); } cv::Mat cameraMatrix, rotation, translation; - this->error = ofxCv::calibrateProjector(cameraMatrix, rotation, translation, - worldPoints, projectorImagePoints, - this->getInput()->getWidth(), this->getInput()->getHeight(), - false, - 0.5f, 1.4f); + this->error = ofxCv::calibrateProjector(cameraMatrix + , rotation + , translation + , worldPoints + , projectorImagePoints + , this->getInput()->getWidth() + , this->getInput()->getHeight() + , false + , 0.5f + , 1.4f); auto view = ofxCv::makeMatrix(rotation, translation); projectorNode->setTransform(view.getInverse()); @@ -414,20 +506,9 @@ namespace ofxRulr { try { this->addCapture(); } - RULR_CATCH_ALL_TO_ERROR; + RULR_CATCH_ALL_TO_ALERT; }, ' '); - inspector->addButton("Delete last capture", [this]() { - this->deleteLastCapture(); - }); - inspector->addButton("Delete selection", [this]() { - this->deleteSelection(); - }); - inspector->addButton("Clear captures", [this]() { - this->clearCaptures(); - }); - inspector->addLiveValue("Capture count", [this]() { - return this->captures.size(); - }); + this->captures.populateWidgets(inspector); inspector->addButton("Calibrate", [this]() { try { this->calibrate(); @@ -443,81 +524,14 @@ namespace ofxRulr { //---------- void ProjectorFromGraycode::serialize(Json::Value & json) { Utils::Serializable::serialize(json, this->parameters); - - auto & jsonCaptures = json["captures"]; - for (int i = 0; i < this->captures.size(); i++) { - auto & capture = this->captures[i]; - auto & jsonCapture = jsonCaptures[i]; - - for (int j = 0; j < capture.worldPoints.size(); j++) { - auto & jsonCorrespondence = jsonCapture[j]; - jsonCorrespondence["worldPoint"] << capture.worldPoints[j]; - jsonCorrespondence["projectorImagePoint"] << capture.projectorImagePoints[j]; - } - } + this->captures.serialize(json["captures"]); } //---------- void ProjectorFromGraycode::deserialize(const Json::Value & json) { Utils::Serializable::deserialize(json, this->parameters); - - this->captures.clear(); - const auto & jsonCaptures = json["captures"]; - for (const auto & jsonCapture : jsonCaptures) { - Capture capture; - int pointIndex = 0; - for (const auto & jsonCorrespondence : jsonCapture) { - ofVec3f worldPoint; - ofVec2f projectorImagePoint; - jsonCorrespondence["worldPoint"] >> worldPoint; - jsonCorrespondence["projectorImagePoint"] >> projectorImagePoint; - capture.worldPoints.push_back(worldPoint); - capture.projectorImagePoints.push_back(projectorImagePoint); - } - this->captures.push_back(move(capture)); - } + this->captures.deserialize(json["captures"]); } - - //---------- - vector ProjectorFromGraycode::getSelection() const { - auto selectionString = this->parameters.selection.get(); - auto selectionItemStrings = ofSplitString(selectionString, ",", true, true); - - vector selectionInts; - for (auto & selectionStringItem : selectionItemStrings) { - if (selectionStringItem.empty()) { - continue; - } - auto value = ofToInt(selectionStringItem); - if (value >= 0 && value < this->captures.size()) { - selectionInts.push_back(value); - } - } - - if (selectionInts.empty()) { - for (int i = 0; i < this->captures.size(); i++) { - selectionInts.push_back(i); - } - } - - return selectionInts; - } - - //---------- - void ProjectorFromGraycode::deleteSelection() { - int index = 0; - auto selection = this->getSelection(); - for (auto it = this->captures.begin(); it != this->captures.end(); index++) { - if (find(selection.begin(), selection.end(), index) != selection.end()) { - it = this->captures.erase(it); - } - else { - it++; - } - } - this->parameters.selection = ""; - } - } } } diff --git a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.h b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.h index d7e0ef53ba..189ef8b993 100644 --- a/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.h +++ b/Plugin_Calibrate/src/ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromGraycode.h @@ -4,6 +4,8 @@ #include "ofxRulr/Nodes/Item/AbstractBoard.h" #include "ofxRulr/Nodes/Item/AbstractBoard.h" +#include "ofxRulr/Nodes/Item/Projector.h" +#include "ofxRulr/Utils/CaptureSet.h" #include "Constants_Plugin_Calibration.h" namespace ofxRulr { @@ -12,9 +14,21 @@ namespace ofxRulr { namespace Calibrate { class PLUGIN_CALIBRATION_EXPORTS ProjectorFromGraycode : public Base { public: - struct Capture { + class Capture : public Utils::AbstractCaptureSet::BaseCapture { + public: + Capture(); + string getDisplayString() const override; vector worldPoints; + vector cameraImagePoints; vector projectorImagePoints; + ofMatrix4x4 transform; + + void serialize(Json::Value &); + void deserialize(const Json::Value &); + void drawWorld(shared_ptr); + + void drawOnCameraImage(); + void drawOnProjectorImage(); }; ProjectorFromGraycode(); @@ -22,40 +36,47 @@ namespace ofxRulr { ofxCvGui::PanelPtr getPanel() override; void init(); + void update(); void drawWorldStage(); void addCapture(); - void deleteLastCapture(); - void clearCaptures(); void calibrate(); void populateInspector(ofxCvGui::InspectArguments &); void serialize(Json::Value &); void deserialize(const Json::Value &); - - vector getSelection() const; - void deleteSelection(); protected: ofxCvGui::PanelPtr panel; struct : ofParameterGroup { struct : ofParameterGroup { ofParameter autoScan{ "Automatically scan", true }; - ofParameter searchBrightArea{ "Search bright area", true }; ofParameter brightAreaThreshold{ "Bright area threshold", 64, 0, 255 }; + ofParameter maximumDelauneyPoints{"Maximum delauney points", 100000 }; ofParameter findBoardMode{ "Find board mode", FindBoardMode::Optimized }; - ofParameter useRansacForSolvePnp{ "Use RANSAC for SolvePNP", true }; + ofParameter useRansacForSolvePnp{ "Use RANSAC for SolvePNP", false }; ofParameter erosion{ "Erosion (/Board size)", 0.02f, 0.0f, 0.1f }; ofParameter maxTriangleArea{ "Max triangle area", 100*100 }; - PARAM_DECLARE("Capture", autoScan, searchBrightArea, brightAreaThreshold, findBoardMode, erosion, maxTriangleArea); + PARAM_DECLARE("Capture" + , autoScan + , brightAreaThreshold + , maximumDelauneyPoints + , findBoardMode + , useRansacForSolvePnp + , erosion + , maxTriangleArea); } capture; ofParameter selection{ "Selection", "" }; PARAM_DECLARE("ProjectorFromGraycode", capture, selection); } parameters; - vector captures; - ofFloatImage preview; + Utils::CaptureSet captures; float error = 0.0f; + + struct { + ofTexture projectorInCamera; + ofTexture cameraInProjector; + } preview; }; } } diff --git a/Plugin_Calibrate/src/pch_Plugin_Calibrate.h b/Plugin_Calibrate/src/pch_Plugin_Calibrate.h index e153245859..1decda201c 100644 --- a/Plugin_Calibrate/src/pch_Plugin_Calibrate.h +++ b/Plugin_Calibrate/src/pch_Plugin_Calibrate.h @@ -17,3 +17,5 @@ #include "ofxRulr/Nodes/Procedure/Calibrate/ProjectorFromStereoCameras.h" #include "ofxRulr/Nodes/Procedure/Calibrate/StereoCalibrate.h" #include "ofxRulr/Nodes/Procedure/Calibrate/ViewToVertices.h" + +#include "ofxCvMin.h" diff --git a/Plugin_SLS/Plugin_SLS.vcxproj b/Plugin_SLS/Plugin_SLS.vcxproj new file mode 100644 index 0000000000..cd12c54032 --- /dev/null +++ b/Plugin_SLS/Plugin_SLS.vcxproj @@ -0,0 +1,90 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {651FDD8B-6C0C-4A82-89ED-182CFF129D03} + ofxRulr::Nodes::SLS + 8.1 + Plugin_SLS + + + + DynamicLibrary + true + v140 + MultiByte + + + DynamicLibrary + false + v140 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + Use + pch_Plugin_SLS.h + ProgramDatabase + + + true + + + + + + Level3 + MaxSpeed + true + true + true + Use + pch_Plugin_SLS.h + + + true + true + true + + + + + + + Create + Create + + + + + + + + + + + \ No newline at end of file diff --git a/Plugin_SLS/Plugin_SLS.vcxproj.filters b/Plugin_SLS/Plugin_SLS.vcxproj.filters new file mode 100644 index 0000000000..85ef5c4da7 --- /dev/null +++ b/Plugin_SLS/Plugin_SLS.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + src + + + src + + + src\ofxRulr\Nodes\SLS + + + + + {cbc2aa11-f629-44f1-8a35-3e1941a95761} + + + {689a618a-7684-4c7e-84fa-d23515db1316} + + + {41612ea4-ecf3-47f3-b62f-e7584aaf14eb} + + + {03cf3a80-4631-4119-a58a-ace92a56810f} + + + + + src + + + src\ofxRulr\Nodes\SLS + + + \ No newline at end of file diff --git a/Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.cpp b/Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.cpp new file mode 100644 index 0000000000..250594020a --- /dev/null +++ b/Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.cpp @@ -0,0 +1,211 @@ +#include "pch_Plugin_SLS.h" + +namespace ofxRulr { + namespace Nodes { + namespace SLS { + //---------- + Scan::Scan() { + RULR_NODE_INIT_LISTENER; + } + + //---------- + std::string Scan::getTypeName() const { + return "SLS::Scan"; + } + + //---------- + void Scan::init() { + this->addInput(); + this->addInput(); + } + + //---------- + shared_ptr Scan::capture() { + this->throwIfMissingAnyConnection(); + + auto camera = this->getInput(); + auto videoOutput = this->getInput(); + + auto grabber = camera->getGrabber(); + if (!grabber) { + throw(ofxRulr::Exception("Cannot scan without a grabber")); + } + + auto suite = this->getSuite(); + auto dataSet = make_shared(); + dataSet->cameraWidth = (int)grabber->getWidth(); + dataSet->cameraHeight = (int)grabber->getHeight(); + + //capture the projected scan patterns + { + //clear the output + this->message.allocate(suite->params.width + , suite->params.height + , ofImageType::OF_IMAGE_GRAYSCALE); + + ofHideCursor(); + + try { + auto patternCount = suite->pattern->getNumberOfPatternImages(); + auto totalFrameCount = patternCount + 2; + Utils::ScopedProcess scopedProcess("Scanning graycode" + , true + , totalFrameCount); + + for (int iFrame = 0; iFrame < totalFrameCount; iFrame++) { + Utils::ScopedProcess frameScopedProcess("Scanning frame", false); + + cv::Mat * input; + cv::Mat * output; + + if (iFrame == 0) { + //white + input = &suite->whiteProjector; + output = &dataSet->whiteCamera; + } + else if (iFrame == 1) { + //black + input = &suite->blackProjector; + output = &dataSet->blackCamera; + } + else { + //data frame + input = &suite->patternImages[iFrame - 2]; + output = &dataSet->captures[iFrame - 2]; + } + + //copy image to message texture + { + ofxCv::copy(*input, message.getPixels()); + message.update(); + } + +#ifdef TARGET_OSX + //see notes on Graycode node + for (int iOSXRedo = 0; iOSXRedo < 2; iOSXRedo++) { +#endif + for (int iFlush = 0; iFlush < this->parameters.scan.flushOutputFrames + 1; iFlush++) { + videoOutput->clearFbo(false); + videoOutput->begin(); + { + ofPushStyle(); + { + auto brightness = this->parameters.scan.brightness; + ofSetColor(brightness * 255.0f); + this->message.draw(0, 0); + } + ofPopStyle(); + } + videoOutput->end(); + videoOutput->presentFbo(); + } + + auto startWait = ofGetElapsedTimeMillis(); + while (ofGetElapsedTimeMillis() - startWait < this->parameters.scan.captureDelay) { + ofSleepMillis(1); + grabber->update(); + } + + for (int iFlush = 0; iFlush < this->parameters.scan.flushInputFrames; iFlush++) { + grabber->getFreshFrame(); + } +#ifdef TARGET_OSX + } +#endif + auto frame = grabber->getFreshFrame(); + if (!frame) { + throw(ofxRulr::Exception("Couldn't get fresh frame from camera")); + } + ofxCv::copy(frame->getPixels(), *output); + } + scopedProcess.end(); + } + RULR_CATCH_ALL_TO_ALERT + catch (...) { + } + ofShowCursor(); + } + + dataSet->suite = suite; + return dataSet; + } + + //---------- + void Scan::decode(shared_ptr dataSet) { + //decode the scan + { + auto dataSet = make_shared(); + + //calculate projector pixel indices + { + dataSet->projectorXYInCamera.allocate(dataSet->cameraWidth + , dataSet->cameraHeight + , 2); + + for (int j = 0; j < dataSet->cameraHeight; j++) { + auto line = dataSet->projectorXYInCamera.getLine(j); + auto output = line.begin(); + for (int i = 0; i < dataSet->cameraWidth; i++) { + cv::Point2i projectorPixel; + dataSet->suite->pattern->getProjPixel( + dataSet->captures + , i + , j + , projectorPixel); + *output++ = projectorPixel.x; + *output++ = projectorPixel.y; + } + } + } + + //calculate the mask + { + dataSet->mask.allocate(dataSet->cameraWidth + , dataSet->cameraHeight + , OF_IMAGE_GRAYSCALE); + + int whiteThreshold = this->parameters.threshold.white; + int blackThreshold = this->parameters.threshold.black; + + //from https://github.com/opencv/opencv_contrib/blob/master/modules/structured_light/src/graycodepattern.cpp#L344 + + auto output = (uint8_t*)dataSet->mask.getData(); + auto white = (uint8_t*)dataSet->whiteCamera.data; + auto black = (uint8_t*)dataSet->blackCamera.data; + + size_t size = dataSet->cameraWidth * dataSet->cameraHeight; + for (size_t i = 0; i < size; i++) { + *output++ = *white++ - *black++ > blackThreshold; + } + } + } + } + + //---------- + void Scan::scan() { + auto dataSet = this->capture(); + decode(dataSet); + } + + //---------- + shared_ptr Scan::getSuite() { + this->throwIfMissingAConnection(); + auto videoOutput = this->getInput(); + if (!videoOutput->isWindowOpen()) { + throw(ofxRulr::Exception("")); + } + + + auto suite = make_shared(); + suite->params.width = videoOutput->getWidth(); + suite->params.height = videoOutput->getHeight(); + + auto graycode = cv::structured_light::GrayCodePattern::create(suite->params); + graycode->generate(suite->patternImages); + graycode->getImagesForShadowMasks(suite->blackProjector, suite->whiteProjector); + + return suite; + } + } + } +} \ No newline at end of file diff --git a/Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.h b/Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.h new file mode 100644 index 0000000000..369a932df3 --- /dev/null +++ b/Plugin_SLS/src/ofxRulr/Nodes/SLS/Scan.h @@ -0,0 +1,64 @@ +#pragma once + +#include "pch_Plugin_SLS.h" + +namespace ofxRulr { + namespace Nodes { + namespace SLS { + class Scan : public ofxRulr::Nodes::Base { + public: + struct Suite { + cv::structured_light::GrayCodePattern::Params params; + cv::Ptr pattern; + vector patternImages; + cv::Mat whiteProjector; + cv::Mat blackProjector; + }; + + struct DataSet { + shared_ptr suite; + + int cameraWidth; + int cameraHeight; + vector captures; + cv::Mat whiteCamera; + cv::Mat blackCamera; + + ofShortPixels projectorXYInCamera; + ofPixels mask; + }; + + Scan(); + string getTypeName() const override; + void init(); + + shared_ptr capture(); + void decode(shared_ptr); + + void scan(); + shared_ptr getSuite(); // throws ofxRulr::Exception + protected: + struct : ofParameterGroup { + struct : ofParameterGroup { + ofParameter brightness{ "Brightness", 1, 0, 1 }; + ofParameter flushOutputFrames{ "Flush output frames", 2 }; + ofParameter flushInputFrames{ "Flush input frames", 0 }; + ofParameter captureDelay{ "Capture delay [ms]", 100 }; + PARAM_DECLARE("Scan", flushOutputFrames, flushInputFrames, captureDelay); + } scan; + + struct : ofParameterGroup { + ofParameter white{ "White", 20 }; + ofParameter black{ "Black", 5 }; + PARAM_DECLARE("Threshold", white, black); + } threshold; + + PARAM_DECLARE("Scan", scan, threshold); + } parameters; + + shared_ptr dataSet; + ofImage message; + }; + } + } +} \ No newline at end of file diff --git a/Plugin_SLS/src/pch_Plugin_SLS.cpp b/Plugin_SLS/src/pch_Plugin_SLS.cpp new file mode 100644 index 0000000000..e3eaa1d41f --- /dev/null +++ b/Plugin_SLS/src/pch_Plugin_SLS.cpp @@ -0,0 +1 @@ +#include "pch_Plugin_SLS.h" \ No newline at end of file diff --git a/Plugin_SLS/src/pch_Plugin_SLS.h b/Plugin_SLS/src/pch_Plugin_SLS.h new file mode 100644 index 0000000000..62f2b254be --- /dev/null +++ b/Plugin_SLS/src/pch_Plugin_SLS.h @@ -0,0 +1,13 @@ +#pragma once + +#include "pch_RulrNodes.h" +#include "pch_RulrCore.h" +#include "ofxPlugin.h" + +#include + +#include "ofxRulr.h" +#include "ofxRulr/Nodes/Item/Camera.h" +#include "ofxRulr/Nodes/System/VideoOutput.h" + +#include "ofxRulr/Nodes/SLS/Scan.h" diff --git a/Plugin_SLS/src/plugin.cpp b/Plugin_SLS/src/plugin.cpp new file mode 100644 index 0000000000..1e064ca868 --- /dev/null +++ b/Plugin_SLS/src/plugin.cpp @@ -0,0 +1,8 @@ +#include "pch_Plugin_SLS.h" + + +OFXPLUGIN_PLUGIN_MODULES_BEGIN(ofxRulr::Nodes::Base) +{ + OFXPLUGIN_PLUGIN_REGISTER_MODULE(ofxRulr::Nodes::SLS::Scan); +} +OFXPLUGIN_PLUGIN_MODULES_END \ No newline at end of file diff --git a/RulrLibrary/RulrLibrary_Debug.def b/RulrLibrary/RulrLibrary_Debug.def index 9e6330a7e8..61956bdcee 100644 --- a/RulrLibrary/RulrLibrary_Debug.def +++ b/RulrLibrary/RulrLibrary_Debug.def @@ -31,8 +31,6 @@ EXPORTS ??0ofRectangle@@QEAA@AEBV0@@Z ??0ofRectangle@@QEAA@MMMM@Z ??0ofSerial@@QEAA@XZ - ??0ofTexture@@QEAA@$$QEAV0@@Z - ??0ofTexture@@QEAA@AEBV0@@Z ??0ofTexture@@QEAA@XZ ??0ofVbo@@QEAA@AEBV0@@Z ??0ofVbo@@QEAA@XZ @@ -52,8 +50,6 @@ EXPORTS ??1ofVboMesh@@UEAA@XZ ??1ofxOscBundle@@QEAA@XZ ??1ofxOscMessage@@QEAA@XZ - ??4ofTexture@@QEAAAEAV0@$$QEAV0@@Z - ??4ofTexture@@QEAAAEAV0@AEBV0@@Z ??9tpp@@YA_NAEBVfIterator@Delaunay@0@0@Z ?ofCircle@@YAXAEBVofVec3f@@M@Z ?ofCircle@@YAXMMM@Z @@ -63,6 +59,7 @@ EXPORTS ?ofCreateWindow@@YA?AV?$shared_ptr@VofAppBaseWindow@@@std@@AEBVofWindowSettings@@@Z ?ofDisableAlphaBlending@@YAXXZ ?ofDisableBlendMode@@YAXXZ + ?ofDrawAxis@@YAXM@Z ?ofDrawCircle@@YAXAEBVofVec3f@@M@Z ?ofDrawCircle@@YAXMMM@Z ?ofDrawGridPlane@@YAXM_K_N@Z @@ -154,30 +151,30 @@ EXPORTS ?isAllocated@?$ofPixels_@E@@QEBA_NXZ ?resize@?$ofPixels_@E@@QEAA_NHHW4ofInterpolationMethod@@@Z ?rotate90@?$ofPixels_@E@@QEAAXH@Z - ?size@?$ofPixels_@E@@QEBAHXZ + ?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@XZ - ??0?$ofPixels_@E@@QEAA@$$QEAV0@@Z ??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@AEAV?$ofPixels_@E@@@Z ??4?$ofImage_@E@@QEAAAEAV0@AEBV0@@Z - ??4?$ofPixels_@E@@QEAAAEAV0@$$QEAV0@@Z ??4?$ofPixels_@E@@QEAAAEAV0@AEBV0@@Z ??A?$ofColor_@E@@QEAAAEAE_K@Z ??A?$ofColor_@E@@QEBAAEBE_K@Z + ?allocate@?$ofPixels_@G@@QEAAXHHH@Z ?allocate@?$ofPixels_@G@@QEAAXHHW4ofImageType@@@Z ?getData@?$ofPixels_@G@@QEAAPEAGXZ ?getData@?$ofPixels_@G@@QEBAPEBGXZ ?getHeight@?$ofPixels_@G@@QEBAHXZ + ?getNumChannels@?$ofPixels_@G@@QEBAHXZ ?getPixels@?$ofPixels_@G@@QEAAPEAGXZ ?getPixels@?$ofPixels_@G@@QEBAPEBGXZ ?getWidth@?$ofPixels_@G@@QEBAHXZ - ?size@?$ofPixels_@G@@QEBAHXZ + ?size@?$ofPixels_@G@@QEBA_KXZ ??0?$ofPixels_@G@@QEAA@XZ ??1?$ofPixels_@G@@QEAA@XZ ?getData@?$ofPixels_@I@@QEBAPEBIXZ @@ -230,18 +227,13 @@ EXPORTS ?getPixels@?$ofPixels_@M@@QEAAPEAMXZ ?getWidth@?$ofPixels_@M@@QEBAHXZ ?setFromPixels@?$ofPixels_@M@@QEAAXPEBMHHH@Z - ?size@?$ofPixels_@M@@QEBAHXZ + ?size@?$ofPixels_@M@@QEBA_KXZ ??0?$ofColor_@M@@QEAA@MM@Z ??0?$ofColor_@M@@QEAA@MMMM@Z - ??0?$ofImage_@M@@QEAA@$$QEAV0@@Z - ??0?$ofImage_@M@@QEAA@AEBV0@@Z ??0?$ofImage_@M@@QEAA@XZ ??0?$ofPixels_@M@@QEAA@XZ ??1?$ofImage_@M@@UEAA@XZ ??1?$ofPixels_@M@@QEAA@XZ - ??4?$ofImage_@M@@QEAAAEAV0@$$QEAV0@@Z - ??4?$ofImage_@M@@QEAAAEAV0@AEBV0@@Z - ??A?$ofColor_@M@@QEAAAEAM_K@Z ??X?$ofColor_@M@@QEAAAEAV0@AEBM@Z ??1BaseFunctionId@priv@of@@UEAA@XZ ?draw@of3dPrimitive@@UEBAXXZ @@ -356,6 +348,7 @@ EXPORTS ?getHeight@ofRectangle@@QEBAMXZ ?getTopRight@ofRectangle@@QEBA?AVofVec3f@@XZ ?getWidth@ofRectangle@@QEBAMXZ + ?inside@ofRectangle@@QEBA_NAEBVofVec3f@@@Z ?close@ofSerial@@QEAAXXZ ?isInitialized@ofSerial@@QEBA_NXZ ?setup@ofSerial@@QEAA_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z @@ -374,6 +367,7 @@ EXPORTS ?isAllocated@ofTexture@@QEBA_NXZ ?loadData@ofTexture@@QEAAXAEBV?$ofPixels_@E@@@Z ?loadData@ofTexture@@QEAAXAEBV?$ofPixels_@G@@@Z + ?loadData@ofTexture@@QEAAXAEBV?$ofPixels_@M@@@Z ?readToPixels@ofTexture@@QEBAXAEAV?$ofPixels_@M@@@Z ?setTextureMinMagFilter@ofTexture@@QEAAXHH@Z ?unbind@ofTexture@@QEBAXH@Z @@ -406,6 +400,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 + ?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 ?toOf@ofxCv@@YAAEAVofVec2f@@AEAV?$Point_@M@cv@@@Z @@ -516,6 +511,7 @@ EXPORTS ?getHasData@DataSet@ofxGraycode@@QEBA_NXZ ?getHeight@DataSet@ofxGraycode@@QEBAIXZ ?getMedian@DataSet@ofxGraycode@@QEBAAEBV?$ofPixels_@E@@XZ + ?getMedianInverse@DataSet@ofxGraycode@@QEBAAEBV?$ofPixels_@E@@XZ ?getPayloadHeight@DataSet@ofxGraycode@@QEBAIXZ ?getPayloadWidth@DataSet@ofxGraycode@@QEBAIXZ ?getWidth@DataSet@ofxGraycode@@QEBAIXZ @@ -565,6 +561,7 @@ EXPORTS ?setup@ofxOscSender@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z ??0Ray@ofxRay@@QEAA@VofVec3f@@0_N@Z ?beginAsCamera@Projector@ofxRay@@QEBAX_N@Z + ?castPixel@Projector@ofxRay@@QEBA?AVRay@2@AEBVofVec2f@@@Z ?endAsCamera@Projector@ofxRay@@QEBAXXZ ?getClippedProjectionMatrix@Projector@ofxRay@@QEBA?AVofMatrix4x4@@XZ ?getHeight@Projector@ofxRay@@QEBAHXZ @@ -678,7 +675,7 @@ EXPORTS ?serialize@AbstractCaptureSet@Utils@ofxRulr@@QEAAXAEAVValue@Json@@@Z ?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) Plugin_ArUco E:\openFrameworks\addons\ofxRulr\Plugin_ArUco\AlignMarkerMap.obj 1 + ?isSelected@BaseCapture@AbstractCaptureSet@Utils@ofxRulr@@QEBA_NXZ ?glDisable@Graphics@Utils@ofxRulr@@SAXI@Z ?glEnable@Graphics@Utils@ofxRulr@@SAXI@Z ?popPointSize@Graphics@Utils@ofxRulr@@SAXXZ diff --git a/RulrLibrary/RulrLibrary_Release.def b/RulrLibrary/RulrLibrary_Release.def index 259b31e323..9a1e14aebd 100644 --- a/RulrLibrary/RulrLibrary_Release.def +++ b/RulrLibrary/RulrLibrary_Release.def @@ -31,8 +31,6 @@ EXPORTS ??0ofRectangle@@QEAA@AEBV0@@Z ??0ofRectangle@@QEAA@MMMM@Z ??0ofSerial@@QEAA@XZ - ??0ofTexture@@QEAA@$$QEAV0@@Z - ??0ofTexture@@QEAA@AEBV0@@Z ??0ofTexture@@QEAA@XZ ??0ofVbo@@QEAA@AEBV0@@Z ??0ofVbo@@QEAA@XZ @@ -52,8 +50,6 @@ EXPORTS ??1ofVboMesh@@UEAA@XZ ??1ofxOscBundle@@QEAA@XZ ??1ofxOscMessage@@QEAA@XZ - ??4ofTexture@@QEAAAEAV0@$$QEAV0@@Z - ??4ofTexture@@QEAAAEAV0@AEBV0@@Z ??9tpp@@YA_NAEBVfIterator@Delaunay@0@0@Z ?ofCircle@@YAXAEBVofVec3f@@M@Z ?ofCircle@@YAXMMM@Z @@ -63,6 +59,7 @@ EXPORTS ?ofCreateWindow@@YA?AV?$shared_ptr@VofAppBaseWindow@@@std@@AEBVofWindowSettings@@@Z ?ofDisableAlphaBlending@@YAXXZ ?ofDisableBlendMode@@YAXXZ + ?ofDrawAxis@@YAXM@Z ?ofDrawCircle@@YAXAEBVofVec3f@@M@Z ?ofDrawCircle@@YAXMMM@Z ?ofDrawGridPlane@@YAXM_K_N@Z @@ -154,30 +151,30 @@ EXPORTS ?isAllocated@?$ofPixels_@E@@QEBA_NXZ ?resize@?$ofPixels_@E@@QEAA_NHHW4ofInterpolationMethod@@@Z ?rotate90@?$ofPixels_@E@@QEAAXH@Z - ?size@?$ofPixels_@E@@QEBAHXZ + ?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@XZ - ??0?$ofPixels_@E@@QEAA@$$QEAV0@@Z ??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@AEAV?$ofPixels_@E@@@Z ??4?$ofImage_@E@@QEAAAEAV0@AEBV0@@Z - ??4?$ofPixels_@E@@QEAAAEAV0@$$QEAV0@@Z ??4?$ofPixels_@E@@QEAAAEAV0@AEBV0@@Z ??A?$ofColor_@E@@QEAAAEAE_K@Z ??A?$ofColor_@E@@QEBAAEBE_K@Z + ?allocate@?$ofPixels_@G@@QEAAXHHH@Z ?allocate@?$ofPixels_@G@@QEAAXHHW4ofImageType@@@Z ?getData@?$ofPixels_@G@@QEAAPEAGXZ ?getData@?$ofPixels_@G@@QEBAPEBGXZ ?getHeight@?$ofPixels_@G@@QEBAHXZ + ?getNumChannels@?$ofPixels_@G@@QEBAHXZ ?getPixels@?$ofPixels_@G@@QEAAPEAGXZ ?getPixels@?$ofPixels_@G@@QEBAPEBGXZ ?getWidth@?$ofPixels_@G@@QEBAHXZ - ?size@?$ofPixels_@G@@QEBAHXZ + ?size@?$ofPixels_@G@@QEBA_KXZ ??0?$ofPixels_@G@@QEAA@XZ ??1?$ofPixels_@G@@QEAA@XZ ?getData@?$ofPixels_@I@@QEBAPEBIXZ @@ -230,18 +227,13 @@ EXPORTS ?getPixels@?$ofPixels_@M@@QEAAPEAMXZ ?getWidth@?$ofPixels_@M@@QEBAHXZ ?setFromPixels@?$ofPixels_@M@@QEAAXPEBMHHH@Z - ?size@?$ofPixels_@M@@QEBAHXZ + ?size@?$ofPixels_@M@@QEBA_KXZ ??0?$ofColor_@M@@QEAA@MM@Z ??0?$ofColor_@M@@QEAA@MMMM@Z - ??0?$ofImage_@M@@QEAA@$$QEAV0@@Z - ??0?$ofImage_@M@@QEAA@AEBV0@@Z ??0?$ofImage_@M@@QEAA@XZ ??0?$ofPixels_@M@@QEAA@XZ ??1?$ofImage_@M@@UEAA@XZ ??1?$ofPixels_@M@@QEAA@XZ - ??4?$ofImage_@M@@QEAAAEAV0@$$QEAV0@@Z - ??4?$ofImage_@M@@QEAAAEAV0@AEBV0@@Z - ??A?$ofColor_@M@@QEAAAEAM_K@Z ??X?$ofColor_@M@@QEAAAEAV0@AEBM@Z ??1BaseFunctionId@priv@of@@UEAA@XZ ?draw@of3dPrimitive@@UEBAXXZ @@ -356,6 +348,7 @@ EXPORTS ?getHeight@ofRectangle@@QEBAMXZ ?getTopRight@ofRectangle@@QEBA?AVofVec3f@@XZ ?getWidth@ofRectangle@@QEBAMXZ + ?inside@ofRectangle@@QEBA_NAEBVofVec3f@@@Z ?close@ofSerial@@QEAAXXZ ?isInitialized@ofSerial@@QEBA_NXZ ?setup@ofSerial@@QEAA_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z @@ -374,6 +367,7 @@ EXPORTS ?isAllocated@ofTexture@@QEBA_NXZ ?loadData@ofTexture@@QEAAXAEBV?$ofPixels_@E@@@Z ?loadData@ofTexture@@QEAAXAEBV?$ofPixels_@G@@@Z + ?loadData@ofTexture@@QEAAXAEBV?$ofPixels_@M@@@Z ?readToPixels@ofTexture@@QEBAXAEAV?$ofPixels_@M@@@Z ?setTextureMinMagFilter@ofTexture@@QEAAXHH@Z ?unbind@ofTexture@@QEBAXH@Z @@ -406,6 +400,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 + ?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 ?toOf@ofxCv@@YAAEAVofVec2f@@AEAV?$Point_@M@cv@@@Z @@ -516,6 +511,7 @@ EXPORTS ?getHasData@DataSet@ofxGraycode@@QEBA_NXZ ?getHeight@DataSet@ofxGraycode@@QEBAIXZ ?getMedian@DataSet@ofxGraycode@@QEBAAEBV?$ofPixels_@E@@XZ + ?getMedianInverse@DataSet@ofxGraycode@@QEBAAEBV?$ofPixels_@E@@XZ ?getPayloadHeight@DataSet@ofxGraycode@@QEBAIXZ ?getPayloadWidth@DataSet@ofxGraycode@@QEBAIXZ ?getWidth@DataSet@ofxGraycode@@QEBAIXZ @@ -565,6 +561,7 @@ EXPORTS ?setup@ofxOscSender@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z ??0Ray@ofxRay@@QEAA@VofVec3f@@0_N@Z ?beginAsCamera@Projector@ofxRay@@QEBAX_N@Z + ?castPixel@Projector@ofxRay@@QEBA?AVRay@2@AEBVofVec2f@@@Z ?endAsCamera@Projector@ofxRay@@QEBAXXZ ?getClippedProjectionMatrix@Projector@ofxRay@@QEBA?AVofMatrix4x4@@XZ ?getHeight@Projector@ofxRay@@QEBAHXZ