How to use ONNX model in C++ code on Linux? - c++

I train some Unet-based model in Pytorch. It take an image as an input, and return a mask.
After training i save it to ONNX format, run it with onnxruntime python module and it worked like a charm.
Now, i want to use this model in C++ code in Linux.
Is there simple tutorial (Hello world) when explained:
How to incorporate onnxruntime module to C++ program in Ubuntu
(install shared lib and so on)?
How to properly load an image and pass it to model?
P.S. I found only this: https://www.onnxruntime.ai/docs/tutorials/samples_catalog.html#cc
But there no info about loading image and converting it to ONNX - compatible format in C++ code.

For installation on the Linux, you should refer to https://www.onnxruntime.ai/.
You can refer to the following code to get help regarding how to load and run the ONNX model.
#include <algorithm> // std::generate
#include <assert.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <experimental_onnxruntime_cxx_api.h>
// pretty prints a shape dimension vector
std::string print_shape(const std::vector<int64_t>& v) {
std::stringstream ss("");
for (size_t i = 0; i < v.size() - 1; i++)
ss << v[i] << "x";
ss << v[v.size() - 1];
return ss.str();
}
int calculate_product(const std::vector<int64_t>& v) {
int total = 1;
for (auto& i : v) total *= i;
return total;
}
using namespace std;
int main(int argc, char** argv) {
if (argc != 2) {
cout << "Usage: ./onnx-api-example <onnx_model.onnx>" << endl;
return -1;
}
#ifdef _WIN32
std::string str = argv[1];
std::wstring wide_string = std::wstring(str.begin(), str.end());
std::basic_string<ORTCHAR_T> model_file = std::basic_string<ORTCHAR_T>(wide_string);
#else
std::string model_file = argv[1];
#endif
// onnxruntime setup
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "example-model-explorer");
Ort::SessionOptions session_options;
Ort::Experimental::Session session = Ort::Experimental::Session(env, model_file, session_options); // access experimental components via the Experimental namespace
// print name/shape of inputs
std::vector<std::string> input_names = session.GetInputNames();
std::vector<std::vector<int64_t> > input_shapes = session.GetInputShapes();
cout << "Input Node Name/Shape (" << input_names.size() << "):" << endl;
for (size_t i = 0; i < input_names.size(); i++) {
cout << "\t" << input_names[i] << " : " << print_shape(input_shapes[i]) << endl;
}
// print name/shape of outputs
std::vector<std::string> output_names = session.GetOutputNames();
std::vector<std::vector<int64_t> > output_shapes = session.GetOutputShapes();
cout << "Output Node Name/Shape (" << output_names.size() << "):" << endl;
for (size_t i = 0; i < output_names.size(); i++) {
cout << "\t" << output_names[i] << " : " << print_shape(output_shapes[i]) << endl;
}
// Assume model has 1 input node and 1 output node.
assert(input_names.size() == 1 && output_names.size() == 1);
// Create a single Ort tensor of random numbers
auto input_shape = input_shapes[0];
int total_number_elements = calculate_product(input_shape);
std::vector<float> input_tensor_values(total_number_elements);
std::generate(input_tensor_values.begin(), input_tensor_values.end(), [&] { return rand() % 255; }); // generate random numbers in the range [0, 255]
std::vector<Ort::Value> input_tensors;
input_tensors.push_back(Ort::Experimental::Value::CreateTensor<float>(input_tensor_values.data(), input_tensor_values.size(), input_shape));
// double-check the dimensions of the input tensor
assert(input_tensors[0].IsTensor() &&
input_tensors[0].GetTensorTypeAndShapeInfo().GetShape() == input_shape);
cout << "\ninput_tensor shape: " << print_shape(input_tensors[0].GetTensorTypeAndShapeInfo().GetShape()) << endl;
// pass data through model
cout << "Running model...";
try {
auto output_tensors = session.Run(session.GetInputNames(), input_tensors, session.GetOutputNames());
cout << "done" << endl;
// double-check the dimensions of the output tensors
// NOTE: the number of output tensors is equal to the number of output nodes specifed in the Run() call
assert(output_tensors.size() == session.GetOutputNames().size() &&
output_tensors[0].IsTensor());
cout << "output_tensor_shape: " << print_shape(output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()) << endl;
} catch (const Ort::Exception& exception) {
cout << "ERROR running model inference: " << exception.what() << endl;
exit(-1);
}
}

Related

I can't use fbx sdk (2020) to save a fbx with embed texture

