Cos’è FlowSDK?

FlowSDK è il Software Development Kit perfetto: un motore di ricostruzione fotogrammetrico potente e completamente personalizzabile, scritto in C++ per piattaforme Windows.

Con FlowSDK, sei libero di sviluppare la tua applicazione di ricostruzione 3D, sfruttando lo stato dell’arte della tecnologia fotogrammetrica di 3Dflow. Il nostro prodotto principale, 3DF Zephyr, utilizza la stessa identica tecnologia del nostro SDK.

Con FlowSDK, catturare la realtà con la fotogrammetria non è mai stato così facile.

Come funziona?

FlowSDK è distribuito tramite due dynamic link libraries per Microsoft Windows. Le interfacce Structure from motion e Multiview stereo sono scritte in C++ e sono facilissime da usare. Puoi controllare completamente il processo di ricostruzione in ogni sua fase, dall’orientamento delle camere alla generazione della mesh con texture. I parametri possono essere passati direttamente via codice o tramite comodi file XML, compatibili al 100% con 3DF Zephyr.

Con che licenza viene venduto? A che prezzo?

FlowSDK può essere usato potenzialmente in qualsiasi scenario (per esempio in un server interno per ricerca e sviluppo, una componente aggiuntiva per un prodotto commerciale, …. ) e copre un numero illimitato di ricostruzioni (una volta acquistata una licenza, non dovrete pagare royalties o altri costi nascosti).

La licenza di FlowSDK license è  per installazione, e legato direttamente allo scenario di utilizzo. Questo significa che il prezzo non è fisso, ma viene bensì personalizzato per la tua compagnia, garantendo un costo adatto allo scenario.

Dove trovo la documentazione? Un esempio?

La documentazione è disponibile all’URL https://www.3dflow.net/3DFSDKdoc/

Inoltre, un progetto di esempio Visual Studio 2015 è incluso nell’SDK. Trovi di seguito un esempio:

