scanning-and-process-sample.cpp

This comprehensive code sample includes the essential scanning and processing steps to produce a complete 3D model.

The code initializes the scanner and captures a series of frames, registers and processes, then combines them into a single 3D mesh. Functions in the artec::sdk::algorithms namespace handle the processing. Using this sample you can store models, both textured and untextured as well as raw frames into OBJ file. The final step is to display a model in a 3D viewer by calling meshContainer directly.

The files required for compiling this sample are packed in the archive.
Note
The sample requires the GLFW library to function. This library is included in the archive. When loading the solution in Microsoft Visual Studio, maintain the original folder hierarchy.
/********************************************************************
*
* Project Artec 3D Scanning SDK Samples
*
* Purpose: Scanning and processing sample
*
* Copyright: Artec Group
*
********************************************************************/
#include <iomanip>
#include <iostream>
#include "ScenePresenter.h"
namespace asdk {
using namespace artec::sdk::base;
using namespace artec::sdk::capturing;
using namespace artec::sdk::scanning;
using namespace artec::sdk::algorithms;
};
using asdk::TRef;
// this constant determines the number of frames to collect.
const int NumberOfFramesToCapture = 100;
// Texture processing needs a lot of processing power and a suitable
// graphic card. Define the following macro if you wish to include it
// into this sample.
#define ENABLE_TEXTURE_MAPPING
// Saving the results takes time and needs considerable amount
// of disc space. Uncomment one or both of the following macros
// in order to enable it. Make sure you have a subdirectory
// designated as OUTPUT_DIR in the current directory
// for output files to be placed.
#define OUTPUT_DIR L"scans"
//#define SAVE_FUSION_MESH_ON
//#define SAVE_TEXTURED_MESH_ON
// simple error log handling for SDK calls
#define SDK_STRINGIFY(x) #x
#define SDK_STRING(x) SDK_STRINGIFY(x)
#define SAFE_SDK_CALL(x) \
{ \
asdk::ErrorCode ec = (x); \
if ( ec != asdk::ErrorCode_OK ) \
{ \
reportError( ec, __FILE__ " [ line " SDK_STRING(__LINE__) " ]"); \
return ec; \
} \
}
void reportError( asdk::ErrorCode ec, const char *place )
{
const wchar_t* msg = L"No error";
switch( ec ){
msg = L"Not enough storage is available to process the operation";
break;
msg = L"Provided argument is invalid";
break;
msg = L"Requested operation is invalid";
break;
msg = L"Data format is unsupported or invalid";
break;
msg = L"Requested scanner is not connected";
break;
msg = L"Requested scanner is not licensed";
break;
msg = L"Requested scanner is already used by someone else";
break;
msg = L"Scanner initialization failed";
break;
msg = L"Frame is corrupted";
break;
msg = L"Frame reconstruction failed";
break;
msg = L"Frame registration failed";
break;
msg = L"Requested operation is unsupported. Check versions";
break;
msg = L"Requested operation is denied. Check your license(s)";
break;
msg = L"Requested operation has failed";
break;
msg = L"Requested operation was canceled from client's side";
break;
default:
msg = L"Unexplained error";
break;
}
std::wcerr << msg << " [error " << std::hex << ec << "] " << "at " << place << std::endl;
}
// simple demonstration handler for scanner events
class SimpleScannerObserver : public asdk::ScannerObserverBase
{
public:
// scanner event handling
virtual void buttonPressed( asdk::ScannerButton button )
{
switch(button)
{
std::wcout << L"ScannerEvent: trigger button was pressed" << std::endl;
break;
std::wcout << L"ScannerEvent: stop button was pressed" << std::endl;
break;
std::wcout << L"ScannerEvent: record button was pressed" << std::endl;
break;
default:
std::wcout << L"ScannerEvent: unknown button was pressed" << std::endl;
break;
}
}
virtual void deviceOverheated()
{
std::wcout << L"ScannerEvent: device is overheated" << std::endl;
}
virtual void deviceTemperatureBackToNormal()
{
std::wcout << L"ScannerEvent: device temperature is back to normal" << std::endl;
}
virtual void deviceDisconnected()
{
std::wcout << L"ScannerEvent : device was disconnected" << std::endl;
}
};
// creator function for simple demonstration handler defined above
//
// This function is used to initialize TRef<asdk::IScannerObserver>, and
// this is a preferred way to deal with current implementation of TRef.
// Please don't assign a newly created object directly to TRef as this
// may cause memory leaks.
asdk::ErrorCode createSimpleScannerObserver( asdk::IScannerObserver** observer )
{
*observer = new SimpleScannerObserver();
}
// scanning procedure sample
asdk::ErrorCode ScanningProcedureSample( asdk::AlgorithmWorkset& workset )
{
std::wcout << L"Looking for scanner..." << std::endl;
asdk::ErrorCode ec = asdk::createScanner( &scanner, NULL );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"No scanners found" << std::endl;
return ec;
}
std::wcout << L"ok" << std::endl;
std::wcout << L"Scanner found with serial number " << scanner->getId()->serial << std::endl;
std::wcout << L"Setting the scanner event handler..." << std::endl;
createSimpleScannerObserver( &observer );
SAFE_SDK_CALL( scanner->setObserver( observer ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Scanner is ready, press ENTER to start" << std::endl;
std::wcin.setf( ~std::ios::skipws, std::ios::skipws );
std::wcin.get();
std::wcout << L"Creating scanning procedure..." << std::endl;
asdk::ScanningProcedureSettings desc = { 0 };
desc.maxFrameCount = NumberOfFramesToCapture;
desc.initialState = asdk::ScanningState_Record;
desc.pipelineConfiguration =
;
desc.captureTextureFrequency = 10;
desc.ignoreRegistrationErrors = false;
SAFE_SDK_CALL( asdk::createScanningProcedure( &scanning, scanner, &desc ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Launching scanning procedure in fully automatic mode..." << std::endl;
SAFE_SDK_CALL( executeJob( scanning, &workset ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Preparing workset for further processing..." << std::endl;
std::swap( workset.in, workset.out );
workset.out->clear();
std::wcout << L"ok" << std::endl;
}
// example of the post-scanning processing
asdk::ErrorCode AlgorithmProcessingSample( asdk::AlgorithmWorkset& workset )
{
// get scanner type from the very first scan in workset
asdk::ScannerType scannerType = (asdk::ScannerType)workset.in->getElement(0)->getScannerType();
// apply serial registration
{
std::wcout << L"Creating serial registration procedure..." << std::endl;
TRef<asdk::IAlgorithm> serialRegistration;
asdk::SerialRegistrationSettings serialDesc = {
};
SAFE_SDK_CALL( asdk::createSerialRegistrationAlgorithm( &serialRegistration, &serialDesc ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Launching the serial registration algorithm..." << std::endl;
SAFE_SDK_CALL( asdk::executeJob( serialRegistration, &workset ) );
std::wcout << L"ok" << std::endl;
}
// prepare serial registration output for the global registration
std::swap( workset.in, workset.out );
workset.out->clear();
// proceed with global registration
{
std::wcout << L"Creating global registration procedure..." << std::endl;
TRef<asdk::IAlgorithm> globalRegistration;
asdk::GlobalRegistrationSettings globalDesc = {
};
SAFE_SDK_CALL( asdk::createGlobalRegistrationAlgorithm( &globalRegistration, &globalDesc ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Launching the global registration algorithm..." << std::endl;
SAFE_SDK_CALL( asdk::executeJob( globalRegistration, &workset ) );
std::wcout << L"ok" << std::endl;
}
// prepare global registration output for outliers removal
std::swap( workset.in, workset.out );
workset.out->clear();
// apply outliers removal
{
std::wcout << L"Creating outliers removal procedure..." << std::endl;
asdk::OutliersRemovalSettings outliersDesc;
// get default settings
SAFE_SDK_CALL( asdk::initializeOutliersRemovalSettings( &outliersDesc, scannerType ) );
SAFE_SDK_CALL( asdk::createOutliersRemovalAlgorithm( &noOutliers, &outliersDesc ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Launching the outliers removal algorithm..." << std::endl;
SAFE_SDK_CALL( asdk::executeJob( noOutliers, &workset ) );
std::wcout << L"ok" << std::endl;
}
// prepare outliers removal results for fusion input
std::swap( workset.in, workset.out );
workset.out->clear();
// apply fast fusion
{
std::wcout << L"Creating fast fusion procedure..." << std::endl;
asdk::FastFusionSettings fusionDesc;
// get default settings
SAFE_SDK_CALL( asdk::initializeFastFusionSettings( &fusionDesc, scannerType ) );
fusionDesc.resolution = 2.f;
SAFE_SDK_CALL( asdk::createFastFusionAlgorithm( &fusion, &fusionDesc ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Launching the fast fusion algorithm..." << std::endl;
SAFE_SDK_CALL( asdk::executeJob( fusion, &workset ) );
std::wcout << L"ok" << std::endl;
}
std::wcout << L"Preparing workset for further processing..." << std::endl;
std::swap( workset.in, workset.out );
workset.out->clear();
std::wcout << L"ok" << std::endl;
}
#ifdef ENABLE_TEXTURE_MAPPING
// texture mapping snippet
asdk::ErrorCode TextureProcessingSample( asdk::AlgorithmWorkset& workset )
{
// get scanner type from the very first scan in workset
asdk::ScannerType scannerType = (asdk::ScannerType)workset.in->getElement(0)->getScannerType();
// apply texture mapping
{
std::wcout << L"Creating texture mapping procedure..." << std::endl;
asdk::TexturizationSettings textureDesc;
// get default settings
SAFE_SDK_CALL( asdk::initializeTexturizationSettings( &textureDesc, scannerType ) );
textureDesc.texturizeType = asdk::TexturizeType_Atlas;
SAFE_SDK_CALL( asdk::createTexturizationAlgorithm( &texturize, &textureDesc ) );
std::wcout << L"ok" << std::endl;
std::wcout << L"Launching the texture mapping algorithm..." << std::endl;
SAFE_SDK_CALL( asdk::executeJob( texturize, &workset ) );
std::wcout << L"ok" << std::endl;
}
std::wcout << L"Preparing workset for further processing..." << std::endl;
std::swap( workset.in, workset.out );
workset.out->clear();
std::wcout << L"ok" << std::endl;
}
#endif
int main( int argc, char **argv )
{
// The log verbosity level is set here. It is set to the most
// verbose value - Trace. If you have any problems working with
// our examples, please do not hesitate to send us this extensive
// information along with your questions. However, if you feel
// comfortable with these Artec Scanning SDK code examples,
// we suggest you to set this level to asdk::VerboseLevel_Info.
// create workset for scanned data
TRef<asdk::IModel> inputContainer;
TRef<asdk::IModel> outputContainer;
SAFE_SDK_CALL( asdk::createModel( &inputContainer ) );
SAFE_SDK_CALL( asdk::createModel( &outputContainer ) );
SAFE_SDK_CALL( asdk::createCancellationTokenSource( &ctSource ) );
asdk::AlgorithmWorkset workset = { inputContainer, outputContainer, 0, ctSource->getToken(), 0 };
asdk::ErrorCode ec = ScanningProcedureSample( workset );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"Finishing work on errors when scanning..." << std::endl;
return (int)ec;
}
ec = AlgorithmProcessingSample( workset );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"Finishing work on errors when processing..." << std::endl;
return (int)ec;
}
#ifdef SAVE_FUSION_MESH_ON
// saving the resulting mesh to OBJ format
{
asdk::ICompositeContainer* meshContainer = workset.in->getCompositeContainer();
if( meshContainer && meshContainer->getSize() > 0 )
{
asdk::ICompositeMesh* resultMesh = meshContainer->getElement( 0 );
std::wcout << L"Saving the resulting mesh to an OBJ file..." << std::endl;
const wchar_t* filename = OUTPUT_DIR L"\\untextured-mesh.obj";
asdk::ErrorCode ec = asdk::io::saveObjCompositeToFile( filename, resultMesh );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"Cannot open file '" << filename << L"'" << std::endl;
std::wcout << L"skipped" << std::endl;
}
else
{
std::wcout << L"ok" << std::endl;
}
}
}
// saving all frame meshes to OBJ format
{
std::wcout << L"Saving all the reconstructed frames to separate OBJ files..." << std::endl;
for( int ix = 0; ix < workset.in->getSize(); ix++ )
{
TRef<asdk::IScan> scan( workset.in->getElement( ix ) );
for( int jx = 0; jx < scan->getSize(); jx++ )
{
TRef<asdk::IFrameMesh> frameMesh( scan->getElement( jx ) );
std::wstring pathFormat( OUTPUT_DIR L"\\frame-S%02dF%02d.obj" );
std::vector<wchar_t> pathBuffer( pathFormat.size() +1 );
std::swprintf( pathBuffer.data(), pathBuffer.size(), pathFormat.c_str(), ix, jx );
ec = asdk::io::saveObjFrameToFile( pathBuffer.data(), frameMesh );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"Cannot open file '" << pathBuffer.data() << "'" << std::endl;
std::wcout << L"skipped" << std::endl;
break;
}
}
}
if( ec == asdk::ErrorCode_OK )
{
std::wcout << L"ok" << std::endl;
}
}
#endif
#ifdef ENABLE_TEXTURE_MAPPING
ec = TextureProcessingSample( workset );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"failed" << std::endl;
std::wcout << L"Continue to work withstanding errors when texturing..." << std::endl;
}
else
{
#ifdef SAVE_TEXTURED_MESH_ON
// saving the resulting texture to OBJ format
{
asdk::ICompositeContainer* meshContainer = workset.in->getCompositeContainer();
if( meshContainer && meshContainer->getSize() > 0 )
{
asdk::ICompositeMesh* resultMesh = meshContainer->getElement( 0 );
std::wcout << L"Saving the resulting textured mesh to an OBJ file..." << std::endl;
const wchar_t* filename = OUTPUT_DIR L"\\textured-mesh.obj";
asdk::ErrorCode ec = asdk::io::saveObjCompositeToFile( filename, resultMesh );
if( ec != asdk::ErrorCode_OK )
{
std::wcout << L"Cannot open file '" << filename << "'" << std::endl;
std::wcout << L"skipped" << std::endl;
}
else
{
std::wcout << L"ok" << std::endl;
}
}
}
#endif
}
#endif
// demonstrating the results via the simple GLFW viewer
{
asdk::ICompositeContainer* meshContainer = workset.in->getCompositeContainer();
if( meshContainer && meshContainer->getSize() > 0 )
{
asdk::ICompositeMesh* resultMesh = meshContainer->getElement( 0 );
std::wcout << L"Showing the resulting mesh..." << std::endl;
SAFE_SDK_CALL( DisplayScene( *resultMesh ) );
std::wcout << L"ok" << std::endl;
}
}
std::wcout << L"Finishing work with capturing library..." << std::endl;
return (int)ec;
}