I use cloneManager to clone one of the groups in one scene that contains 2-3 meshes and their materials and textures, and then I want to export it to another scene as fbx, which contains the materials and embedded textures.
I've isolated some of the models I want, but they don't have textures or textures
I saw that the mesh in the new scene I cloned already contained the texture path, which pointed to an fbm path, but I still couldn't see the texture when I opened it. And the fbm files that belong to this new fbx model are not automatically created.
I have two questions:
Did I actually clone the texture? Or did I actually export the texture?
Here is my code
Thanks
`
#include <iostream>
#include "Common.h"
#include "Util.h"
#include <string>
#include <fbxsdk/core/fbxobject.h>
#include "fbxsdk.h"
using namespace std;
int main(int, char **) {
std::cout << "Hello, Start!\n";
FbxManager *lSdkManager = nullptr;
FbxScene *lScene = nullptr;
// Prepare the FBX SDK.
InitializeSdkObjects(lSdkManager, lScene);
string lFilePath = "/Users/Downloads/new_scene1_0.fbx";
string targetPath = "/Users/CLionProjects/CMakeLists/test/";
LoadScene(lSdkManager, lScene, lFilePath.c_str());
FbxNode *rootNode = lScene->GetRootNode();
int childCount = rootNode->GetChildCount();
for (int i = 0; i < 1; i++) {
auto pScene = FbxScene::Create(lSdkManager, "My Scene");
auto node = rootNode->GetChild(i);
// clone
CloneAndSetting(lScene, pScene, node);
auto pSceneRoot = pScene->GetRootNode();
GetNodeNameAndAttributeTypeName(pSceneRoot);
GetDefaultTranslationInfo(pSceneRoot);
GetNodeVisibility(pSceneRoot);
std::cout << "pScene srcCount: " << pScene->GetSrcObjectCount() << "\n";
std::cout << "pScene geoCount: " << pScene->GetGeometryCount() << "\n";
std::cout << "pScene matCount: " << pScene->GetMaterialCount() << "\n";
std::cout << "pScene nodeCount: " << pScene->GetNodeCount() << "\n";
GetNodeCountInfo(pSceneRoot);
// find the first group node
FbxObject *shapeNode = nullptr;
for (int j = 0; j < pSceneRoot->GetSrcObjectCount(); ++j) {
auto obj = pSceneRoot->GetSrcObject(j);
string name = obj->GetName();
if (name.find("Shape") != string::npos) {
shapeNode = obj;
}
}
// disconnect some connection, and connect the shape to root
pSceneRoot->DisconnectAllSrcObject();
GetNodeCountInfo(pSceneRoot);
//shapeNode->DisconnectAllDstObject();
shapeNode->ConnectDstObject(pSceneRoot);
GetNodeCountInfo(pSceneRoot);
for (int j = 0; j < pSceneRoot->GetChildCount(); j++) {
auto shape = pSceneRoot->GetChild(j);
std::cout << "c name: " << shape->GetName() << "\n";
std::cout << "c type: " << shape->GetTypeName() << "\n";
std::cout << "c dst: " << shape->GetDstObject()->GetName() << "\n";
int groupCount = shape->GetChildCount();
for (int k = 0; k < groupCount; k++) {
auto group = shape->GetChild(k);
auto groupChildCount = group->GetChildCount();
for (int l = 0; l < groupChildCount; l++) {
FbxNode *meshNode = group->GetChild(l);
FbxSurfaceMaterial *meshMat = meshNode->GetMaterial(0);
FbxProperty meshMatProp = meshMat->FindProperty(FbxSurfaceMaterial::sDiffuse);
string texPath = nullptr;
int layeredTextureCount = meshMatProp.GetSrcObjectCount<FbxLayeredTexture>();
if (layeredTextureCount != 0) {
std::cout << "link layer" << "\n";
auto *layeredTexture = meshMatProp.GetSrcObject<FbxLayeredTexture>();
for (int m = 0; m < layeredTexture->GetSrcObjectCount<FbxTexture>(); m++) {
auto *fbxFileTexture = FbxCast<FbxFileTexture>(layeredTexture->GetSrcObject<FbxTexture>(m));
string fileName = fbxFileTexture->GetFileName();
texPath = fileName;
}
}
int textureCount = meshMatProp.GetSrcObjectCount<FbxTexture>();
if (textureCount != 0) {
std::cout << "link mat" << "\n";
auto *fbxTexture = FbxCast<FbxTexture>(meshMatProp.GetSrcObject<FbxTexture>(0));
auto *fbxFileTexture = FbxCast<FbxFileTexture>(fbxTexture);
string fileName = fbxFileTexture->GetFileName();
std::cout << "fileName: " << fileName << "\n";
texPath = fileName;
}
meshNode->GetMesh()->Set
}
}
}
for (int j = 0; j < pSceneRoot->GetSrcObjectCount(); j++) {
std::cout << "s name: " << pSceneRoot->GetSrcObject(j)->GetName() << "\n";
std::cout << "s type: " << pSceneRoot->GetSrcObject(j)->GetTypeName() << "\n";
std::cout << "s dst: " << pSceneRoot->GetSrcObject(j)->GetDstObject()->GetName() << "\n";
}
std::cout << "-----start generate------\n";
string meshName = node->GetName();
meshName = targetPath + meshName + ".fbx";
lSdkManager->GetIOSettings()->SetBoolProp(EXP_FBX_EMBEDDED, true);
SaveScene(lSdkManager, pScene, meshName.c_str());
pScene->Destroy();
std::cout << "----" << i << " end----\n";
}
}
`
To export a scene and have its media embedded within the exported FBX file, the binary (or native) FBX file format identifier must be specified in the call to FbxExporter::Initialize().
// ... Initialize the required objects and variables ...
// Set the FbxIOSettings EXP_FBX_EMBEDDED property to true.
(*(lSdkManager->GetIOSettings())).SetBoolProp(EXP_FBX_EMBEDDED, true);
// Get the appropriate file format.
int lFileFormat = lSdkManager->GetIOPluginRegistry()-
>GetNativeWriterFormat();
// Initialize the exporter to embed the scene's media into the exported file.
bool lExportStatus = lExporter->Initialize(lFilename, lFileFormat,
lSdkManager->GetIOSettings());
// ... Export the scene ...