// Application main loop
void run( int argc, char **argv )
{
    // Prepare the log listener to let the application write the log to file
    std::string   logFileName( "log.txt" );
    std::ofstream myfile;

    myfile.open( logFileName );

    FlowCore::LogListenerOStream *stdOutLogListener = NULL;

    if ( myfile.good() )
    {
        stdOutLogListener = new FlowCore::LogListenerOStream( &myfile );
    }
    else
    {
        // Write log on the std::out only
        stdOutLogListener = new FlowCore::LogListenerOStream();
        std::cout << "Cannot write log to file" << std::endl;
    }

    // Declare data needed by Samantha. This will be filled by Samantha during computation.
    // List of cameras and a point cloud
    std::vector< FlowSaM::CameraInterface * >  cameraVector;
    FlowSaM::PointCloudInterface              *pointCloud = new FlowSaM::PointCloudInterface();

    // Get all the files in the ./Images folder
    WIN32_FIND_DATA data;
    std::wstring    path( L"./Images/*.*" );
    HANDLE          hFile = FindFirstFile( path.c_str(), &data );

    if ( hFile == INVALID_HANDLE_VALUE )
    {
        std::cout << "No file to load" << std::endl;
        return;
    }

    while ( FindNextFile( hFile, &data ) != 0 || GetLastError() != ERROR_NO_MORE_FILES )
    {
        if ( std::wstring( L".." ).compare( data.cFileName ) == 0 || std::wstring( L"." ).compare( data.cFileName ) == 0 )
            continue;

        // Push a new camera object on the vector. Samantha just need the camera filename
        // Supported format are jpg, png, bmp, tiff.
        FlowSaM::CameraInterface *cam = new FlowSaM::CameraInterface();
        std::wstring              ws  = std::wstring( L"./Images/" ) + data.cFileName;
        int                       i   = 0;

        for ( std::wstring::iterator it = ws.begin(); it != ws.end(); ++it, ++i )
            cam->mFilePath[ i ] = ( *it );

        cam->mFilePath[ i ] = '\0';


        cameraVector.push_back( cam );
    }

    // create output directory
    CreateDirectory( L".\\out\\", NULL );

    // Create the samantha object
    FlowSaM::SamanthaInterface *sammy = dynamic_cast< FlowSaM::SamanthaInterface * >( FlowSaM::CreateSamanthaInterfaceObject() );

    // Feed samantha - required input
    for ( size_t i = 0; i < cameraVector.size(); ++i )
        sammy->addCamera( cameraVector[ i ] );

    sammy->setPointCloud( pointCloud );

    // optional stuff
    sammy->setLogListener( stdOutLogListener );

    // run samantha
    int errCode = sammy->compute();

    // Test code for triangulation a 3D point
    if ( pointCloud->mNumberOfPoints > 0 )
    {
        // Read the first point image coords
        std::vector< unsigned int > camViewingTheFirstPoint;                                                 // #tracklength
        std::vector< double >       camCoordsViewingTheFirstPoint;                                           // #2 * tracklength

        for ( unsigned int i = 0; i < static_cast< unsigned int >( cameraVector.size() ); ++i )
        {
            if ( cameraVector[ i ]->mVisiblePointsCount > 0 && cameraVector[ i ]->mVisiblePoints[ 0 ] == 0 ) // First point viewed by this camera
            {
                camViewingTheFirstPoint.push_back( i );
                camCoordsViewingTheFirstPoint.push_back( cameraVector[ i ]->mVisiblePointsCoords[ 0 ] );
                camCoordsViewingTheFirstPoint.push_back( cameraVector[ i ]->mVisiblePointsCoords[ 1 ] );
            }
        }

        // .. and triangulate it
        double triangulatePt[ 3 ];
        sammy->triangulatePoint( static_cast< unsigned int >( camViewingTheFirstPoint.size() ), &camViewingTheFirstPoint[ 0 ], &camCoordsViewingTheFirstPoint[ 0 ], triangulatePt );

        // position can be (slightly) different because the points coming from the SaMInterface are triangulated with a different procedure
        double dx = pointCloud->mPositions[ 0 ] - triangulatePt[ 0 ];
        double dy = pointCloud->mPositions[ 1 ] - triangulatePt[ 1 ];
        double dz = pointCloud->mPositions[ 2 ] - triangulatePt[ 2 ];
    }

    // You can safely delete the samantha object after computation
    delete sammy;

    if ( errCode == 1 )
    {
        // Success - level up
        // Count number of reconstructed cameras and remove invalid one
        std::vector< FlowSaM::CameraInterface * >::iterator it = cameraVector.begin();

        while ( it != cameraVector.end() )
        {
            if ( ( *it )->mVisiblePointsCount == 0 )
            {
                delete ( *it );
                it = cameraVector.erase( it );
            }
            else
            {
                ++it;
            }
        }

        // Setup Stereo data - Stesha will fill this mesh
        FlowStesha::StereoMeshInterface *stereoMesh = new FlowStesha::StereoMeshInterface();

        // Create the stasia object
        FlowStesha::SteshaInterface *stesha = dynamic_cast< FlowStesha::SteshaInterface * >( FlowStesha::CreateSteshaInterfaceObject() );

        // Feed Stesha - required input
        for ( size_t i = 0; i < cameraVector.size(); ++i )
            stesha->addCamera( cameraVector[ i ] );

        stesha->setPointCloud( pointCloud ); // Samantha output
        stesha->setStereoMesh( stereoMesh ); // Samantha output

        // optional stuff
        stesha->setLogListener( stdOutLogListener );

        stesha->setSettingValue( "Workspace", "ExportPath", "./Out/" );
        stesha->setSettingValue( "Texture", "TextureSize", "4096" );

        // Compute and generate both a colored mesh and a texture.
        bool generateMesh     = true;
        bool generateTextures = true;
        errCode = stesha->compute( generateMesh, generateTextures );

        if ( errCode == 1 )
        {
            // Export as 3DK, a file that can be opened by 3DF Zephyr
            stesha->save( "./Out/out.3dk" );

            // Export also the final mesh as ply or obj+texture
            if ( !generateTextures )
                exportPly( stereoMesh, "./Out/mesh.ply" );
            else
                exportObj( stereoMesh, "./Out/", "TexturedMesh" );

            std::cout << "All done." << std::endl;
        }
        else
        {
            std::cout << "Stasia computation failed. Error code: " << errCode << std::endl;
        }

        // You can safely delete the stesha object after computation
        delete stesha;

        // Delete stereo mesh
        delete stereoMesh;
    }
    else
    {
        std::cout << "Samantha computation failed. Error code: " << errCode << std::endl;
    }

    // Clean up
    if ( stdOutLogListener )
        delete stdOutLogListener;

    delete pointCloud;

    for ( size_t i = 0; i < cameraVector.size(); ++i )
        delete cameraVector[ i ];

    cameraVector.clear();
}

Quali sono gli input e output ?

L’SDK è completamente indipendente. Puoi caricare qualsiasi immagine (JPG, PNG…) e puoi esportare direttamente nel nostro formato nativo (.3dk) per leggerlo in Zephyr, o puoi esportarlo nel metodo che preferisci. Sei libero in qualunque momento di accedere a tutte le informazioni generate dalla nostra pipeline di ricostruzione.

Ottimo! Come faccio a iniziare??

Richiedi flowSDK