To build use something along the lines of
g++ NRUBSOpenCV.cpp -o NRUBSOpenCV -I/usr/local/include -I/usr/local/include/eigen3 -L/usr/local/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -ldxflib
| /* | |
| * Simple 2D NURBS renderer for OpenCV, reading DXF files | |
| * | |
| * The MIT License (MIT) | |
| * | |
| * Copyright (c) 2013 Roy Shilkrot | |
| * | |
| * Updated: Nov 2016 | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| * THE SOFTWARE. | |
| * | |
| */ | |
| #include <dxflib/dl_dxf.h> | |
| #include <dxflib/dl_creationadapter.h> | |
| #include <Eigen/Eigen> | |
| #include <opencv2/core/core.hpp> | |
| #include <opencv2/imgproc/imgproc.hpp> | |
| #include <opencv2/highgui/highgui.hpp> | |
| #include <numeric> | |
| #include <vector> | |
| #include <iostream> | |
| #include <iterator> | |
| #include <functional> | |
| using namespace std; | |
| /** | |
| * from: http://en.wikipedia.org/wiki/NURBS | |
| * and: http://web.cs.wpi.edu/~matt/courses/cs563/talks/nurbs.html | |
| * | |
| * This is the main NURBS rendering implementation. | |
| */ | |
| struct MyNURBS { | |
| typedef Eigen::Vector3f Vec3T; | |
| int deg; | |
| vector<Vec3T> P; | |
| vector<float> k; | |
| vector<float> w; | |
| float f(const int i, const int n, const float u) const { | |
| const float denom = k[i + n] - k[i]; | |
| if (denom == 0) { | |
| return 0.0f; | |
| } | |
| return (u - k[i]) / denom; | |
| } | |
| float g(const int i, const int n, const float u) const { | |
| const float denom = k[i + n + 1] - k[i]; | |
| if (denom == 0) { | |
| return 0.0f; | |
| } | |
| return (k[i + n + 1] - u) / denom; | |
| } | |
| float N(const int i, const int n, const float u) const { | |
| if (n == 0) { | |
| const float Ninu = ((k[i] <= u && u <= k[i + 1])) ? 1 : 0; | |
| return Ninu; | |
| } else { | |
| const float Nin1u = N(i, n - 1, u); | |
| const float Ni1n1u = N(i + 1, n - 1, u); | |
| const float a = (Nin1u != 0.0f) ? f(i, n, u) * Nin1u : 0.0f; | |
| const float b = (Ni1n1u != 0.0f) ? g(i, n, u) * Ni1n1u : 0.0f; | |
| const float Ninu = a + b; | |
| return Ninu; | |
| } | |
| } | |
| /** | |
| * Evaluate the NURBS over a number of segments and return the curve points | |
| */ | |
| vector<Vec3T> evaluate(const int num_segments = 10) { | |
| if (w.size() <= 0) { | |
| w.resize(P.size(), 1.0); //uniform weights if they were not specified | |
| } | |
| vector<Vec3T> C; | |
| const float step = (k.back() - k.front()) / static_cast<float>(num_segments); | |
| for (float u = k.front(); u <= k.back(); u += step) { | |
| Vec3T C_u = Vec3T::Zero(); | |
| vector<float> Nku; | |
| for (int j = 0; j < P.size(); ++j) { //sum(j = 0, n){w_j * N_j,k(u)} | |
| Nku.push_back(w[j] * N(j, deg, u)); | |
| } | |
| const float denom = std::accumulate(Nku.begin(), Nku.end(), 0.0f); | |
| for (int i = 0; i < P.size(); ++i) { | |
| if (Nku[i] != 0.0f && denom != 0.0f) { | |
| //R_i,k(u) = w_i * N_i,k(u) / sum(j = 0, n){w_j * N_j,k(u)} | |
| float R_iku = Nku[i] / denom; | |
| if (R_iku != 0.0f) { | |
| C_u += P[i] * R_iku; | |
| } | |
| } | |
| } | |
| C.push_back(C_u); | |
| } | |
| return C; | |
| } | |
| void draw(cv::Mat& img, const cv::Scalar s = cv::Scalar(255), const int num_segments = 10) { | |
| vector<Vec3T> C = evaluate(num_segments); | |
| //draw curve | |
| for (int i = 1; i < C.size(); ++i) { | |
| cv::line(img, *(cv::Point2f*) &C[i], *(cv::Point2f*) &C[i - 1], s, 1); | |
| } | |
| //control points | |
| for (int i = 0; i < P.size(); ++i) { | |
| const Vec3T v = P[i]; | |
| cv::circle(img, *(cv::Point2f*) &(v), 2, cv::Scalar(0, 255, 255), 1); | |
| } | |
| } | |
| }; | |
| class MyDXFFilter: public DL_CreationAdapter { | |
| public: | |
| virtual void addSpline(const DL_SplineData& s) { | |
| cerr << "spline " << s.degree << " " | |
| << s.flags << " " | |
| << s.nControl << " " | |
| << s.nKnots << endl; | |
| nurbs.push_back(MyNURBS()); | |
| nurbs.back().deg = s.degree; | |
| } | |
| virtual void addControlPoint(const DL_ControlPointData& cp) { | |
| cerr << "ctrl pt " << cp.x << " " << cp.y << " " << cp.z << endl; | |
| nurbs.back().P.push_back(Eigen::Vector3f(cp.x, cp.y, cp.z)); | |
| } | |
| virtual void addKnot(const DL_KnotData& k) { | |
| cerr << "knot " << k.k << endl; | |
| nurbs.back().k.push_back(k.k); | |
| } | |
| vector<MyNURBS> nurbs; | |
| private: | |
| }; | |
| int main(int argc, char** argv) { | |
| cv::Mat tmp; | |
| tmp.create(512, 512, CV_8UC3); | |
| if (argc < 2) { | |
| cerr << "Please provide DXF filename to process." << endl; | |
| return 1; | |
| } | |
| const string filename(argv[1]); | |
| MyDXFFilter f; | |
| DL_Dxf dxf; | |
| if (!dxf.in(filename, &f)) { | |
| cerr << filename << " could not be opened." << endl; | |
| return 1; | |
| } else { | |
| cout << "loaded " << filename << endl; | |
| } | |
| for (int i = 0; i < f.nurbs.size(); ++i) { | |
| f.nurbs[i].draw(tmp, cv::Scalar(255), 50); | |
| } | |
| cv::imshow("tmp", tmp); | |
| cv::waitKey(); | |
| } |
Thanks for the sample code. I was able to build it in Xcode after installing dxflib and eigen3 via homebrew.
However, I cannot find a dxf file that will render anything visible. I imagine this may have something to do with DXF versions?
Would it be possible to add a sample image to this GIST?