How to identify stranger in dlib`s one vs one classifier

I`m using a one_vs_one_trainer and one_vs_one_decision_function for classify 128D face descriptors, and i want to detect unknown face.
I`m detecting faces using OpenCV and my wrapper, then i followed the guide and computed the 128D face descriptors, that i stored in files. Next, i trained one_vs_one classifier following this tutorial. All works perfectly, but when i try to classify unknown face it returns some label.
I used code from guides, but if you want to look at my code - it is here
Is there a better way to identify faces? Maybe, its simpler to use OpenCV`s methods, or other from Dlib?
Thanks for Davis!
Here is forum thread on SourceForge.
The answer is:
Use a bunch of binary classifiers rather than one vs one. If all the binary
classifiers say they don't match then you know the person doesn't match any
of them.
And i implemented this as follows:
#include <iostream>
#include <ctime>
#include <vector>
#include <dlib/svm.h>
using namespace std;
using namespace dlib;
int main() {
typedef matrix<double, 128, 1> sample_type;
typedef histogram_intersection_kernel<sample_type> kernel_type;
typedef svm_c_trainer<kernel_type> trainer_type;
typedef decision_function<kernel_type> classifier_type;
std::vector<sample_type> samples;
std::vector<double> labels;
sample_type sample;
// Samples ->
sample = -0.104075,0.0353173,...,0.114782,-0.0360935;
samples.emplace_back(sample);
labels.emplace_back(0);
sample = -0.0842,-0.0103397,...,0.0938285,0.010045;
samples.emplace_back(sample);
labels.emplace_back(0);
sample = -0.0978358,0.0709425,...,0.052436,-0.0582029;
samples.emplace_back(sample);
labels.emplace_back(0);
sample = -0.126522,0.0319873,...,0.12045,-0.0277105;
samples.emplace_back(sample);
labels.emplace_back(0);
sample = -0.10335,-0.0261625,...,0.0600661,0.00703168,-8.67462e-05,-0.0598214,-0.104442,-0.046698,0.0553857,-0.0880691,0.0482511,0.0331484;
samples.emplace_back(sample);
labels.emplace_back(0);
sample = -0.0747794,0.0599716,...,-0.0440207,-6.45183e-05;
samples.emplace_back(sample);
labels.emplace_back(1);
sample = -0.0280804,0.0900723,...,-0.0267513,0.00824318;
samples.emplace_back(sample);
labels.emplace_back(1);
sample = -0.0721213,0.00700722,...,-0.0128318,0.100784;
samples.emplace_back(sample);
labels.emplace_back(1);
sample = -0.122747,0.0737782,0.0375799,...,0.0168201,-0.0246723;
samples.emplace_back(sample);
labels.emplace_back(1);
sample = -0.0218071,0.118063,...,-0.0735178,0.04046;
samples.emplace_back(sample);
labels.emplace_back(1);
sample = -0.0680787,0.0490121,-0.0228516,...,-0.0366242,0.0287891;
samples.emplace_back(sample);
labels.emplace_back(2);
sample = 0.00152394,0.107174,...,-0.0479925,0.0182667;
samples.emplace_back(sample);
labels.emplace_back(2);
sample = -0.0334521,0.165314,...,-0.0385227,-0.0215499;
samples.emplace_back(sample);
labels.emplace_back(2);
sample = 0.0276394,0.106774,...,-0.0496831,-0.020857;
samples.emplace_back(sample);
labels.emplace_back(2);
// <- Samples
// Unique labels ->
std::vector<double> total_labels;
for(double &label : labels) {
if(find(total_labels.begin(), total_labels.end(), label) == total_labels.end())
total_labels.emplace_back(label);
}
// <- Unique labels
// Init trainers ->
std::vector<trainer_type> trainers;
int num_trainers = total_labels.size() * (total_labels.size() - 1) / 2;
cout << "Number of trainers is " << num_trainers << endl;
for(int i = 0; i < num_trainers; i++) {
trainers.emplace_back(trainer_type());
trainers[i].set_kernel(kernel_type());
trainers[i].set_c(10);
}
// <- Init trainers
// Init classifiers ->
std::vector<pair<double, double>> classifiersLabels;
std::vector<classifier_type> classifiers;
int label1 = 0, label2 = 1;
for(trainer_type &trainer : trainers) {
std::vector<sample_type> samples4pair;
std::vector<double> labels4pair;
for(int i = 0; i < samples.size(); i++) {
if(labels[i] == total_labels[label1]) {
samples4pair.emplace_back(samples[i]);
labels4pair.emplace_back(-1);
}
if(labels[i] == total_labels[label2]) {
samples4pair.emplace_back(samples[i]);
labels4pair.emplace_back(+1);
}
}
classifiers.emplace_back(trainer.train(samples4pair, labels4pair));
classifiersLabels.emplace_back(make_pair(total_labels[label1],
total_labels[label2]));
label2++;
if(label2 == total_labels.size()) {
label1++;
label2 = label1 + 1;
}
}
// <- Init classifiers
double threshold = 0.3;
auto classify = [&](){
std::map<double, int> votes;
for(int i = 0; i < classifiers.size(); i++) {
cout << "Classifier #" << i << ":" << endl;
double prediction = classifiers[i](sample);
cout << prediction << ": ";
if(abs(prediction) < threshold) {
cout << "-1" << endl;
} else if (prediction < 0) {
votes[classifiersLabels[i].first]++;
cout << classifiersLabels[i].first << endl;
} else {
votes[classifiersLabels[i].second]++;
cout << classifiersLabels[i].second << endl;
}
}
cout << "Votes: " << endl;
for(auto &vote : votes) {
cout << vote.first << ": " << vote.second << endl;
}
auto max = std::max_element(votes.begin(), votes.end(),
[](const pair<double, int>& p1, const pair<double, int>& p2) {
return p1.second < p2.second; });
double label = votes.empty() ? -1 : max->first;
cout << "Label is " << label << endl;
};
// Test ->
cout << endl;
sample = -0.0971093, ..., 0.123482, -0.0399552;
cout << "True: 0 - " << endl;
classify();
cout << endl;
sample = -0.0548414, ..., 0.0277335, 0.0460183;
cout << "True: 1 - " << endl;
classify();
cout << endl;
sample = -0.0456186,0.0617834,...,-0.0387607,0.0366309;
cout << "True: 1 - " << endl;
classify();
cout << endl;
sample = -0.0500396, 0.0947202, ..., -0.0540899, 0.0206803;
cout << "True: 2 - " << endl;
classify();
cout << endl;
sample = -0.0702862, 0.065316, ..., -0.0279446, 0.0453012;
cout << "Unknown - " << endl;
classify();
cout << endl;
sample = -0.0789684, 0.0632067, ..., 0.0330486, 0.0117508;
cout << "Unknown - " << endl;
classify();
cout << endl;
sample = -0.0941284, 0.0542927, ..., 0.00855513, 0.00840678;
cout << "Unknown - " << endl;
classify();
// <- Test
return 0;
}

