Artec 3D Scanning SDK  2.0
project-sample.cpp

This code sample shows how to align scans using the SDK and store the results into an Artec Studio project. It performs in sequence the following operations:

The files required for compiling this sample are packed in the archive.
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS 1
#endif // _MSC_VER
#include <boost/atomic.hpp>
#include <boost/thread.hpp>
#include <iomanip>
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace artec::sdk;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper classes, functions and macros
// Simple error log handling for SDK calls
#define SDK_STRINGIFY(x) #x
#define SDK_STRING(x) SDK_STRINGIFY(x)
#define SAFE_SDK_CALL(x) \
{ \
base::ErrorCode ec = (x); \
if ( ec != base::ErrorCode_OK ) \
{ \
reportError( ec, __FILE__ " [ line " SDK_STRING(__LINE__) " ]"); \
return ec; \
} \
}
void printUsage()
{
std::cout << "Usage:" << '\n';
std::cout << " project-sample.exe input_project_path output_project_path" << '\n';
std::cout << '\n';
std::cout << " input_project_path \t path to a valid Arteс Studio project which must exist" << '\n';
std::cout << " output_project_path \t path to the destination project which must NOT exist" << '\n';
std::cout << '\n';
std::cout << "Example:" << '\n';
std::cout << " project-sample.exe C:\\project1\\project1.sproj C:\\processed\\processed.sproj" << std::endl;
std::cout << '\n';
}
std::wstring stringToWString(const std::string& src)
{
std::wstring result(src.length(), L' ');
mbstowcs(&result[0], src.c_str(), src.length());
return result;
}
void reportError(base::ErrorCode ec, const char* place)
{
const char* msg = "No error";
switch (ec)
{
msg = "Not enough storage is available to process the operation";
break;
msg = "Provided argument is invalid";
break;
msg = "Requested operation is invalid";
break;
msg = "Data format is unsupported or invalid";
break;
msg = "Requested scanner is not connected";
break;
msg = "Requested scanner is not licensed";
break;
msg = "Requested scanner is already used by someone else";
break;
msg = "Scanner initialization failed";
break;
msg = "Frame is corrupted";
break;
msg = "Frame reconstruction failed";
break;
msg = "Frame registration failed";
break;
msg = "Requested operation is unsupported. Check versions";
break;
msg = "Requested operation is denied. Check your license(s)";
break;
msg = "Requested operation has failed";
break;
msg = "Requested operation was canceled from client's side";
break;
msg = "Unable to start algorithm because input data turned out to be invalid. Please rescan the object.";
break;
default:
msg = "Unexplained error";
break;
}
std::cerr << msg << " [error " << std::hex << ec << "] " << "at " << place << std::endl;
}
// Objects of this class yield processor time to other threads while waiting for a completion signal
class SleepingObserver : public base::JobObserverBase
{
public:
SleepingObserver()
: done_(false)
, result_(base::ErrorCode_OK)
{
}
void completed(base::ErrorCode result) override
{
result_ = result;
done_ = true;
}
void waitForCompletion() const
{
while (!done_)
{
boost::this_thread::yield();
}
}
base::ErrorCode getResult() const
{
return result_;
}
private:
boost::atomic<bool> done_;
base::ErrorCode result_;
}; // class SleepingObserver
base::ErrorCode createSleepingObserver(SleepingObserver** observer)
{
if (!observer)
{
}
*observer = new SleepingObserver;
}
// This progress implementation simply prints the current progress to the console
class SimpleConsoleProgress : public base::ProgressInfoBase
{
public:
SimpleConsoleProgress(const char* name)
: name_(name)
{
}
private:
void report(int current, int total) override
{
std::cout << name_ << ": " << current << " of " << total << std::endl;
}
void pulse() override {}
void notify(base::DetailsInfo details) override {}
private:
const std::string name_;
}; // Class SimpleConsoleProgress
// Run a job asynchronously with progress tracking and release a reference to the job on success
base::ErrorCode runJobAsynchronously(
base::IModel* srcModel, base::IModel* trgModel, base::IJob* job, const char* jobName)
{
// And use it to align the scans
workset.in = srcModel;
workset.out = trgModel;
SimpleConsoleProgress* const progress = new SimpleConsoleProgress(jobName);
progressRef.attach(progress);
workset.progress = progressRef;
workset.cancellation = NULL;
createSleepingObserver(&observer);
SAFE_SDK_CALL(base::launchJob(job, &workset, observer));
// Here, a real-world application should perform some useful operations instead of waiting
observer->waitForCompletion();
return observer->getResult();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Functions that constitute the program major operations
// Load scans from an input project
{
// Load two scans
const int numScansToLoad = 2;
SAFE_SDK_CALL(createArrayUuid(&uuids, numScansToLoad));
int numScans = 0;
const int numEntries = project->getEntryCount();
for (int i = 0; i < numEntries && numScans < numScansToLoad; ++i)
{
SAFE_SDK_CALL(project->getEntry(i, &entry));
{
uuids->setElement(numScans, entry.uuid);
++numScans;
}
}
// Check the number of scans: the alignment algorithm requires this number to be no less than 2
if (numScans < numScansToLoad)
{
std::cerr << "Not enough scans to align." << std::endl;
}
// Load the scans
settings.entryList = uuids;
SAFE_SDK_CALL(project->createLoader(&loader, &settings));
SAFE_SDK_CALL(base::createModel(pModel));
SAFE_SDK_CALL(runJobAsynchronously(nullptr, *pModel, loader, "Loading scans"));
}
// Apply auto-alignment to the IModel's scans
base::ErrorCode autoAlignModelScans(base::IModel* model, base::IModel* targetModel)
{
// Calculate normals to vertices for each frame in order to prepare the scans for alignment
const int numScans = model->getSize();
for (int i = 0; i < numScans; ++i)
{
base::IScan* const scan = model->getElement(i);
const int numFrames = scan->getSize();
for (int j = 0; j < numFrames; ++j)
{
base::IFrameMesh* const frame = scan->getElement(j);
}
}
// Remember transformation matrix of the second scan
const base::Matrix4x4D& secondScanTransformationBefore = model->getElement(1)->getScanTransformation();
// Now create an Auto-alignment algorithm instance
{
algorithms::AutoAlignSettings autoAlignSettings;
SAFE_SDK_CALL(algorithms::createAutoalignAlgorithm(&autoAlignAlgorithm, &autoAlignSettings));
SAFE_SDK_CALL(runJobAsynchronously(model, targetModel, autoAlignAlgorithm, "Auto-aligning"));
}
// Check whether the second scan has changed its position
const base::Matrix4x4D& secondScanTransformationAfter = targetModel->getElement(1)->getScanTransformation();
if (secondScanTransformationAfter == secondScanTransformationBefore)
{
std::cerr << "No alignment happened." << std::endl;
}
}
// Make a model (3D mesh) out of the aligned scans
base::ErrorCode fuseModelScans(base::IModel* model, base::IModel* targetModel)
{
// Run Global registration
base::TRef<base::IModel> registrationTargetModel;
SAFE_SDK_CALL(base::createModel(&registrationTargetModel));
{
registrationSettings.scannerType = base::ScannerType_Unknown;
base::TRef<algorithms::IAlgorithm> registrationAlgorithm;
SAFE_SDK_CALL(algorithms::createGlobalRegistrationAlgorithm(&registrationAlgorithm, &registrationSettings));
SAFE_SDK_CALL(runJobAsynchronously(model, registrationTargetModel, registrationAlgorithm, "Global registration"));
}
// Apply a Fast-fusion algorithm
{
algorithms::FastFusionSettings fastFusionSettings;
SAFE_SDK_CALL(algorithms::createFastFusionAlgorithm(&fastFusionAlgorithm, &fastFusionSettings));
SAFE_SDK_CALL(runJobAsynchronously(registrationTargetModel, targetModel, fastFusionAlgorithm, "Fast fusion"));
}
// Check the presence of a model
const base::ICompositeContainer* const compositeContainer = targetModel->getCompositeContainer();
if (compositeContainer == NULL || compositeContainer->getSize() < 1)
{
std::cerr << "No model mesh created." << std::endl;
}
}
// Save the IModel object to an Artec Studio project
base::ErrorCode saveModelAsProject(base::IModel* model, project::IProject* project, const wchar_t* projectDstPath)
{
saveSettings.path = projectDstPath;
SAFE_SDK_CALL(base::generateUuid(&saveSettings.projectId));
SAFE_SDK_CALL(project->createSaver(&saver, &saveSettings));
SAFE_SDK_CALL(runJobAsynchronously(model, nullptr, saver, "Saving project"));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
if (argc < 3)
{
printUsage();
return -1;
}
const std::wstring srcProjectPath = stringToWString(argv[1]);
SAFE_SDK_CALL(project::openProject(&srcProject, srcProjectPath.c_str()));
if (loadInputScans(srcProject, &srcModel) != base::ErrorCode_OK)
{
std::cerr << "Failed to load input data. Exiting..." << std::endl;
return 1;
}
base::TRef<base::IModel> alignTargetModel;
SAFE_SDK_CALL(base::createModel(&alignTargetModel));
if (autoAlignModelScans(srcModel, alignTargetModel) != base::ErrorCode_OK)
{
std::cerr << "Auto-alignment failed. Exiting..." << std::endl;
return 1;
}
base::TRef<base::IModel> fusionTargetModel;
SAFE_SDK_CALL(base::createModel(&fusionTargetModel));
if (fuseModelScans(alignTargetModel, fusionTargetModel) != base::ErrorCode_OK)
{
std::cerr << "3D mesh model creation failed. Exiting..." << std::endl;
return 1;
}
const std::wstring dstProjectPath = stringToWString(argv[2]);
if (saveModelAsProject(fusionTargetModel, srcProject, dstProjectPath.c_str()) != base::ErrorCode_OK)
{
std::cerr << "Project saving failed. Exiting..." << std::endl;
return 1;
}
return 0;
}