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?