How to convert tensor to image array?

I would like to convert a tensor to image array and use tensor.data() method.
But it doesn't work.
#include <torch/script.h> // One-stop header.
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkImageRegionIterator.h"
//////////////////////////////////////////////////////
//Goal: load jit script model and segment myocardium
//Step: 1. load jit script model
// 2. load input image
// 3. predict by model
// 4. save the result to file
//////////////////////////////////////////////////////
typedef short PixelType;
const unsigned int Dimension = 3;
typedef itk::Image<PixelType, Dimension> ImageType;
typedef itk::ImageFileReader<ImageType> ReaderType;
typedef itk::ImageRegionIterator<ImageType> IteratorType;
bool itk2tensor(ImageType::Pointer itk_img, torch::Tensor &tensor_img) {
typename ImageType::RegionType region = itk_img->GetLargestPossibleRegion();
const typename ImageType::SizeType size = region.GetSize();
std::cout << "Input size: " << size[0] << ", " << size[1]<< ", " << size[2] << std::endl;
int len = size[0] * size[1] * size[2];
short rowdata[len];
int count = 0;
IteratorType iter(itk_img, itk_img->GetRequestedRegion());
// convert itk to array
for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter) {
rowdata[count] = iter.Get();
count++;
}
std::cout << "Convert itk to array DONE!" << std::endl;
// convert array to tensor
tensor_img = torch::from_blob(rowdata, {1, 1, (int)size[0], (int)size[1], (int)size[2]}, torch::kShort).clone();
tensor_img = tensor_img.toType(torch::kFloat);
tensor_img = tensor_img.to(torch::kCUDA);
tensor_img.set_requires_grad(0);
return true;
}
bool tensor2itk(torch::Tensor &t, ImageType::Pointer itk_img) {
std::cout << "tensor dtype = " << t.dtype() << std::endl;
std::cout << "tensor size = " << t.sizes() << std::endl;
t = t.toType(torch::kShort);
short * array = t.data<short>();
ImageType::IndexType start;
start[0] = 0; // first index on X
start[1] = 0; // first index on Y
start[2] = 0; // first index on Z
ImageType::SizeType size;
size[0] = t.size(2);
size[1] = t.size(3);
size[2] = t.size(4);
ImageType::RegionType region;
region.SetSize( size );
region.SetIndex( start );
itk_img->SetRegions( region );
itk_img->Allocate();
int len = size[0] * size[1] * size[2];
IteratorType iter(itk_img, itk_img->GetRequestedRegion());
int count = 0;
// convert array to itk
std::cout << "start!" << std::endl;
for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter) {
short temp = *array++; // ERROR!
std::cout << temp << " ";
iter.Set(temp);
count++;
}
std::cout << "end!" << std::endl;
return true;
}
int main(int argc, const char* argv[]) {
int a, b, c;
if (argc != 4) {
std::cerr << "usage: automyo input jitmodel output\n";
return -1;
}
std::cout << "========= jit start =========\n";
// 1. load jit script model
std::cout << "Load script module: " << argv[2] << std::endl;
std::shared_ptr<torch::jit::script::Module> module = torch::jit::load(argv[2]);
module->to(at::kCUDA);
// assert(module != nullptr);
std::cout << "Load script module DONE" << std::endl;
// 2. load input image
const char* img_path = argv[1];
std::cout << "Load image: " << img_path << std::endl;
ReaderType::Pointer reader = ReaderType::New();
if (!img_path) {
std::cout << "Load input file error!" << std::endl;
return false;
}
reader->SetFileName(img_path);
reader->Update();
std::cout << "Load image DONE!" << std::endl;
ImageType::Pointer itk_img = reader->GetOutput();
torch::Tensor tensor_img;
if (!itk2tensor(itk_img, tensor_img)) {
std::cerr << "itk2tensor ERROR!" << std::endl;
}
else {
std::cout << "Convert array to tensor DONE!" << std::endl;
}
std::vector<torch::jit::IValue> inputs;
inputs.push_back(tensor_img);
// 3. predict by model
torch::Tensor y = module->forward(inputs).toTensor();
std::cout << "Inference DONE!" << std::endl;
// 4. save the result to file
torch::Tensor seg = y.gt(0.5);
// std::cout << seg << std::endl;
ImageType::Pointer out_itk_img = ImageType::New();
if (!tensor2itk(seg, out_itk_img)) {
std::cerr << "tensor2itk ERROR!" << std::endl;
}
else {
std::cout << "Convert tensor to itk DONE!" << std::endl;
}
std::cout << out_itk_img << std::endl;
return true;
}
The runtime log is showed below:
Load script module:model_myo_jit.pt
Load script module DONE Load
image: patch_6.nii.gz
Load image DONE!
Input size: 128, 128, 128
Convert itk to array DONE!
Convert array to tensor DONE!
Inference DONE!
tensor dtype = unsigned char
tensor size = [1, 1, 96, 96, 96]
start!
Segmentation fault (core dumped)
Why and how to convert?
I have found the solution. When I convert the y to kCPU, it works. Because it in CUDA before.

saving CGAL alpha shape surface mesh

I have never used CGAL and have got almost no C/C++ experience. But following
Google I have however managed to compile the example "Alpha_shapes_3"
(\CGAL-4.1-beta1\examples\Alpha_shapes_3) on a Windows 7 64bit machine using
visual studio 2010.
Now if we check the source code for the program "ex_alpha_shapes_3" we
notice that a data file called "bunny_1000" is red where the 3d point
cluster resides.
Now my question is how can I change the source code so that after the alpha
shape is computed for the given points, surface mesh of the alpha shape is
saved/wrote in an external file. It can be simply the list of polygons and
their respective 3D vertices. I guess these polygons will be defining the
surface mesh of the alpha shape. If I can do that I can see the output of
the alpha shape generation program in an external tool I am familiar with.
I know this is very straightforward but I could not figure this out with my
limited knowledge of CGAL.
I know you gueys have the code but I am pasting it again for completion.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Alpha_shape_3.h>
#include <fstream>
#include <list>
#include <cassert>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Gt;
typedef CGAL::Alpha_shape_vertex_base_3<Gt> Vb;
typedef CGAL::Alpha_shape_cell_base_3<Gt> Fb;
typedef CGAL::Triangulation_data_structure_3<Vb,Fb> Tds;
typedef CGAL::Delaunay_triangulation_3<Gt,Tds> Triangulation_3;
typedef CGAL::Alpha_shape_3<Triangulation_3> Alpha_shape_3;
typedef Gt::Point_3 Point;
typedef Alpha_shape_3::Alpha_iterator Alpha_iterator;
int main()
{
std::list<Point> lp;
//read input
std::ifstream is("./data/bunny_1000");
int n;
is >> n;
std::cout << "Reading " << n << " points " << std::endl;
Point p;
for( ; n>0 ; n--) {
is >> p;
lp.push_back(p);
}
// compute alpha shape
Alpha_shape_3 as(lp.begin(),lp.end());
std::cout << "Alpha shape computed in REGULARIZED mode by default"
<< std::endl;
// find optimal alpha value
Alpha_iterator opt = as.find_optimal_alpha(1);
std::cout << "Optimal alpha value to get one connected component is "
<< *opt << std::endl;
as.set_alpha(*opt);
assert(as.number_of_solid_components() == 1);
return 0;
}
After searching a lot in the internet I found that probably we need to use something like
std::list<Facet> facets;
alpha_shape.get_alpha_shape_facets
(
std::back_inserter(facets),Alpha_shape::REGULAR
);
But I am still completely clueless how to use this in the above code!
As documented here, a facet is a pair (Cell_handle c,int i) defined as the facet in c opposite to the vertex of index i.
On this page, you have the description of how the vertex indices of a cell are.
In the following code sample, I added a small output that prints an OFF file on cout by duplicating the vertices. To do something clean, you can either use a std::map<Alpha_shape_3::Vertex_handle,int> to associate a unique index per vertex or add an info to the vertices like in those examples.
/// collect all regular facets
std::vector<Alpha_shape_3::Facet> facets;
as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::REGULAR);
std::stringstream pts;
std::stringstream ind;
std::size_t nbf=facets.size();
for (std::size_t i=0;i<nbf;++i)
{
//To have a consistent orientation of the facet, always consider an exterior cell
if ( as.classify( facets[i].first )!=Alpha_shape_3::EXTERIOR )
facets[i]=as.mirror_facet( facets[i] );
CGAL_assertion( as.classify( facets[i].first )==Alpha_shape_3::EXTERIOR );
int indices[3]={
(facets[i].second+1)%4,
(facets[i].second+2)%4,
(facets[i].second+3)%4,
};
/// according to the encoding of vertex indices, this is needed to get
/// a consistent orienation
if ( facets[i].second%2==0 ) std::swap(indices[0], indices[1]);
pts <<
facets[i].first->vertex(indices[0])->point() << "\n" <<
facets[i].first->vertex(indices[1])->point() << "\n" <<
facets[i].first->vertex(indices[2])->point() << "\n";
ind << "3 " << 3*i << " " << 3*i+1 << " " << 3*i+2 << "\n";
}
std::cout << "OFF "<< 3*nbf << " " << nbf << " 0\n";
std::cout << pts.str();
std::cout << ind.str();
Here is my code, which outputs vtk file for visualization in Paraview. Comparing with slorior's solutions, no duplicated points are saved in the file. But my code is just for the visualization, if you need to figure out the exterior or interior simplexes, you should modify the code to get these results.
void writevtk(Alpha_shape_3 &as, const std::string &asfile) {
// http://cgal-discuss.949826.n4.nabble.com/Help-with-filtration-and-filtration-with-alpha-values-td4659524.html#a4659549
std::cout << "Information of the Alpha_Complex:\n";
std::vector<Alpha_shape_3::Cell_handle> cells;
std::vector<Alpha_shape_3::Facet> facets;
std::vector<Alpha_shape_3::Edge> edges;
// tetrahedron = cell, they should be the interior, it is inside the 3D space
as.get_alpha_shape_cells(std::back_inserter(cells), Alpha_shape_3::INTERIOR);
// triangles
// for the visualiization, don't need regular because tetrahedron will show it
//as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::REGULAR);
as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::SINGULAR);
// edges
as.get_alpha_shape_edges(std::back_inserter(edges), Alpha_shape_3::SINGULAR);
std::cout << "The alpha-complex has : " << std::endl;
std::cout << cells.size() << " cells as tetrahedrons" << std::endl;
std::cout << facets.size() << " triangles" << std::endl;
std::cout << edges.size() << " edges" << std::endl;
size_t tetra_num, tri_num, edge_num;
tetra_num = cells.size();
tri_num = facets.size();
edge_num = edges.size();
// vertices: points <-> id
std::map<Point, size_t> points;
size_t index = 0;
// finite_.. is from DT class
for (auto v_it = as.finite_vertices_begin(); v_it != as.finite_vertices_end(); v_it++) {
points[v_it->point()] = index;
index++;
}
// write
std::ofstream of(asfile);
of << "# vtk DataFile Version 2.0\n\nASCII\nDATASET UNSTRUCTURED_GRID\n\n";
of << "POINTS " << index << " float\n";
for (auto v_it = as.finite_vertices_begin(); v_it != as.finite_vertices_end(); v_it++) {
of << v_it->point() << std::endl;
}
of << std::endl;
of << "CELLS " << tetra_num + tri_num + edge_num << " " << 5 * tetra_num + 4 * tri_num + 3 * edge_num << std::endl;
for (auto cell:cells) {
size_t v0 = points.find(cell->vertex(0)->point())->second;
size_t v1 = points.find(cell->vertex(1)->point())->second;
size_t v2 = points.find(cell->vertex(2)->point())->second;
size_t v3 = points.find(cell->vertex(3)->point())->second;
of << "4 " << v0 << " " << v1 << " " << v2 << " " << v3 << std::endl;
}
// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#ad6a20b45e66dfb690bfcdb8438e9fcae
for (auto tri_it = facets.begin(); tri_it != facets.end(); ++tri_it) {
of << "3 ";
auto tmp_tetra = tri_it->first;
for (int i = 0; i < 4; i++) {
if (i != tri_it->second) {
of << points.find(tmp_tetra->vertex(i)->point())->second << " ";
}
}
of << std::endl;
}
// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#af31db7673a6d7d28c0bb90a3115ac695
for (auto e : edges) {
of << "2 ";
auto tmp_tetra = e.get<0>();
int p1, p2;
p1 = e.get<1>();
p2 = e.get<2>();
of << points.find(tmp_tetra->vertex(p1)->point())->second << " "
<< points.find(tmp_tetra->vertex(p2)->point())->second << std::endl;
}
of << std::endl;
of << "CELL_TYPES " << tetra_num + tri_num + edge_num << std::endl;
for (int i = 0; i < tetra_num; i++) {
of << "10 ";
}
for (int i = 0; i < tri_num; i++) {
of << "5 ";
}
for (int i = 0; i < edge_num; i++) {
of << "3 ";
}
of << std::endl;
of.close();
}

How enable dragging a file on the *.exe and get it as parameter?

What do I have to do to make my program use a file that has been dragged and dropped onto its icon as a parameter?
My current main method looks like this:
int main(int argc, char* argv[])
{
if (argc != 2) {
cout << "ERROR: Wrong amount of arguments!" << endl;
cout << "\n" << "Programm closed...\n\n" << endl;
exit(1);
return 0;
}
Converter a(argv[1]);
// ...
cout << "\n" << "Programm finished...\n\n" << endl;
// cin.ignore();
return 0;
}
What I'd really like to be able to do is select 10 (or so) files, drop them onto the EXE, and process them from within my application.
EDIT:
The incomming parameter is used as filename, constructed in the cunstructor.
Converter::Converter(char* file) {
// string filename is a global variable
filename = file;
myfile.open(filename.c_str(), ios_base::in);
}
The method where the textfile gets read:
string Converter::readTextFile() {
char c;
string txt = "";
if (myfile.is_open()) {
while (!myfile.eof()) {
myfile.get(c);
txt += c;
}
} else {
error("ERROR: can't open file:", filename.c_str());
}
return txt;
}
EDIT2:
deleted
Update:
I got again to this point.
Actual Main method:
// File path as argument
int main(int argc, char* argv[]) {
if (argc < 2) {
cout
<< "ERROR: Wrong amount of arguments! Give at least one argument ...\n"
<< endl;
cout << "\n" << "Programm closed...\n\n" << endl;
cin.ignore();
exit(1);
return 0;
}
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename = s.substr(pos + 1);
cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\n pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getATCommandsFromCSV();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}
Actually the console window apears for maybe 0.5 sec and closes again.
It doen't stop on any of my cin.ignore(); Maybe it doesn't get there?
Can anyone help?
Your program does not need to do anything special apart from handling command-line arguments. When you drag-drop a file onto an application in Explorer it does nothing more than to pass the file name as argument to the program. Likewise for multiple files.
If all you expect is a list of file names, then just iterate over all arguments, do whatever you want with them and be done. This will work for zero to almost arbitrarily many arguments.
Maybe you could write a test program like this:
int main(int argc, char* argv[])
{
// argv[0] is not interesting, since it's just your program's path.
for (int i = 1; i < argc, ++i)
cout << "argv[" << i << "] is " << argv[i] << endl;
return 0;
}
And see what happens after you throw different files at it.
EDIT: Just look at Joey's answer.
Answer to the main question
TO SEE THE ANSWER TO YOUR LAST PROBLEM SEE BOTTOM OF THIS ANSWER
All drag&dropped files are get-able as argv[orderOfTheFile] (orderOfTheFile is from 1-n),
however how does windows create that order, now that is a real mystery...
Anyway let's say I would create 26 plain text files ( *.txt ), from a.txt to z.txt on my Desktop,
now if I would drag&dropped them on my ArgsPrinter_c++.exe located directly on C:\ drive,
an output would be similar to this:
argc = 27
argv[0] = C:\ArgsPrinter_c++.exe
argv[1] = C:\Users\MyUserName\Desktop\c.txt
argv[2] = C:\Users\MyUserName\Desktop\d.txt
argv[3] = C:\Users\MyUserName\Desktop\e.txt
argv[4] = C:\Users\MyUserName\Desktop\f.txt
argv[5] = C:\Users\MyUserName\Desktop\g.txt
argv[6] = C:\Users\MyUserName\Desktop\h.txt
argv[7] = C:\Users\MyUserName\Desktop\i.txt
argv[8] = C:\Users\MyUserName\Desktop\j.txt
argv[9] = C:\Users\MyUserName\Desktop\k.txt
argv[10] = C:\Users\MyUserName\Desktop\l.txt
argv[11] = C:\Users\MyUserName\Desktop\m.txt
argv[12] = C:\Users\MyUserName\Desktop\n.txt
argv[13] = C:\Users\MyUserName\Desktop\o.txt
argv[14] = C:\Users\MyUserName\Desktop\p.txt
argv[15] = C:\Users\MyUserName\Desktop\q.txt
argv[16] = C:\Users\MyUserName\Desktop\r.txt
argv[17] = C:\Users\MyUserName\Desktop\s.txt
argv[18] = C:\Users\MyUserName\Desktop\t.txt
argv[19] = C:\Users\MyUserName\Desktop\u.txt
argv[20] = C:\Users\MyUserName\Desktop\v.txt
argv[21] = C:\Users\MyUserName\Desktop\w.txt
argv[22] = C:\Users\MyUserName\Desktop\x.txt
argv[23] = C:\Users\MyUserName\Desktop\y.txt
argv[24] = C:\Users\MyUserName\Desktop\z.txt
argv[25] = C:\Users\MyUserName\Desktop\a.txt
argv[26] = C:\Users\MyUserName\Desktop\b.txt
My ArgsPrinter_c++.exe source code:
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
cout << "argc = " << argc << endl;
for(int i = 0; i < argc; i++)
cout << "argv[" << i << "] = " << argv[i] << endl;
std::cin.ignore();
return 0;
}
Your last problem
I have created a simple program that creates only a sceleton of your class so it can be used, and the program's main itself ran JUST FINE => if your program exits too soon, the problem will be in your class...
Tested source code:
#include <iostream>
#include <vector>
using namespace std;
class Converter{
public:
Converter(const char* f){ cout << f << endl; }
void getATCommandsFromCSV(){ cout << "called getATCommandsFromCSV" << endl; }
};
int main(int argc, char* argv[]) {
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename = s.substr(pos + 1);
cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\n pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getATCommandsFromCSV();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}